В данном разделе будут рассмотрены варианты реализации отказоустойчивого кластера на PostgreSQL на примере использования таких решений как Patroni и Postgres Pro BiHA, а также HAProxy для создание единой точки входа со стороны «1С: Предприятие».
Для создания единой точки входа также можно использовать расширенную строку подключения с указанием нескольких хостов. Эта возможность будет подробно рассмотрена в статье.
Patroni является внешним сервисом по отношению к Postgres и выполняет роль менеджера кластера. Основная задача Patroni - обеспечение надежного переключения роли ведущего узла на резервный узел.
Для максимальной доступности СУБД в кластере Patroni необходимо хранить и, при необходимости, изменять информацию о роли узлов. Для этого используется DCS (Distributed Configuration System), которое реализуется с помощью etcd или consul и представляет собой распределенное хранилище конфигурации ключ/значение.
На данной схеме (рис. 1) рассмотрен вариант, состоящий из одного мастера и реплики, и который позволяет пережить недоступность одного из узлов. Достоинством такого варианта является его экономичность.
Узлы распределенного хранилище etcd в этом случае расположены на серверах СУБД и сервере 1С. При значительной нагрузке на серверах могут быть замедлены операции с etcd, например, из-за конкуренции записи на диск. При недоступности одного из узлов etcd текущий мастер может сложить с себя полномочия и передать их другому узлу кластера, что может привести к ложным срабатываниям при повышенной загрузке серверов, где расположены узлы etcd.
В этом случае стоит вынести узлы etcd на другие менее загруженные сервера. Кроме того, можно попробовать скорректировать параметры Patroni ttl, loop_wait, retry_timeout, влияющие на периодичность проверки состояния кластера, чтобы за счет их увеличения пережить кратковременные пики нагрузки.
Ссылка на документацию:
https://patroni.readthedocs.io/en/latest/dynamic_configuration.html
Рис. 1 Отказоустойчивый кластер из двух узлов (использование Patroni + Etcd + HAProxy)
На схеме (рис. 2), приведенной ниже, рассмотрен более сложный вариант, состоящий из трех узлов, один из которых синхронный. В отличие от варианта, состоящего из двух узлов (рис. 1), такой кластер может пережить недоступность двух узлов или повторного сбоя в кластере.
Кроме того, за счет синхронной репликации исключаются потери данных, т.к. каждая транзакция фиксируется одновременно на мастере и на реплике. Обратной стороной медали здесь является потеря в производительности операций, т.к. приходится ожидать фиксации транзакции не только на основном, но и на резервном сервере. В случае, если синхронная реплика станет не доступной, Patroni переведет кластер в асинхронный режим для того, чтобы текущий мастер смог принимать клиентские подключения на запись.
Включить синхронную репликацию можно, с помощью параметров Patroni:
Ссылка на документацию:
https://patroni.readthedocs.io/en/latest/replication_modes.html
Для администрирования отказоустойчивого кластера используется утилита patronictl (https://patroni.readthedocs.io/en/latest/patronictl.html). С ее помощью можно просматривать информацию о кластере и состоянии его узлов, изменять параметры кластера, выполнять ручное переключение на другой узел, восстанавливать резервные узлы, выполнять перезапуск узлов кластера и т.д.
Для создания единой точки входа со стороны 1С, в связке с Patroni используется HAProxy.
В качестве точки подключения в 1С нужно указывать сервер, где установлен HAProxy. Порт, по умолчанию, используется стандартный для Postgres 5432.
HAProxy от Patroni по REST API может получить информацию по тому, какой сервер является мастером, а какой репликой, и выполнить подключение только к доступному для записи серверу (запрос к REST API Patroni вернет код 200, если узел работает как мастер).
Настройка осуществляется через конфигурационный файл haproxy.cfg
Рис. 2 Отказоустойчивый кластер из трех узлов (использование Patroni + Etcd + HAProxy)
Пошаговую настройку Patroni + Etcd + HAProxy можно выполнить по инструкции с сайта 1С ИТС: https://its.1c.ru/db/metod8dev/content/5971/hdoc (с учетом информации из текущего раздела).
BiHA - это расширение Postgres Pro, которое управляется утилитой bihactl и функциями из состава расширения. В сочетании с доработками ядра, SQL интерфейсом и служебным процессом biha-worker, координирующим узлы кластера, BiHA превращает несколько узлов в кластер с физической репликацией Postgres и встроенным аварийным переключением узлов, отказоустойчивостью и автоматическим восстановлением после отказа.
Ключевые особенности:
Существует несколько вариантов конфигурации узлов: три и более узлов (рис. 3), два узла (мастер и реплика) и вариант, когда к двум узлам (мастер и реплика) добавляется еще узел рефери.
Вариант, состоящий из двух серверов, в данной статье не рассматривается, т.к. при отказе основного сервера в такой конфигурации, для предотвращения Split Brain (разделение кластера, когда клиентские соединения могут записывать данные на оба сервера) кластер будет доступен только для чтения, т.е. станет фактически недоступным из 1С.
Для решения этой проблемы используется узел-рефери, участвующий в голосовании во время выборов нового мастера при отказе основного сервера, что позволяет избежать потенциального разделения кластера. В зависимости от режима referee или referee_with_wal узел-рефери может принимать только участие в выборе нового мастера, либо также принимать участие в выборах и дополнительно реплицировать данные журнала WAL, чтобы новый мастер получил все файлы WAL с узла рефери (если там оказались самые последние журнальные записи перед сбоем мастера).
Рис. 3: Отказоустойчивый кластер из трех узлов (расширенная строка подключения libpq)
На приведенной схеме (рис. 3) узел-рефери может обладать минимальными аппаратными ресурсами, поскольку он не содержит данных 1С и будет являться только наблюдателем за другими узлами кластера.
Ссылки на документацию:
https://postgrespro.ru/docs/enterprise/16/biha
https://postgrespro.ru/clusters/biha
При инициализации кластера можно создать узел мастера и узлы реплики с нуля, либо преобразовать уже существующие узлы и включить их в отказоустойчивый кластер. Для инициализации кластера и добавления в него узлов используется утилита bihactl, также с ее помощью можно посмотреть статус каждого узла (https://postgrespro.ru/docs/enterprise/16/bihactl).
Пример команды для инициализации существующего кластера Postgres (опция --convert):
sudo -u postgres /opt/pgpro/ent-16/bin/bihactl init --convert --biha-node-id=1 --host=192.168.50.01 --port=5432 --biha-port=5433 --nquorum=2 --pgdata=/var/lib/pgpro/ent-16/data/
Описание параметров:
При инициализации кластера BiHA создается файл pg_hba.biha.conf (включается в основной файл pg_hba.conf с помощью команды include pg_hba.biha.conf) и файл postgresql.biha.conf который включен в postgresql.conf. В файле pg_hba.biha.conf для узла может быть указан метод аутентификации scram-sha-256 (в зависимости от версии BiHA), что может быть несовместимо с существующими настройками хранения паролей, например если параметр конфигурации password_encryption установлен в md5. В этом случае метод аутентификации в файле pg_hba.biha.conf нужно также выставить в md5.
Кроме того, чтобы узлы кластера BiHA могли подключаться друг к другу, необходимо для каждого узла в домашней директории пользователя Postgres (/var/lib/postgresql/) заполнить файл паролей .pgpass и разрешить доступ к нему только для владельца:
sudo -u postgres mcedit /var/lib/postgresql/.pgpass
*:5432:*:biha_replication_user:<password> - шаблон для заполнения
sudo chmod 600 /var/lib/postgresql/.pgpass
Перед добавлением узла желательно проверить доступность узла мастера с ведомого узла, например:
sudo -u postgres psql -h <IPServer> -p 5432 -U biha_replication_user -d postgres
Если подключение проходит успешно, то можно добавить узел в кластер BiHA с помощью команды bihactl add на ведомом узле:
sudo -u postgres /opt/pgpro/ent-16/bin/bihactl add --biha-node-id=2 --host=192.168.50.02 --port=5432 --biha-port=5433 --use-leader "host=192.168.50.01 port=5432 biha-port=5433" --pgdata=/var/lib/pgpro/ent-16/data/
Для добавления существующего кластера можно использовать опцию --convert-standby, если вы уже настраивали репликацию с основного узла.
Таким же образом можно добавить несколько дополнительных ведомых узлов.
Для добавления узла-рефери, который не может стать лидером, но может участвовать в голосовании и таким образом предотвратить потенциальное разделение кластера, необходимо указать параметр –mode
sudo -u postgres /opt/pgpro/ent-16/bin/bihactl add --biha-node-id=3 --host=192.168.50.03 --port=5432 --biha-port=5433 --use-leader "host=192.168.50.01 port=5432 biha-port=5433" --pgdata=/var/lib/pgpro/ent-16/data/ --mode="referee_with_wal"
Параметр --mode может быть равен:
Администрирование и просмотр состояния кластера осуществляется с помощью SQL-функций и представлений расширения, которые следует вызывать из базы данных biha_db:
Кроме того, расширение BiHA поддерживает дополнительные конфигурационные параметры для управления отказоустойчивым кластером, например:
Полное описание параметров в документации по ссылкам ниже:
https://postgrespro.ru/docs/enterprise/16/biha#BIHA-CLUSTER-CONFIGURATION-FUNCTIONS
https://postgrespro.ru/docs/enterprise/16/biha#BIHA-VIEWS
https://postgrespro.ru/docs/enterprise/16/biha#BIHA-CONFIGURATION-PARAMETERS
BiHA также поддерживает синхронный режим репликации. Для этого нужно при инициализации кластера добавить параметр --sync-standbys=<число синхронных реплик> к команде bihactl init.
При инициализации автоматически будут изменены параметры Postgres synchronous_commit и synchronous_standby_names. Репликация в BiHA кворумная, все ведомые узлы перечислены в synchronous_standby_names, при этом мастер ждет ответа, только от количества, указанного в параметре --sync-standbys.
Узел-рефери в режиме referee_with_wal тоже может участвовать в синхронной репликации, при этом, обратите внимание, если для параметра synchronous_commit установлено значение remote_apply, то рефери не сможет подтверждать транзакции.В качестве единой точки входа можно использовать:
Рассмотрим более подробно каждый из способов подключения.
В Postgres есть возможность использовать расширенную строку подключения с помощью функции управления подключением к базе данных библиотеки libpq. Пример схемы такого подключения приведен на рис.3
Ссылка на описание в документации:
https://postgrespro.ru/docs/postgresql/16/libpq-connect#LIBPQ-CONNSTRING
1С поддерживает такую возможность в формате ключ/значение, но с некоторыми нюансами, о которых будет сказано ниже.
Пример из документации по libpq:
host=localhost port=5432 dbname=mydb connect_timeout=10
В строке подключения можно задать несколько узлов (host), к которым клиент будет пытаться подключиться в заданном порядке. Первый доступный узел становится выбранным для подключения, остальные узлы уже не рассматриваются. Параметры host и port в формате ключ/значение принимают списки значений, разделённых запятыми. В каждом определяемом параметре должно содержаться одинаковое число элементов, чтобы, например, первый элемент host соответствовал первому узлу, второй - второму узлу и так далее. Исключение составляет port - если этот параметр содержит только один элемент, то он применяется ко всем узлам.
Если такой формат применить для строки подключения к узлам pgnode-01, pgnode-02, pgnode-03 со стандартным портом 5432 с необходимостью не подключаться к репликам, то строка, которая указывается в поле «Сервер баз данных», должна выглядеть так:
host=pgnode-01, pgnode-02, pgnode-03 port=5432 dbname=db1c target_session_attrs=read-write
https://postgrespro.ru/docs/postgresql/16/libpq-connect#LIBPQ-CONNECT-TARGET-SESSION-ATTRS
pgnode-01, pgnode-02, pgnode-03 port=5432 target_session_attrs=read-write
То есть из начала строки убран host= и параметр указания базы данных dbname=db1c, т.к. имя базы данных, как и при стандартном подключении, указывается в поле «База данных» (рис.4).
Рис.4 Расширенная строка подключения при создании базы в 1С
По аналогии с Patroni в качестве единой точки входа можно использовать HAProxy (рис. 5), но т.к. у BiHA на текущий момент нет REST API, который бы мог возвращать текущее состояние узлов кластера, то используется возможность HAProxy выполнять в качестве проверки внешний скрипт. Пример настройки в haproxy.cfg:
Скрипт pgcheck.sh должен возвращать «0» для разрешенных подключений и «1» для реплик, а также недоступных серверов.
При проверке узлов HAProxy опрашивает их состояния с периодичностью, заданной параметром timeout check и, если скрипт возвращает «0», то сервер считается доступным для подключения. Для реализации проверки можно использовать функцию pg_is_in_recovery(), которая возвращает «f», если кластер не является репликой.
В качестве краткого вывода можно сказать, что с точки зрения функциональных возможностей Patroni и Postgres Pro BiHA похожи и успешно справляются со своими задачами. Главным отличием здесь является то, что Patroni является внешним сервисом по отношению к Postgres и поэтому может не всегда правильно реагировать на временную недоступность сервера для клиентских запросов, например, когда сервер перегружен, т. е. возможны ложные срабатывания.
BiHA является частью СУБД PostgresPro Enterprise, что отличает ее от сторонних решений, поэтому вероятность ложных срабатываний ниже, т. к. она может точнее отслеживать текущее состояние каждого узла кластера. Кроме того, наличие встроенной интеграции с PostgresPro Enterprise упрощает настройку и управление отказоустойчивым кластером.