
Как и у многих, в нашей компании возник вопрос импортозамещения. В целом вопрос понятный, много раз обсужденный со всех точек зрения. И вот настал счастливый момент, когда слова трансформировались в конкретные задачи с конкретными сроками. И одна из них была о замене СУБД для 1С.
Ну и конечно же, первым делом был поднят вопрос о кластеризации этой истории. Никто подвоха особого не ожидал, ибо у нас есть уже зарекомендовавшее себя решение в виде связки pg_auto_failover версии 1.6 от Citus (далее PGAF для краткости) и keepalived. Это решение нас целиком и полностью устраивает, поэтому выбор наш был очевиден.
Но когда мы начали настраивать выяснился очень неприятный момент - обычная сборка PGAF просто не работает с версией СУБД от PostgresPro - все ломается из-за жестко прописанных зависимостей. Тут то и началось "веселье".
Был вариант игнорировать зависимости, но в таком случае мы получаем проблемы при обновлении. В итоге нашли альтернативу - собрать из исходников самим, настраивая пути и зависимости самостоятельно, о чем и расскажу. В моем повествовании нет какой-то особой магии, но пару дней сберечь точно поможет.
Кластер предполагается разворачивать используя продукты PostgresPro 14 в редакции 1C (14.2.1) и PGAF (1.6.4)
В соответствии с архитектурой системы разворачиваем минимально необходимые 3 ноды (схема из гита проекта):

