Построение отказоустойчивого кластера PostgreSQL. Настройка внешней синхронизации на PostgreSQL для механизма копий баз данных.

Материал из support.qbpro.ru

Введение

Целью данной статьи является описание способов построения отказоустойчивого кластера PostgreSQL, для систем на базе 1С:Предприятие, подобного решению "MS SQL Server Always On Availability Groups" от Microsoft. Построение такого отказоустойчивого кластера затруднено тем, что в PostgreSQL не предоставляет механизмов автоматического переключения на резервный сервер. Ниже будет описано, каким образом можно реализовать автоматическое переключение на резервный сервер в случае сбоя ведущего сервера PostgreSQL.

Используемые программные продукты

Patroni - шаблон для построения отказоустойчивых кластеров на базе PostgreSQL. Обеспечивает автоматическое переключение на резервный сервер при сбое ведущего сервера. Patroni управляет экземпляром базы данных PostgreSQL и использует внешние системы (ZooKeeper, etcd, Consul или Kubernetes) для хранения общей для всех узлов кластера информации, такой как список узлов кластера и положение ведущего сервера.

  • Напомню, что такое Patroni, для чего предназначен и что умеет.

Patroni — это шаблон для построения HA из коробки. Так написано в документации и с моей точки зрения — это очень правильное уточнение. То есть Patroni — это не серебряная пуля, которую поставил и она решит все проблемы. Нужно приложить усилия, чтобы он начал работать и приносить пользу.

Patroni — агентская служба. Устанавливается на каждом сервере с базой данных и является своего рода init-системой для PostgreSQL: запускает, останавливает, перезапускает, меняет конфигурацию и топологию кластера.

Patroni хранит «состояние кластера» в DCS. Чтобы хранить состояние кластера, его текущее представление, нужно хранилище. Patroni хранит состояние во внешней системе — распределенном хранилище конфигураций. Это может быть один из вариантов: Etcd, Consul, ZooKeeper либо Etcd Kubernetes.

Автофейловер в Patroni включен по умолчанию. Вы получаете автофейловер из коробки, сразу же после установки Patroni.

etcd - согласованное распределенное хранилище пар ключ-значение, обеспечивающее надежный способ хранения данных, к которым могут обращаться распределенная система или кластер машин.

HAProxy - прокси сервер TCP и HTTP трафика. Позволяет создать единую точку подключения к PostgreSQL, не зависящую от распределения ролей в кластерах PostgreSQL.

Развёртывание стенда

Описание стенда Для демонстрации работы кластера будет использоваться четыре машины под управлением CentOS 7:

Имя	IP-адрес	Описание
Core.example	10.0.0.11	симуляция инфраструктуры
DBOne.example	10.0.0.21	первый сервер PostgreSQL 
DBTwo.example	10.0.0.22	второй сервер PostgreSQL
DBThree.example	10.0.0.23	третий сервер PostgreSQL

Предполагается, что:

Машины уже созданы и на них установлена операционная система Операционная система обновлена Машины находятся в одной сети Машины могут обнаруживать друг друга по DNS именам (core.example, dbone.example, dbtwo.example, dbthree.example). Дополнительные настройки Приведём имя машины к её имени в DNS и проверим результат. Для этого выполняем следующие команды:

на core.example

Копировать в буфер обмена

> sudo hostnamectl set-hostname core.example --static > hostname core.example
на dbone.example

Копировать в буфер обмена

> sudo hostnamectl set-hostname dbone.example --static > hostname dbone.example
на dbtwo.example

Копировать в буфер обмена

> sudo hostnamectl set-hostname dbtwo.example --static > hostname dbtwo.example
на dbthree.example

Копировать в буфер обмена

> sudo hostnamectl set-hostname dbthree.example --static > hostname dbthree.example

Подготовка дистрибутивов Следует скачать следующие дистрибутивы:

