PostgreSQL
06.04.2020
Целью данной статьи является описание способов построения отказоустойчивого кластера PostgreSQL, для систем на базе 1С:Предприятие, подобного решению "MS SQL Server Always On Availability Groups" от Microsoft. Построение такого отказоустойчивого кластера затруднено тем, что в PostgreSQL не предоставляет механизмов автоматического переключения на резервный сервер. Ниже будет описано, каким образом можно реализовать автоматическое переключение на резервный сервер в случае сбоя ведущего сервера PostgreSQL.
Используемые программные продукты
Patroni - шаблон для построения отказоустойчивых кластеров на базе PostgreSQL. Обеспечивает автоматическое переключение на резервный сервер при сбое ведущего сервера. Patroni управляет экземпляром базы данных PostgreSQL и использует внешние системы (ZooKeeper, etcd, Consul или Kubernetes) для хранения общей для всех узлов кластера информации, такой как список узлов кластера и положение ведущего сервера.
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 и проверим результат. Для этого выполняем следующие команды:
> sudo hostnamectl set-hostname core.example --static > hostname core.example
> sudo hostnamectl set-hostname dbone.example --static > hostname dbone.example
> sudo hostnamectl set-hostname dbtwo.example --static > hostname dbtwo.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
На этот сервер следует установить и запустить сервер 1С:Предприятие. Подробную инструкцию по установке можно найти по ссылке: https://its.1c.ru/db/metod8dev#content:5953:hdoc
На этом этапе настройки этих машин не отличаются. На обоих необходимо установить 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.
Ниже описано создание кластера etcd и добавление нового узла в кластер. Затем будет построен кластер Patroni, использующий etcd для хранения своей конфигурации. После будет настроен HAProxy, который будет отслеживать смену ведущего сервера и скрывать это событие от 1С:Предприятие.
Кластер 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" #[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 происходит в два этапа. На первом этапе кластер предупреждается о появлении нового узла. На втором запускается сам новый узел.
Следующие действия необходимо последовательно выполнить на всех оставшихся серверах стенда. Для примера будет использоваться dbtwo.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"
> sudo yum install -y etcd > sudo mv /etc/etcd/etcd.conf /etc/etcd/etcd.conf.def
#[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" важен для первого запуска, если в нём будут указаны несуществующие сервера или допущены другие ошибки - запуск завершится сбоем.
> sudo systemctl start etcd.service
> sudo systemctl status etcd.service
> 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
> 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 на всех серверах, следует привести содержание файла /etc/etcd/etcd.conf в окончательное состояние. Для этого необходимо изменить следующие параметры в этом файле на всех серверах.
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.
> 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
Следующие действия выполняются на всех серверах PostgreSQL (dbone.example, dbtwo.example и dbthree.exemple).
> 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
> sudo python3 -m pip install patroni[etcd]
> patroni --version patroni 1.6.4
Следующие действия выполняются на 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
В приведённом примере настроек, есть ряд параметров, влияющих на выполнение переключения на резервный сервер.
Создаём сервис для запуска демона Patroni
[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В приведённых настройках запуска особенно важны два параметра
> sudo systemctl daemon-reload
Запускаем демон Patroni
Копировать в буфер обмена> sudo systemctl start patroni.service
Проверяем успешность запуска
> 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
> 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
> ss -ltn | grep 5432 LISTEN 0 128 0.0.0.0:5432 0.0.0.0:*
> 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.
dcs_api: etcd://localhost:2379 namespace: /db/ scope: postgres authentication: username: patroni password: patroni
> patronictl list +----------+------------+---------------+--------+---------+----+-----------+-----------------+ | Cluster | Member | Host | Role | State | TL | Lag in MB | Pending restart | +----------+------------+---------------+--------+---------+----+-----------+-----------------+ | postgres | dbone | dbone.example | Leader | running | 1 | 0.0 | | +----------+------------+---------------+--------+---------+----+-----------+-----------------+
Для добавления нового узла в кластер 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 должно быть три узла:
Копировать в буфер обмена> 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 | | +----------+------------+---------------+--------+---------+----+-----------+-----------------+
В настройках информационной базы, в кластере 1С:Предприятие, необходимо указывать имя сервера СУБД. В нашем случае им может быть один из серверов PostgreSQL. Так как указать все имена сразу мы не можем, а роли серверов (ведущий сервер и резервный сервер) могут поменяться в любой момент, следует создать точку подключения к PostgreSQL. В качестве точки подключения будет выступать HAProxy установленный на core.example. В задачe HAProxy будет входить слежение за ролями серверов PostgreSQL и, в случае их изменения, оперативное перенаправление запросов от 1С:Предприятие к СУБД на новый ведущий сервер.
Следующие действия выполняются на core.example
> sudo yum install -y haproxy > sudo mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.conf.def
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
> 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 задаются через конфигурационный файл Paroni. Рекомендуется поддерживать данные настройки одинаковыми на всех узлах. Для задания настроек PostgreSQL используется параметр "parameters" в секции "postgresql" файла /etc/patroni/patroni.yml Сейчас он выглядит следующим образом:
Копировать в буфер обменаparameters: unix_socket_directories: '/var/run/postgresql/'
Следующие действия выполняются на серверах PostgreSQL.
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;
Проведём несколько проверок отказоустойчивости кластера.
Создаём информационную базу в кластере 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С:Предприятие.
> 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 | | +----------+------------+---------------+--------+---------+----+-----------+-----------------+
> shutdown -r
> 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 полностью выполнено и его лишь надо наполнить данными.
> patronictl list
> sudo systemctl stop patroni > sudo systemctl stop postgresql-11
> sudo systemctl stop patroni > sudo systemctl stop postgresql-11
> patronictl remove postgres
> 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 доступен только на чтение, включая временные таблицы. Такое ограничение приводит к тому, что запросы к СУБД, с созданием временных таблиц, выполняемые 1С:Предприятие, будут завершаться с ошибкой.
Для снятия этого ограничения требуется развёртывание дополнительного кластера 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] 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
#------------------------------------------------------------------------------ # 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
> 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
> sudo -u postgres psql -p 5433
posgres> CREATE USER usr1cv8 WITH SUPERUSER LOGIN ENCRYPTED PASSWORD 'usr1cv8';
Имя: test Сервер баз данных: dbthree.example port=5433 Тип СУБД: PostgreSQL База данных: test Пользователь сервера БД: usr1cv8 Пароль пользователя сервера БД: usr1cv8Создание информационной базы необходимо для корректного создания базы данных в кластере PostgreSQL. При создании базы данных 1С:Предприятие выполняет тонкие настройки в создаваемой базе данных, которые могут зависеть от версии платформы 1С:Предприятие.
> sudo -u postgres psql -p 5433 -d test
select 'DROP TABLE ' || string_agg(tablename, ', ') || ';' from pg_tables where schemaname = 'public' \gexec
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
В результате выполнения указанных настроек внешняя синхронизация для механизма копий баз данных при использовании СУБД MS SQL Server, а также выполнение запросов на копии, должны работать корректно.
В случае изменения структуры метаданных информационной базы в структуру базы данных вносятся соответствующие изменения. Эти изменения необходимо перенести в промежуточную базу данных. Для этого следует удалить импортированные таблицы и импортировать схему заново.
Следующие действия выполнятся на dbthree.example.
> 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;