У нас это будет 3 чистых (с набором минимально необходимого для комфортной работы) сервера Ubuntu Server 20.04 LTS (Focal Fossa) и называем их, например так:
serv-pg-mon.local - сервер, который будет выполнять роль монитор-сервера. Для его полноценной работы будет достаточно выделить всего 1 ядро и 1 ГБ оперативной памяти, 20 Гб медленного дискового пространства.
serv-pg-data1.local - первый дата-сервер, который будет хранить базы и выполнять роль primary или secondary в процессе репликации - конфигурацию железа следует подбирать исходя из ваших потребностей.
serv-pg-data2.local - второй дата-сервер, который будет хранить базы и выполнять роль primary или secondary в процессе репликации - конфигурацию железа следует подбирать исходя из ваших потребностей - она должна совпадать с конфигурацией первого дата-сервера.
Настройку имён хостов, сети, фаервола и доступа к хостам по ssh оставим за рамками этой статьи - будем считать, что это всё уже сделано. Важно, чтобы серверы могли обращаться друг к другу по указанным именам - всё взаимодействие в конфигах ниже построено на их DNS-именах, а не их IP-адресах.
Пункт 1: Первоначальная настройка хостов и установка софта из репозиториев
На всех нодах настраиваем русскую локаль (пригодится для корректной работы 1С).
Для начала смотрим текущую локаль:
locale
Если в выводе не ru_RU.UTF-8, то делаем следующее:
sudo locale-gen ru_RU.UTF-8
sudo localectl set-locale LANG=ru_RU.UTF-8 LC_TIME=ru_RU.UTF-8 LC_COLLATE=ru_RU.UTF-8
Если всё прошло без ошибок, то для всех новых сессий кодировка будет ru_RU.UTF-8 - проверяем, перелогинившись.
На всех нодах подключаем репозиторий PostgresPRO:
bash -c 'curl https://repo.postgrespro.ru/pg1c-14/keys/pgpro-repo-add.sh | sudo bash'
На всех нодах устанавливаем пакеты:
sudo apt install postgrespro-1c-14 postgrespro-1c-14-dev
Пакет postgrespro-1c-14-dev нам пригодится из-за входящей в его состав утилиты pg_config, которая требуется для работы PGAF
PostgresPRO по дефолту установится по пути /opt/pgpro/1c-14/ - в дальнейшем это будет важно.
На всех нодах создаём папки, в которых у нас будут размещаться файл PostgreSQL, например, папку "/data/postgres/". В нашем случае это просто отдельный диск, но и там где это не требуется, все равно делаем такую папку - это наши договоренности внутри команды. Если же необходимости в этом нет, можно использовать стандартный путь /var/lib/postgresql/ - в таком случае этот шаг можно пропустить и перейти сразу к подключению репозитория.
sudo mkdir -p /data/postgres/
sudo chown -R postgres. /data
Обратите внимание, что права юзеру postgres нужно выдавать не на папку /data/postgres/, а на папку выше (/data) - в ней PGAF должен иметь возможность при необходимости создавать и удалять папку backup и совершать операции с самой папкой /data/postgres.
На всех нодах задаём переменные окружения (чтобы облегчить себе жизнь - потом меньше параметров передавать PGAF вручную) создаём файл /etc/profile.d/02-pgenv.sh со следующим содержимым (указываем, где у нас будут файлы PostgreSQL и где его бинарники):
export PGDATA="/data/postgres"
export PATH="$PATH:/opt/pgpro/1c-14/bin"
Если же мы не создавали отдельную папку, то содержимое файла будет:
export PGDATA="/var/lib/postgresql/"
export PATH="$PATH:/opt/pgpro/1c-14/bin"
Пункт 2: Сборка PGAF из исходников
Ставим необходимые для сборки пакеты:
sudo apt install libssl-dev libkrb5-dev libncurses6 libselinux1-dev liblz4-dev libxslt1-dev libpam0g-dev libreadline-dev git
Скачиваем из гита проекта, собираем и устанавливаем PGAF (ставим наиболее свежую на момент написания статьи версию 1.6.4 - тестировалось всё на ней и всё работает, если хотите, можете попробовать на других):
cd /tmp
git clone https://github.com/citusdata/pg_auto_failover -b v1.6.4
cd pg_auto_failover/
make
sudo make install
Создаём отдельную службу в systemd, которая будет производить управление Postgres-ом через pgaf - создаём файл /etc/systemd/system/pgautofailover.service с правами root:root 0644 и со следующим содержимым:
[Unit]
Description = pg_auto_failover
[Service]
WorkingDirectory = /var/lib/postgresql
Environment = 'PGDATA=/data/postgres/'
User = postgres
ExecStart = /opt/pgpro/1c-14/bin/pg_autoctl run
Restart = always
StartLimitBurst = 0
[Install]
WantedBy = multi-user.target
Обратите внимание, что в поле Environment нужно вписать своё значение. Без корректного значения служба нормально стартовать не будет.
Применяем изменения, включаем службу PGAF и отключаем запуск стандартной службы postgrespro-1c-14.service:
sudo systemctl daemon-reload
sudo systemctl disable --now postgrespro-1c-14.service
sudo systemctl enable pgautofailover.service
Повторяем на остальных серверах.
Пункт 3: Настройка монитор-ноды
Внимание! Команды настройки кластера PGAF выполняются от имени локального пользователя postgres - чтобы начать в консоли сеанс этого пользователя выполните:
sudo su - postgres
Создаём конфигурацию монитор-ноды, выполнив в консоли:
pg_autoctl create monitor --auth md5 --ssl-mode require --ssl-self-signed --pgport 5432 --hostname serv-pg-mon.local
Объясняю, что мы только что сделали - мы создали монитор-ноду:
используем метода аутентификации md5
шифруем траффик между нодами (с использованием самоподписанных сертификатов)
используем FQDN хоста в качестве имени ноды
В данном примере мы используем конкретные настройки, которые подойдут большинству. Для ознакомления с альтернативными настройками обратитесь к документации - https://pg-auto-failover.readthedocs.io/en/v1.6.3/
Результатом выполнения команды станет инициализация БД в каталоге, который мы указали в PGDATA и сообщения такого вида