Сервер 1С:Предприятия для RPM-based Linux-систем (https://releases.1c.ru/project/Platform83), использовалась версия 8.3.16.1224

СУБД PostgreSQL для Linux x86 (64-bit) одним архивом (RPM) (https://releases.1c.ru/project/AddCompPostgre), использовалась версия 11.5_12.1C

Установка дистрибутивов

Core.example

На этот сервер следует установить и запустить сервер 1С:Предприятие.
Подробную инструкцию по установке можно найти тут

DBOne.example, DBTwo.example, DBThree.example

На этом этапе настройки этих машин не отличаются. На обоих необходимо установить PostgreSQL с модификациями фирмы 1С.

Установим PostgreSQL версии 11.5_12.1С. Для этого в каталоге с распакованным дистрибутивом PostgreSQL 11.5_12.1C выполняем:

Копировать в буфер обмена

> sudo yum install -y postgresql11-1c-*.rpm

Кластер инициализировать не нужно. Это будет выполнено при установке Patroni.

Запрещаем автоматический запуск PostgreSQL при старте операционной системы.

Копировать в буфер обмена

> sudo systemctl disable postgresql-11

Removed symlink /etc/systemd/system/multi-user.target.wants/postgresql-11.service. Остановкой и запуском PostgreSQL будет управлять Patroni и автоматический запуск будет только мешать Patroni.

Развёртывание отказоустойчивого кластера PostgreSQL + Patroni + etcd + HAProxy Ниже описано создание кластера etcd и добавление нового узла в кластер. Затем будет построен кластер Patroni, использующий etcd для хранения своей конфигурации. После будет настроен HAProxy, который будет отслеживать смену ведущего сервера и скрывать это событие от 1С:Предприятие.

Установка Etcd

Кластер etcd будет развёрнут на всех машинах стенда.

Создание кластера etcd Создадим первый узел кластера. На core.example выполняем следующие действия:

Устанавливаем etcd из пакета

Копировать в буфер обмена

> sudo yum install -y etcd

Откладываем в сторону настройки по умолчанию

Копировать в буфер обмена

> sudo mv /etc/etcd/etcd.conf /etc/etcd/etcd.conf.def

Используя текстовый редактор создаём новый файл настроек /etc/etcd/etcd.conf и помещаем в него следующие настройки:

Копировать в буфер обмена

#[Member]
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_NAME="core"
ETCD_HEARTBEAT_INTERVAL="1000"
ETCD_ELECTION_TIMEOUT="5000"
ETCDCTL_API=2
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://core.example:2380" 
ETCD_ADVERTISE_CLIENT_URLS="http://core.example:2379"
ETCD_INITIAL_CLUSTER="core=http://core.example:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-example"
ETCD_INITIAL_CLUSTER_STATE="new"

Рассмотрим введённые параметры:

ETCD_DATA_DIR - указывает расположение каталога данных кластера
ETCD_LISTEN_PEER_URLS - задаёт схему и точку подключения для остальных узлов кластера, по шаблону scheme://IP:port. Схема может быть http, https. Альтернатива, unix:// или unixs:// для юникс сокетов. Если в качестве IP адреса указано 0.0.0.0, 
то указанный порт будет прослушиваться на всех интерфейсах.
ETCD_LISTEN_CLIENT_URLS - задаёт схему и точку подключения для клиентов кластера. В остальном совпадает с ETCD_LISTEN_PEER_URLS.
ETCD_NAME - человекочитаемое имя этого узла кластера. Должно быть уникально в кластере. Для первого узла может быть любым. Для последующих должно совпадать с именем, указанным при добавлении узла.
ETCD_HEARTBEAT_INTERVAL - время в миллисекудах, между рассылками лидером оповещений о том, что он всё ещё лидер. Рекомендуется задавать с учётом сетевой задержки между узлами кластера.
ETCD_ELECTION_TIMEOUT - время в миллисекундах, которое проходит между последним принятым оповещением от лидера кластера, до попытки захватить роль лидера на ведомом узле. Рекомендуется задавать его в несколько раз большим, чем 
ETCD_HEARTBEAT_INTERVAL. Более подробно о этих параметрах можно прочесть в документации.
ETCD_INITIAL_ADVERTISE_PEER_URLS - Список равноправных URL-адресов, по которым его могут найти остальные узлы кластера. Эти адреса используются для передачи данных по кластеру. По крайней мере, один из этих адресов должен быть маршрутизируемым для всех членов кластера. Могут содержать доменные имена. Используется только при первом запуске нового узла кластера.
ETCD_ADVERTISE_CLIENT_URLS - Список равноправных URL-адресов, по которым его могут найти остальные узлы кластера. Эти адреса используются для передачи данных по кластеру. По крайней мере, один из этих адресов должен быть маршрутизируемым для всех членов кластера. Могут содержать доменные имена.
ETCD_INITIAL_CLUSTER - Список узлов кластера на момент запуска. Используется только при первом запуске нового узла кластера.
ETCD_INITIAL_CLUSTER_TOKEN - Токен кластера. Должен совпадать на всех узлах кластера. Используется только при первом запуске нового узла кластера.
ETCD_INITIAL_CLUSTER_STATE - может принимать два значения "new" и "existing". Значение "new" используется при первом запуске первого узла в кластере. При значении "existing", узел при старте будет пытаться установить связь с остальными узлами кластера.

Запускаем демон etcd

Копировать в буфер обмена

> sudo systemctl start etcd.service

Проверяем результат

Демон должен быть запущен и находиться в состоянии "active"

Копировать в буфер обмена

> sudo systemctl status etcd.service
? etcd.service - Etcd Server
Loaded: loaded (/usr/lib/systemd/system/etcd.service; disabled; vendor preset: disabled)
Active: active (running) since Wed 2020-03-04 07:39:43 UTC; 18s ago Main PID: 16423 (etcd)
CGroup: /system.slice/etcd.service
 ??16423 /usr/bin/etcd --name=core --data-dir=/var/lib/etcd --listen-client-urls=http://0.0.0.0:2379 Mar 04 07:39:43 core.example etcd[16423]: 18cd9dc4a590c73e became leader at term 6
Mar 04 07:39:43 core.example etcd[16423]: raft.node: 18cd9dc4a590c73e elected leader 18cd9dc4a590c73e at term 6
Mar 04 07:39:43 core.example etcd[16423]: setting up the initial cluster version to 3.3
Mar 04 07:39:43 core.example etcd[16423]: published {Name:core ClientURLs:[http://core.example:...eb8af0
Mar 04 07:39:43 core.example etcd[16423]: ready to serve client requests
Mar 04 07:39:43 core.example etcd[16423]: serving insecure client requests on [::]:2379, this is strongly discouraged!
Mar 04 07:39:43 core.example systemd[1]: Started Etcd Server.
Если запуск завершился с ошибкой, следует проверить сообщения в системном журнале

Копировать в буфер обмена

> journalctl -xel

Кластер должен состоять из одного узла

Копировать в буфер обмена

> etcdctl cluster-health
member 6390c5e1e694effa is healthy: got healthy result from http://core.example:2379 cluster is healthy

Если всё хорошо, то добавляем etcd в автозапуск

Копировать в буфер обмена

> sudo systemctl enable etcd.service
Created symlink from /etc/systemd/system/multi-user.target.wants/etcd.service to /usr/lib/systemd/system/etcd.service.

При запуске etcd пытается найти все узлы, перечисленные в параметре "ETCD_INITIAL_CLUSTER". Если какой-то из узлов не будет найден - запуск завершится сбоем. Если что-то пошло не так, удалите содержимое каталога из параметра:

ETCD_DATA_DIR
  • Добавление нового узла etcd

Добавление нового узла в кластер etcd происходит в два этапа. На первом этапе кластер предупреждается о появлении нового узла.
На втором запускается сам новый узел.
Следующие действия необходимо последовательно выполнить на всех оставшихся серверах стенда.
Для примера будет использоваться dbtwo.example

На core.example выполняем оповещение кластера о появлении нового узла Копировать в буфер обмена

> etcdctl member add dbtwo http://dbtwo.example:2380
Added member named dbtwo with ID 871ff309aeb9cd1 to cluster 
ETCD_NAME="dbtwo" 
ETCD_INITIAL_CLUSTER="dbtwo=http://dbtwo.example:2380,core=http://core.example:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"

Устанавливаем etcd на новый узел и откладываем настройки по умолчанию Копировать в буфер обмена

> sudo yum install -y etcd
> sudo mv /etc/etcd/etcd.conf /etc/etcd/etcd.conf.def

Создаём файл /etc/etcd/etcd.conf со следующим содержимым: Копировать в буфер обмена

#[Member]
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_NAME="dbtwo"
ETCD_HEARTBEAT_INTERVAL="1000"
ETCD_ELECTION_TIMEOUT="5000" 
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://dbtwo.example:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://dbtwo.example:2379"
ETCD_INITIAL_CLUSTER="core=http://core.example:2380,dbtwo=http://dbtwo.example:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-example"
ETCD_INITIAL_CLUSTER_STATE="existing"

Не забывайте менять значение параметра "ETCD_NAME". Параметр "ETCD_INITIAL_CLUSTER" важен для первого запуска, если в нём будут указаны несуществующие сервера или допущены другие ошибки - запуск завершится сбоем. Запускаем демон etcd на dbtwo.example Копировать в буфер обмена

> sudo systemctl start etcd.service

Проверяем результат демон должен быть запущен и находится в состоянии "active" Копировать в буфер обмена

> sudo systemctl status etcd.service

в списке узлов кластера новый узел должен быть в состоянии "healthy" Копировать в буфер обмена

> etcdctl cluster-health
member 871ff309aeb9cd1 is healthy: got healthy result from http://core.example:2379
member 99789c1c8817dff1 is healthy: got healthy result from http://dbtwo.example:2379
cluster is healthy
> etcdctl member list
871ff309aeb9cd1: name=dbtwo peerURLs=http://dbtwo.example:2380 clientURLs=http:dbtwo.example:2379 isLeader=true
99789c1c8817dff1: name=core peerURLs=http://core.example:2380 clientURLs=http://core.example:2379 isLeader=false

Если всё хорошо, тогда добавляем etcd в автозапуск

Копировать в буфер обмена
> sudo systemctl enable etcd.service
Created symlink from /etc/systemd/system/multi-user.target.wants/etcd.service to /usr/lib/systemd/system/etcd.service.

Завершение установки etcd После установки и успешного запуска etcd на всех серверах, следует привести содержание файла /etc/etcd/etcd.conf в окончательное состояние. Для этого необходимо изменить следующие параметры в этом файле на всех серверах.

параметр "ETCD_INITIAL_CLUSTER" должен быть одинаковым на всех узлах Копировать в буфер обмена

ETCD_INITIAL_CLUSTER="core=http://core.example:2380,dbone=http://dbonew.example:2380,dbtwo=http://dbtwo.example:2380,dbthree=http://dbthree.example:2380"

параметр "ETCD_INITIAL_CLUSTER_STATE" следует установить в значение "existing" Копировать в буфер обмена

ETCD_INITIAL_CLUSTER_STATE="existing"

Создание пользователя в etcd Добавим авторизацию по логину и паролю при обращениях на клиентский интерфейс etcd.

Создаём пользователя "root" Копировать в буфер обмена

> etcdctl user add root
New password:
User root created

Это первый пользователь и поэтому ему автоматически назначается роль "root". Копировать в буфер обмена

> etcdctl user get root
User: root
Roles: root

Включаем проверку логина и пароля Копировать в буфер обмена

> etcdctl auth enable

Authentication Enabled Проверяем, что изменения вступили в силу Копировать в буфер обмена

> etcdctl user get root
Insufficient credentials
> etcdctl --username root user get root
Password:
User: root
Roles: root

Установка Patroni

Следующие действия выполняются на всех серверах PostgreSQL (dbone.example, dbtwo.example и dbthree.exemple).

Устанавливаем Python Копировать в буфер обмена

> sudo yum install -y python3
> sudo python3 -m pip install --upgrade pip

проверяем результат: Копировать в буфер обмена

> python3 --version
Python 3.6.8

Устанавливаем зависимости Копировать в буфер обмена

> sudo yum install -y gcc python3-devel
> sudo python3 -m pip install psycopg2-binary

Устанавливаем Patroni Копировать в буфер обмена

> sudo python3 -m pip install patroni[etcd]

Проверяем установку Копировать в буфер обмена

> patroni --version
patroni 1.6.4

Первый узел Patroni Следующие действия выполняются на dbone.example

Создаём каталог настроек Patroni

Копировать в буфер обмена

> sudo mkdir /etc/patroni
> sudo chown postgres:postgres /etc/patroni
> sudo chmod 700 /etc/patroni

Создаём файл настроек /etc/patroni/patroni.yml со следующим содержимым

Копировать в буфер обмена

name: dbone
namespace: /db/
scope: postgres
restapi:
 listen: 0.0.0.0:8008
 connect_address: dbone.example:8008
 authentication:
 username: patroni
 password: patroni
etcd:
 hosts:
 localhost:2379
 username: root
 password: rootpassword
bootstrap:
 dcs:
 ttl: 30
 loop_wait: 10
 retry_timeout: 10
 maximum_lag_on_failover: 1048576
 master_start_timeout: 300
 postgresql:
 use_pg_rewind: true
 use_slots: true
 parameters:
 wal_level: replica
 hot_standby: "on"
 wal_keep_segments: 8
 max_wal_senders: 5
 max_replication_slots: 5
 checkpoint_timeout: 30
 initdb:
 - auth-host: md5
 - auth-local: peer
 - encoding: UTF8
 - data-checksums
 - locale: ru_RU.UTF-8
 pg_hba:
 - host replication replicator samenet md5
 - host replication all 127.0.0.1/32 md5
 - host replication all ::1/128 md5
 users:
 usr1cv8:
 password: usr1cv8
 options:
 - superuser
postgresql:
 listen: 0.0.0.0:5432
 connect_address: dbone.example:5432
 config_dir: /var/lib/pgsql/11/data
 bin_dir: /usr/pgsql-11/bin/
 data_dir: /var/lib/pgsql/11/data
 pgpass: /tmp/pgpass
 authentication:
 superuser:
 username: postgres
 password: V6OxPStvMPh0V7Q982DG
 replication:
 username: replicator
 password: PdR2lIAdwNdcSP4erAXc
 rewind:
 username: rewind_user
 password: WfcuDtEzEbLCHBaYQXX3
 parameters:
 unix_socket_directories: '/var/run/postgresql/'
tags:
 nofailover: false
 noloadbalance: false
 clonefrom: false
 nosync: false

В приведённом примере настроек, есть ряд параметров, влияющих на выполнение переключения на резервный сервер.

maximum_lag_on_failover - максимальное количество байт, на которые может отставать резервный сервер от ведущего, участвующий в выборах нового лидера. master_start_timeout - задержка в секундах, между обнаружением аварийной ситуации и началом отработки переключения на резервный сервер. По умолчанию 300 секунд. Если задано 0, то переключение начнётся немедленно. При использовании асинхронной репликации (как в приведённом примере) это может привести к потере последних транзакций. Максимальное время переключения на реплику равно "loop_wait" + "master_start_timeout" + "loop_wait". Если "master_start_timeout" установленно в 0, то это время становится равно значению параметра "loop_wait". Подробнее о возможных вариантах репликации можно прочитать в документации Создаём сервис для запуска демона Patroni

Создаём unit файл /etc/systemd/system/patroni.service Копировать в буфер обмена

[Unit]
Description=Runners to orchestrate a high-availability PostgreSQL
After=syslog.target network.target
[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/usr/local/bin/patroni /etc/patroni/patroni.yml
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=process
TimeoutSec=30
Restart=no
[Install]
WantedBy=multi-user.target

В приведённых настройках запуска особенно важны два параметра

TimeoutSec - время в секундах, которое система будет ожидать при запуске и остановке сервиса, перед тем как произвести попытку его внештатного завершения.

Restart - может принимать значения: no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, или always. Определяет политику перезапуска сервиса в случае, если он завершает работу не по команде от systemd. Обновляем системные настройки Копировать в буфер обмена

> sudo systemctl daemon-reload

Запускаем демон Patroni

Копировать в буфер обмена

> sudo systemctl start patroni.service

Проверяем успешность запуска

Статус службы должен быть "active" Копировать в буфер обмена

> sudo systemctl status patroni.service
? patroni.service - Runners to orchestrate a high-availability PostgreSQL
Loaded: loaded (/etc/systemd/system/patroni.service; disabled; vendor preset: disabled)
Active: active (running) since Wed 2020-03-04 09:22:32 UTC; 7s ago
Main PID: 19572 (patroni)
CGroup: /system.slice/patroni.service
 ??19572 /usr/bin/python3 /usr/local/bin/patroni /etc/patroni/patroni.yml
 ??19581 /usr/pgsql-11/bin/pg_ctl initdb -D /var/lib/pgsql/11/data -o --encoding=UTF8 --data-checksums --locale=ru_RU.UTF-8 --use...
 ??19583 /usr/pgsql-11/bin/initdb -D /var/lib/pgsql/11/data --encoding=UTF8 --data-checksums --locale=ru_RU.UTF-8 --username=post...
Mar 04 09:22:33 dbone.example patroni[19572]: Data page checksums are enabled.
Mar 04 09:22:33 dbone.example patroni[19572]: fixing permissions on existing directory /var/lib/pgsql/11/data ... ok
Mar 04 09:22:33 dbone.example patroni[19572]: creating subdirectories ... ok
Mar 04 09:22:33 dbone.example patroni[19572]: selecting default max_connections ... 100
Mar 04 09:22:33 dbone.example patroni[19572]: selecting default shared_buffers ... 128MB
Mar 04 09:22:33 dbone.example patroni[19572]: selecting default timezone ... UTC
Mar 04 09:22:33 dbone.example patroni[19572]: selecting dynamic shared memory implementation ... posix
Mar 04 09:22:33 dbone.example patroni[19572]: creating configuration files ... ok
Mar 04 09:22:33 dbone.example patroni[19572]: running bootstrap script ... ok
Mar 04 09:22:34 dbone.example patroni[19572]: performing post-bootstrap initialization ... ok

В каталоге указанном в параметре "data_dir" из секции "postgresql" (/var/lib/pgsql/11/data) должна появиться структура каталогов кластера PostgreSQL Копировать в буфер обмена

> sudo ls -l /var/lib/pgsql/11/data
drwx------. 5 postgres postgres 38 Mar 4 09:28 base
drwx------. 2 postgres postgres 4096 Mar 4 09:28 global
drwx------. 2 postgres postgres 6 Mar 4 09:28 pg_commit_ts
drwx------. 2 postgres postgres 6 Mar 4 09:28 pg_dynshmem
-rw-------. 1 postgres postgres 4581 Mar 4 09:28 pg_hba.conf
-rw-------. 1 postgres postgres 1636 Mar 4 09:28 pg_ident.conf
drwx------. 4 postgres postgres 65 Mar 4 09:28 pg_logical
drwx------. 4 postgres postgres 34 Mar 4 09:28 pg_multixact
drwx------. 2 postgres postgres 17 Mar 4 09:28 pg_notify
drwx------. 2 postgres postgres 6 Mar 4 09:28 pg_replslot
drwx------. 2 postgres postgres 6 Mar 4 09:28 pg_serial
drwx------. 2 postgres postgres 6 Mar 4 09:28 pg_snapshots
drwx------. 2 postgres postgres 6 Mar 4 09:28 pg_stat
drwx------. 2 postgres postgres 6 Mar 4 09:28 pg_stat_tmp
drwx------. 2 postgres postgres 17 Mar 4 09:28 pg_subtrans
drwx------. 2 postgres postgres 6 Mar 4 09:28 pg_tblspc
drwx------. 2 postgres postgres 6 Mar 4 09:28 pg_twophase
-rw-------. 1 postgres postgres 3 Mar 4 09:28 PG_VERSION
drwx------. 3 postgres postgres 58 Mar 4 09:28 pg_wal
drwx------. 2 postgres postgres 17 Mar 4 09:28 pg_xact
-rw-------. 1 postgres postgres 88 Mar 4 09:28 postgresql.auto.conf
-rw-------. 1 postgres postgres 24084 Mar 4 09:28 postgresql.conf
PostgreSQL должен быть запущен и слушать порт 5432

Копировать в буфер обмена

> ss -ltn | grep 5432
LISTEN 0 128 0.0.0.0:5432 0.0.0.0:*

Должна быть возможность подключения к кластеру PostgreSQL под пользователем usr1cv8 Копировать в буфер обмена

> psql -U usr1cv8 -d postgres

Если все хорошо, добавляем patroni.service в автозапуск

Копировать в буфер обмена

> sudo systemctl enable patroni.service

Created symlink from /etc/systemd/system/multi-user.target.wants/patroni.service to /etc/systemd/system/patroni.service. Настройка patronictl Создание файла с настройками по умолчанию позволит не указывать настройки подключения для patronictl.

Создаём файл ~/.config/patroni/patronictl.yaml со следующим содержимым: Копировать в буфер обмена

dcs_api:
 etcd://localhost:2379
namespace: /db/
scope: postgres
authentication:
 username: patroni
 password: patroni

Проверяем результат выполненных настроек Копировать в буфер обмена

> patronictl list

или

> patronictl -c /etc/patroni/patronictl.yml list
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| Cluster | Member | Host | Role | State | TL | Lag in MB | Pending restart |
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| postgres | dbone | dbone.example | Leader | running | 1 | 0.0 | |
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
  • Добавление нового узла Patroni

Для добавления нового узла в кластер Patroni, выполняются действия из раздела "Первый узел Patroni". Различия будут заключаться только в файле /etc/patroni/patroni.yml - все изменения заключаются в замене "dbone" на имя текущего сервера. Поэтому для dbtwo.example содержимое будет следующим:

Копировать в буфер обмена

name: dbtwo
namespace: /db/
scope: postgres
restapi:
 listen: 0.0.0.0:8008
 connect_address: dbtwo.example:8008
 authentication:
 username: patroni
 password: patroni
etcd:
 hosts:
 localhost:2379
 username: root
 password: rootpassword
bootstrap:
 dcs:
 ttl: 30
 loop_wait: 10
 retry_timeout: 10
 maximum_lag_on_failover: 1048576
 master_start_timeout: 300
 postgresql:
 use_pg_rewind: true
 use_slots: true
 parameters:
 wal_level: replica
 hot_standby: "on"
 wal_keep_segments: 8
 max_wal_senders: 5
 max_replication_slots: 5
 checkpoint_timeout: 30
 initdb:
 - auth-host: md5
 - auth-local: peer
 - encoding: UTF8
 - data-checksums
 - locale: ru_RU.UTF-8
 pg_hba:
 - host replication replicator samenet md5
 - host replication all 127.0.0.1/32 md5
 - host replication all ::1/128 md5
 users:
 usr1cv8:
 password: usr1cv8
 options:
 - superuser
postgresql:
 listen: 0.0.0.0:5432
 connect_address: dbtwo.example:5432
 config_dir: /var/lib/pgsql/11/data
 bin_dir: /usr/pgsql-11/bin/
 data_dir: /var/lib/pgsql/11/data
 pgpass: /tmp/pgpass
 authentication:
 superuser:
 username: postgres
 password: V6OxPStvMPh0V7Q982DG
 replication:
 username: replicator
 password: PdR2lIAdwNdcSP4erAXc
 rewind:
 username: rewind_user
 password: WfcuDtEzEbLCHBaYQXX3
 parameters:
 unix_socket_directories: '/var/run/postgresql/'
tags:
 nofailover: false
 noloadbalance: false
 clonefrom: false
 nosync: false

После запуска Patroni на резервном сервере происходит следующее:

Patroni подключается к кластеру на dbone.example Создаётся новый кластер PostgreSQL и заполняется данными с dbone.example Новый кластер PostgreSQL переводится в "slave mode" В результате в кластере Patroni должно быть три узла:

Копировать в буфер обмена

> patronictl list
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| Cluster | Member | Host | Role | State | TL | Lag in MB | Pending restart |
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| postgres | dbone | 10.0.0.21 | Leader | running | 1 | | |
| postgres | dbtwo | 10.0.0.22 | | running | 1 | 0.0 | |
| postgres | dbthree | 10.0.0.23 | | running | 1 | 0.0 | |
+----------+------------+---------------+--------+---------+----+-----------+-----------------+

Установка HAProxy

В настройках информационной базы, в кластере 1С:Предприятие, необходимо указывать имя сервера СУБД. В нашем случае им может быть один из серверов PostgreSQL. Так как указать все имена сразу мы не можем, а роли серверов (ведущий сервер и резервный сервер) могут поменяться в любой момент, следует создать точку подключения к PostgreSQL. В качестве точки подключения будет выступать HAProxy установленный на core.example. В задачe HAProxy будет входить слежение за ролями серверов PostgreSQL и, в случае их изменения, оперативное перенаправление запросов от 1С:Предприятие к СУБД на новый ведущий сервер.

Следующие действия выполняются на core.example

Устанавливаем HPAroxy из пакета и откладываем в сторону настройки по умолчанию Копировать в буфер обмена

> sudo yum install -y haproxy
> sudo mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.conf.def

Создаём файл /etc/haproxy/haproxy.cfg со следующим содержимым Копировать в буфер обмена

global
 maxconn 100
defaults
 log global
 mode tcp
 retries 2
 timeout client 30m
 timeout connect 4s
 timeout server 30m
 timeout check 5s
listen stats
 mode http
 bind *:7000
 stats enable
 stats uri /
listen postgres
 bind *:5432
 option httpchk
 http-check expect status 200
 default-server inter 3s fastinter 1s fall 2 rise 2 on-marked-down shutdown-sessions
 server dbone dbone.example:5432 maxconn 100 check port 8008
 server dbtwo dbtwo.example:5432 maxconn 100 check port 8008
 server dbthree dbthree.example:5432 maxconn 100 check port 8008

Запускаем демон haproxy Если SELinux не выключался, то перед запуском необходимо внести изменения в настройки SELinux, разрешающие HAProxy обращаться на внешние адреса. Копировать в буфер обмена

> sudo setsebool -P haproxy_connect_any=1
> sudo systemctl start haproxy.service

Проверяем результат запуска Копировать в буфер обмена

> sudo systemctl status haproxy.service
? haproxy.service - HAProxy Load Balancer
Loaded: loaded (/usr/lib/systemd/system/haproxy.service; disabled; vendor preset: disabled)
Active: active (running) since Wed 2020-03-04 10:59:47 UTC; 2s ago
Main PID: 17617 (haproxy-systemd)
CGroup: /system.slice/haproxy.service
??17617 /usr/sbin/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
??17618 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
??17619 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
Mar 04 10:59:47 core.example systemd[1]: Started HAProxy Load Balancer.
Mar 04 10:59:47 core.example systemd[1]: Starting HAProxy Load Balancer...
Mar 04 10:59:47 core.example haproxy-systemd-wrapper[17617]: haproxy-systemd-wrapper: executing /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds

Поверяем в интерфейсе haproxy (http://core.example:7000/), что узлы кластера Patroni найдены и их роли определены корректно. Ведущий сервер будет отмечен как "UP", резервные как "DOWN". Если запуск успешен, добавляем демон в автозагрузку Копировать в буфер обмена

> sudo systemctl enable haproxy.service

Created symlink from /etc/systemd/system/multi-user.target.wants/haproxy.service to /usr/lib/systemd/system/haproxy.service. Изменение настроек PostgreSQL через Patroni Так как PostgreSQL теперь управляется Patroni, то и настройки PostgreSQL задаются через конфигурационный файл Paroni. Рекомендуется поддерживать данные настройки одинаковыми на всех узлах. Для задания настроек PostgreSQL используется параметр "parameters" в секции "postgresql" файла /etc/patroni/patroni.yml Сейчас он выглядит следующим образом:

Копировать в буфер обмена parameters:

unix_socket_directories: '/var/run/postgresql/'

Следующие действия выполняются на серверах PostgreSQL.

Приводим "parameters" к следующему виду Копировать в буфер обмена

parameters:
 unix_socket_directories: '/var/run/postgresql/'
 shared_buffers: '1024MB'
 temp_buffers: '256MB'
 work_mem: '64MB'
 standard_conforming_strings: off
 escape_string_warning: off
 shared_preload_libraries: 'online_analyze, plantuner'
 plantuner.fix_empty_table: on
 online_analize.enable: on
 online_analize.table_type: 'temporary'
 online_analize.local_tracking: on
 online_analize.verbose: off
 maintenance_work_mem: '256MB'
 max_locks_per_transaction: 256
 lc_messages: 'en_US.UTF-8'
 log_line_prefix: '%m [%p] %q%u@%d '

Значения параметров приведены только в качестве примера задания значений для этих параметров. Для определения значений парметров следует обратится к следующим статьям Настройки PostgreSQL для работы с 1С:Предприятием и Настройки PostgreSQL для работы с 1С:Предприятием. Часть 2 Применяем настройки Копировать в буфер обмена

> patronictl reload postgres dbone
> patronictl restart postgres dbone
> patronictl reload postgres dbtwo
> patronictl restart postgres dbtwo
> patronictl reload postgres dbthree
> patronictl restart postgres dbthree

Проверяем изменения настроек Копировать в буфер обмена

> psql -U usr1cv8 -d postgres
postgres> SHOW ALL;

Проверка переключения на резервный сервер PostgreSQL Проведём несколько проверок отказоустойчивости кластера.

Подготовка

Создаём информационную базу в кластере 1С:Предприятие. Параметры создания информационной базы:

Копировать в буфер обмена

Имя: test

Сервер баз данных: localhost Тип СУБД: PostgreSQL База данных: test Пользователь сервера БД: usr1cv8 Пароль пользователя сервера БД: usr1cv8 Загружаем в базу произвольную конфигурацию. После окончания загрузки проверяем, что данные переданы на резервный сервер

Копировать в буфер обмена

> patronictl list
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| Cluster | Member | Host | Role | State | TL | Lag in MB | Pending restart |
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| postgres | dbone | 10.0.0.21 | Leader | running | 10 | | |
| postgres | dbtwo | 10.0.0.22 | | running | 1 | 0.0 | |
| postgres | dbthree | 10.0.0.23 | | running | 1 | 0.0 | |
+----------+------------+---------------+--------+---------+----+-----------+-----------------+

в колонке "Lag in MB" должно быть 0.0

Тесты

Для выполнения тестов, откроем информационную базу "Test" в режиме 1С:Предприятие.

Плановое переключение ведущего сервера На dbone.example выполняем команду смены ведущего сервера Копировать в буфер обмена

> patronictl failover

Проверяем переключение Копировать в буфер обмена

> patronictl list
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| Cluster | Member | Host | Role | State | TL | Lag in MB | Pending restart |
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| postgres | dbone | 10.0.0.21 | | running | 10 | 0.0 | |
| postgres | dbtwo | 10.0.0.22 | Leader | running | 1 | | |
| postgres | dbthree | 10.0.0.23 | | running | 10 | 0.0 | |
+----------+------------+---------------+--------+---------+----+-----------+-----------------+

Проверяем работу в информационной базе - 1С:Предприятие изменений в инфраструктуре заменить не должно Сбой ведущего сервера На dbtwo.example выполняем команду для перезагрузки Копировать в буфер обмена

> shutdown -r

На dbone.example проверяем переключение Копировать в буфер обмена

> patronictl list
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| Cluster | Member | Host | Role | State | TL | Lag in MB | Pending restart | 
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| postgres | dbone | 10.0.0.21 | Leader | running | 10 | | |
| postgres | dbtwo | 10.0.0.22 | | running | 1 | 0.0 | |
| postgres | dbthree | 10.0.0.23 | | running | 1 | 0.0 | |
+----------+------------+---------------+--------+---------+----+-----------+-----------------+

Проверяем работу в информационной базе - 1С:Предприятие изменений в инфраструктуре заменить не должно

Резервное копирование Ниже приведён пример создания резервной копии кластера PostgreSQL, теми средствами, что идут в его поставке. Резервная копия может быть снята не только на ведущем сервере, но и на резервном сервере.

Создаём резервную копию Копировать в буфер обмена

> pg_basebackup -D ~/postgres.bak -Ft -z -P -U usr1cv8

Проверяем, что копия создана Копировать в буфер обмена

> ls ~/postgres.bak
base.tar.gz pg_wal.tar.gz

Восстановление после сбоев Ниже приведена последовательность действий для проверки восстановления резервной копии на демонстрационном стенде. Поэтому, в реальной ситуации, раздел "Удаление данных" должен быть пересмотрен с учётом реальных обстоятельств сбоя. Раздел "Восстановление" предполагает, что описанное решение по созданию отказоустойчивого кластера Patroni полностью выполнено и его лишь надо наполнить данными.

Удаление данных

Проверяем текущие роли узлов кластера Patroni Копировать в буфер обмена

> patronictl list

Останавливаем Patroni и PostgreSQL на резервных серверах Копировать в буфер обмена

> sudo systemctl stop patroni
> sudo systemctl stop postgresql-11

Останавливаем Patroni и PostgreSQL на ведущем сервере Копировать в буфер обмена

> sudo systemctl stop patroni
> sudo systemctl stop postgresql-11

Удаляем кластер Patroni Копировать в буфер обмена

> patronictl remove postgres

На всех серверах PostgreSQL удаляем каталоги кластера Копировать в буфер обмена

> sudo rm -rf /var/lib/pgsql/11/data

Восстановление Восстанавливаем каталог кластера PostgreSQL на новом ведущем сервере Следующие действия выполняются на dbone.example

Копировать в буфер обмена

> sudo mkdir /var/lib/pgsql/11/data
> sudo chown postgres:postgres /var/lib/pgsql/11/data
> sudo chmod 700 /var/lib/pgsql/11/data
> sudo tar xzf base.tar.gz -C /var/lib/pgsql/11/data
> sudo tar xzf pg_wal.tar.gz -C /var/lib/pgsql/11/data/pg_wal
> sudo rm /var/lib/pgsql/11/data/recovery.conf
> sudo rm /var/lib/pgsql/11/data/recovery.done
> sudo systemctl start patroni

Переносим файл резервной копии на новый резерный сервер Следующие действия выполняются на dbone.example

Копировать в буфер обмена

> scp -r ~/postgres.bak dbtwo.example:postgres.bak
> scp -r ~/postgres.bak dbthree.example:postgres.bak

Восстанавливаем каталог кластера PostgreSQL на резервном сервере. На dbtwo.example и dbthree.example, повторяем действия выполненные на dbone.example

Проверяем результат

Копировать в буфер обмена

> patronictl list
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| Cluster | Member | Host | Role | State | TL | Lag in MB | Pending restart | 
+----------+------------+---------------+--------+---------+----+-----------+-----------------+
| postgres | dbone | 10.0.0.21 | Leader | running | 1 | | |
| postgres | dbtwo | 10.0.0.22 | | running | 1 | 0.0 | |
| postgres | dbthree | 10.0.0.23 | | running | 1 | 0.0 | |
+----------+------------+---------------+--------+---------+----+-----------+-----------------+

Использование резервного сервера PostgreSQL для механизма копий базы данных Механизм копий баз данных имеет особенность в использовании внешнего типа репликации данных при размещении данных в PostgreSQL. Особенность связана с тем, что резервный сервер PostgreSQL доступен только на чтение, включая временные таблицы. Такое ограничение приводит к тому, что запросы к СУБД, с созданием временных таблиц, выполняемые 1С:Предприятие, будут завершаться с ошибкой. Для снятия этого ограничения требуется развёртывание дополнительного кластера PostgreSQL, доступного для записи данных. Ниже будет описан пример развёртывания такого сервера.

Создание дополнительного кластера PostgreSQL

Для создания дополнительного кластера PostgreSQL можно добавить новый сервер и установить на него PostgreSQL. В этом случае порядок действий не отличается от обычной установки PostgreSQL. В случае, если добавление нового сервера нежелательно, дополнительный кластер PostgreSQL можно разместить на одном из существующих серверов PostgreSQL. Ниже показан пример развёртывания дополнительного кластера на уже существующем сервере. Следующие действия выполняются на dbthree.example Данные кластера будут расположены в каталоге /var/lib/pgsql/11/proxy

  • Инициализируем кластер

Копировать в буфер обмена

> export LANG="ru_RU.UTF-8"
> sudo -u postgres /usr/pgsql-11/bin/initdb -D /var/lib/pgsql/11/proxy --auth-local=peer --auth-host=md5
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
The database cluster will be initialized with locales
COLLATE: ru_RU.UTF-8
CTYPE: ru_RU.UTF-8
MESSAGES: ru_RU.UTF-8
MONETARY: en_GB.UTF-8
NUMERIC: en_GB.UTF-8
TIME: en_GB.UTF-8
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "russian".
Data page checksums are disabled.
creating directory /var/lib/pgsql/11/proxy ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default timezone ... Europe/Moscow
selecting dynamic shared memory implementation ... posix
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok
Success. You can now start the database server using:
/usr/pgsql-11/bin/pg_ctl -D /var/lib/pgsql/11/proxy -l logfile start

Создаём unit файл /etc/systemd/system/postgresql-11-proxy.service Копировать в буфер обмена

[Unit]
Description=PostgreSQL 11 database proxy server
After=syslog.target
After=network.target
[Service]
Type=notify
User=postgres
Group=postgres
Environment=PGDATA=/var/lib/pgsql/11/proxy/
OOMScoreAdjust=-1000
ExecStartPre=/usr/pgsql-11/bin/postgresql-11-check-db-dir ${PGDATA}
ExecStart=/usr/pgsql-11/bin/postmaster -D ${PGDATA}
ExecReload=/bin/kill -HUP $MAINPID
KillMode=mixed
KillSignal=SIGINT
TimeoutSec=300
[Install]

WantedBy=multi-user.target В файле /var/lib/pgsql/11/proxy/postgre.sql задаём порт, на котором будут приниматься подключения от клиентов Копировать в буфер обмена

#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------
# - Connection Settings -
listen_addresses = '*' # what IP address(es) to listen on;
 # comma-separated list of addresses;
 # defaults to 'localhost'; use '*' for all
 # (change requires restart)
port = 5433

Обновляем системные настройки и запускаем второй демон PostgreSQL Копировать в буфер обмена

> sudo systemctl daemon-reload
> sudo systemctl start postgresql-11-proxy.service

Проверяем успешность запуска Копировать в буфер обмена

> ss -ltn | grep 5433
LISTEN 0 128 0.0.0.0:5433 0.0.0.0:*

Если запуск успешен, добавляем демон в автозапуск Копировать в буфер обмена

> sudo systemctl enable postgresql-11-proxy.service

Настройка кластера Создаём пользователя для 1С:Предприятие входим пользователем postgres Копировать в буфер обмена

> sudo -u postgres psql -p 5433

создаём нового пользователя Копировать в буфер обмена

posgres> CREATE USER usr1cv8 WITH SUPERUSER LOGIN ENCRYPTED PASSWORD 'usr1cv8';

Создаём информационную базу в кластере 1С:Предприятие Копировать в буфер обмена Имя: test Сервер баз данных: dbthree.example port=5433 Тип СУБД: PostgreSQL База данных: test Пользователь сервера БД: usr1cv8 Пароль пользователя сервера БД: usr1cv8 Создание информационной базы необходимо для корректного создания базы данных в кластере PostgreSQL. При создании базы данных 1С:Предприятие выполняет тонкие настройки в создаваемой базе данных, которые могут зависеть от версии платформы 1С:Предприятие. Настройка промежуточной базы данных Входим пользователем postgres в созданную базу данных Копировать в буфер обмена

> sudo -u postgres psql -p 5433 -d test

Удаляем все таблицы Копировать в буфер обмена

select 'DROP TABLE ' || string_agg(tablename, ', ') || ';' from pg_tables where schemaname = 'public' \gexec

Создаём в базе расширение postgres_fdw Копировать в буфер обмена

CREATE EXTENSION postgres_fdw;

Создаём сервер, указывая параметры для подключения к резервному серверу Копировать в буфер обмена

CREATE SERVER standby_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host , dbname 'test', port '5432');

Создаём отображение для пользователей промежуточного сервера Копировать в буфер обмена

CREATE USER MAPPING FOR postgres SERVER standby_server OPTIONS (user 'usr1cv8', password 'usr1cv8');
CREATE USER MAPPING FOR usr1cv8 SERVER standby_server OPTIONS (user 'usr1cv8', password 'usr1cv8');

Импортируем таблицы внешней базы Копировать в буфер обмена

IMPORT FOREIGN SCHEMA public FROM SERVER standby_server INTO public;

В настройках подключения к копии базы данных следует указывать промежуточную базу:

Копировать в буфер обмена Сервер: dbthree.example port=5433 База данных: test В варианте использования резервного сервера для копии базы данных, имеет смысл запретить Patroni выбирать этот сервер в качестве ведущего. Для этого следует изменить секцию "tags" patroni.yml По умолчанию, секция "tags" содержит следующие параметры:

Копировать в буфер обмена

tags:
 nofailover: false
 noloadbalance: false
 clonefrom: false
 nosync: false

nofailover - значение "true" запрещает выбирать этот узел в качестве ведущего clonefrom - значение "true" рекомендует выбирать этот узел для создания резервной копии при развёртывании нового узла Patroni. Если значение "true" установлено у нескольких узлов, будет случайным образом выбран один из них noloadbalance - устанавливает HTTP код возврата 503 для запроса GET /replica REST API nosync - значение true запрещает выбирать этот узел для синхронной репликации В результате выполнения указанных настроек внешняя синхронизация для механизма копий баз данных при использовании СУБД MS SQL Server, а также выполнение запросов на копии, должны работать корректно.

Реструктуризация информационной базы

В случае изменения структуры метаданных информационной базы в структуру базы данных вносятся соответствующие изменения. Эти изменения необходимо перенести в промежуточную базу данных. Для этого следует удалить импортированные таблицы и импортировать схему заново. Следующие действия выполнятся на dbthree.example.

Входим пользователем postgres в промежуточную базу данных Копировать в буфер обмена

> sudo -u postgres psql -p 5433 -d test

Удаляем импортированные таблицы Копировать в буфер обмена

select 'DROP FOREIGN TABLE ' || string_agg(table_name, ', ') || ';' from information_schema.tables where table_type = 'FOREIGN' \gexec

Импортируем таблицы внешней базы Копировать в буфер обмена

IMPORT FOREIGN SCHEMA public FROM SERVER standby_server INTO public;

ИСТОЧНИК:


Полезное:

--