Если всё так и прошло, можно двигаться дальше. В случае возникновения ошибок, обратитесь к документации.
Теперь запускаем службу PGAF (обратите внимание, что команды выполняются с sudo, а пользователя postgres обычно таких прав не имеет - переключитесь на нужного пользователя):
sudo systemctl enable --now pgautofailover.service
sudo systemctl status pgautofailover.service
На выходе должны получить картину типа такой:

Теперь нужно задать пароль служебного пользователя autoctl_node (см. документацию). Пусть у нас это будет пароль, например, "BeLL1ss1m0PaSSw0rd1ss1m0".
(Этот пароль будет использоваться для аутентификации между нодами и монитором. Позднее нужно будет также задать ещё один пароль для авторизации дата-нод между собой - пароль репликации. Удобно использовать одинаковый пароль для обеих операций - в этой инструкции мы рассматриваем именно такую схему. Но вы при желании можете задать отдельные пароли.)
Для этого выполняем от имени локального пользователя postgres:
psql -c "alter user autoctl_node password 'BeLL1ss1m0PaSSw0rd1ss1m0'"
Переопределяем стандартные настройки частоты проверок хостов на более приближённые к реальным требованиям - добавляем в конфиг postgresql.conf следующие строки:
#время задается в миллисекундах
pgautofailover.health_check_period = 500
pgautofailover.health_check_retry_delay = 300
pgautofailover.health_check_timeout = 500
pgautofailover.node_considered_unhealthy_timeout = 3000
pgautofailover.primary_demote_timeout = 6000
Значения выведены эмпирически и могут быть изменены в зависимости от ваших потребностей или предпочтений - значения указываются в мс (см. документацию по PGAF).
Перезапускаем службу для применения всех изменений:
sudo systemctl restart pgautofailover.service
Теперь можно приступать к настройке дата-нод.
Пункт 4: Настройка первой дата-ноды
# очищаем дефолтные настройки
rm -f /etc/default/postgrespro-1c-14
# инициализируем PostgreSQL, создавая дефолтную пустые базы из шаблонов - указание ru-локали обязательно для дальнейшей работы 1С
sudo /opt/pgpro/1c-14/bin/pg-setup initdb -D /data/postgres/ --locale=ru_RU.UTF-8
# создаём служебную папку backup, которая будет использоваться PGAF для хранения временных файлов при репликации и восстановлении БД в случае сбоя
sudo mkdir /data/backup
# выдаём на неё нужные права
sudo chown -R postgres. /data/backup
# переключаемся на пользователя postgres
sudo su - postgres
# регистрируем дата-ноду на монитор-ноде
pg_autoctl create postgres --hostname serv-pg-data1.local --name serv-pg-data1 --auth md5 --ssl-self-signed --monitor 'postgres://autoctl_node:BeLL1ss1m0PSSw0rd1ss1m0@serv-pg-mon.local:5432/pg_auto_failover?sslmode=require' --pgport 5432 --pgctl /opt/pgpro/1c-14/bin/pg_ctl
После выполнения последней команды должны получить примерно такую картину:

Произошла регистрация и последующая остановку сервиса. При этом на монитор-ноде выполнение команды:
sudo su - postgres -c "pg_autoctl show state"
должно показать примерно такую картину:
Name | Node | Host:Port | TLI: LSN | Connection | Reported State | Assigned State
--------------+-------+------------------------------+----------------+--------------+---------------------+--------------------
serv-pg-data1 | 1 | serv-pg-data1.local:5432 | 1: 0/1755380 | read-write ! | single | single
Теперь задаём основные параметры репликации (всё на той же первой дата-ноде):
# переключаемся на пользователя postgres
sudo su - postgres
# указываем пароль репликации - он запишется в конфиг pgaf и будет использован в случае, если эта нода станет standby
pg_autoctl config set replication.password 'BeLL1ss1m0PSSw0rd1ss1m0'
# задаём тот же пароль для пользователя БД
psql -c "alter user pgautofailover_replicator password 'BeLL1ss1m0PaSSw0rd1ss1m0';"
# выставляем значения таймаутов - опять же выведены эмпирически и могут быть изменены в зависимости от ваших потребностей или предпочтений - значения указываются в секундах (см. документацию по PGAF)
pg_autoctl config set timeout.network_partition_timeout 3
pg_autoctl config set timeout.prepare_promotion_catchup 6
pg_autoctl config set timeout.prepare_promotion_walreceiver 3
pg_autoctl config set timeout.postgresql_restart_failure_timeout 6
pg_autoctl config set timeout.postgresql_restart_failure_max_retries 2
# ограничение на максимальную скорость репликации - значение 1024M это 1 Гигабит/c - может быть полезно, если Вы реплицируете большую базу и ограничены в пропускной скорости сети. В более ранних версиях PGAF (например, версии 1.4) скорость 1024М была максимально возможной, которую можно быть установить - значение по умолчанию - 100M (то есть 100 Мбит/c)
pg_autoctl config set replication.maximum_backup_rate 1024M
Теперь можно запускать службу PGAF:
sudo systemctl enable --now pgautofailover.service
sudo systemctl status pgautofailover.service
Получаем примерно такую картину:

Теперь можно приступать к настройке второй дата-ноды.
Пункт 5: Настройка второй дата-ноды
После выполнения последней команды должно начаться копирование БД с первой ноды - в логе мы должны получить примерно такую картину:

Если всё пройдёт успешно, далее увидим примерно вот такое:

При этом на монитор-ноде выполнение команды
sudo su - postgres -c "pg_autoctl show state"
должно показать примерно такую картину:
Name | Node | Host:Port | TLI: LSN | Connection | Reported State | Assigned State
--------------+-------+------------------------------+----------------+--------------+---------------------+--------------------
serv-pg-data1 | 1 | serv-pg-data1.local:5432 | 1: 0/8000060 | read-write | wait_primary | wait_primary
serv-pg-data2 | 2 | serv-pg-data2.local:5432 | 1: 0/8000000 | read-only ! | catchingup | catchingup
Это означает, что обе ноды успешно зарегистрированы, вторая нода находится в состоянии catchingup и пока что недоступна (службу pgautofailover мы ещё не запустили).
Теперь задаём параметры репликации - в этот раз уже на второй ноде (да, на каждой ноде эти параметры задаются в конфиге отдельно)
sudo su - postgres
pg_autoctl config set replication.password 'BeLL1ss1m0PSSw0rd1ss1m0'
pg_autoctl config set timeout.network_partition_timeout 3
pg_autoctl config set timeout.prepare_promotion_catchup 6
pg_autoctl config set timeout.prepare_promotion_walreceiver 3
pg_autoctl config set timeout.postgresql_restart_failure_timeout 6
pg_autoctl config set timeout.postgresql_restart_failure_max_retries 2
pg_autoctl config set replication.maximum_backup_rate 1024M
Теперь можно запускать службу PGAF и на ней:
sudo systemctl enable --now pgautofailover.service
sudo systemctl status pgautofailover.service
При этом на монитор-ноде выполнение команды
sudo su - postgres -c "pg_autoctl show state"
должно показать примерно такую картину:
Name | Node | Host:Port | TLI: LSN | Connection | Reported State | Assigned State
--------------+-------+------------------------------+----------------+--------------+---------------------+--------------------
serv-pg-data1 | 1 | serv-pg-data1.local:5432 | 1: 0/8000230 | read-write | primary | primary
serv-pg-data2 | 2 | serv-pg-data2.local:5432 | 1: 0/8000230 | read-only | secondary | secondary
Это означает, что всё прошло успешно, ноды синхронизированы и всё работает, как часы.
Пункт 6: Кластерный IP-адрес
Теперь, когда всё настроено и работает, можно приступить к настройке коннектов к серверу БД - удобнее, когда не приходится перенастраивать свои коннекты в случае смены мастер-ноды в кластере.
Можно воспользоваться балансировщиком нагрузки типа HAProxy (или прочими), а можно выделить кластеру IP-адрес, который будет всегда "привязан" к primary-ноде.
Для этого воспользуемся утилитой keepalive.
На обеих дата-нодах устанавливаем нужный пакет:
sudo apt install keepalived
Создаём скрипт /etc/keepalived/pg_auto_check.sh который проверяет, запущен ли на хосте ноде и какую роль она выполняет в кластере.
#!/bin/bash
if [ "`pidof pg_autoctl`" ]; then
if [ "`su - postgres -c 'pg_autoctl show state --pgdata /data/postgres | grep $HOSTNAME | grep -e primary -e single -e stop_replication'`" ]; then
exit 0
else
exit 1
fi
else
exit 1
fi
Даем разрешение на запуск этого скрипта:
sudo chmod +x /etc/keepalived/pg_auto_check.sh
Приводим настройки /etc/keepalived.conf к следующему виду (предполагается, что у нас на серверах основной сетевой интерфейс eth0, мы присвоили этому кластеру virtual_router_id 66, мы выделили кластеру IP-адрес 172.16.6.66 и задали пароль '123qwe-1A'):
! Configuration File for keepalived
global_defs {
# notification_email {
# admin@example.com
# }
# notification_email_from admin@example.com
# smtp_server mail.example.com
# smtp_connect_timeout 10
#
}
vrrp_script chk_pg_auto {
script "/etc/keepalived/pg_auto_check.sh"
interval 3
timeout 2
weight 100
rise 3
fall 3
user root
}
vrrp_instance <имя_вашего_сервера> {
state BACKUP
interface eth0
dont_track_primary
track_script {
chk_pg_auto
}
virtual_router_id 66
priority 50
advert_int 1
authentication {
auth_type PASS
auth_pass 123qwe-1A
}
virtual_ipaddress {
172.16.6.66 dev eth0 label eth0:1
}
# smtp_alert
}
Не забудьте вписать имя своего сервера в конфиг там где "vrrp_instance".
Если хотите получать почтовые оповещения о переходе IP-адреса с одной ноды на другую, раскомментируйте закомментированные строки и укажите свои актуальные данные.
Теперь стартуем сервис keepalived:
sudo systemctl enable --now keepalived.service
Проверяем, что на primary-ноде появился наш кластерный IP, если так - мы великолепны, если нет - курим мануалы.
Пункт 7: Создание базы 1С
Здесь сложностей возникнуть не должно - мануалов, как создать базу 1С на сервере PostgreSQL средствами самой 1С, более чем достаточно в интернете.
Из тонкостей:
- рекомендуется создать отдельного пользователя для баз 1С
- в параметрах подключения в качестве адреса сервере БД нужно указывать IP-адрес нашего кластера Keepalived
Пункт 8: Тюнинг настроек Postgres
Рекомендуем ознакомиться со статьёй https://postgrespro.ru/docs/postgrespro/14/config-one-c и выставить параметры в соответствии с указанными там.
Что же касается настроек типа shared_buffers, work_mem,effective_cache_size и параметров autovacuum - то можно немного расслабиться - эти параметры pgaf задаёт сам, рассчитывая их по формуле исходя из текущей конфигурации железа сервера. То есть, стоит вам накинуть памяти вашей виртуалке - и при первом же старте значения будут пересчитаны. Текущие параметры можно посмотреть в файле конфига postgresql-auto-failover.conf в корне вашей Postgres.
Пункт 9: Траблшутинг
Логи работы службы pgautofailover можно просмотреть через:
sudo journalctl -u pgautofailover.service
Логи событий в кластере:
su - postgres -c "pg_autoctl show events"
Логи самого PostgreSQL находятся в папке $PGDATA/log/
Послесловие:
не забывайте везде в конфигах и командах изменить значения из примера на свои актуальные