PostgreSQL
06.04.2020

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

Введение

Целью данной статьи является описание способов построения отказоустойчивого кластера 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 и проверим результат. Для этого выполняем следующие команды:

Подготовка дистрибутивов

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

Сервер 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С:Предприятие. Подробную инструкцию по установке можно найти по ссылке: https://its.1c.ru/db/metod8dev#content:5953:hdoc

DBOne.example, DBTwo.example, DBThree.example

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

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

    Копировать в буфер обмена
    > sudo yum install -y postgresql11-1c-*.rpm
    

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

  2. Запрещаем автоматический запуск 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 выполняем следующие действия:

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

    Копировать в буфер обмена
    > sudo yum install -y etcd
    
  2. Откладываем в сторону настройки по умолчанию

    Копировать в буфер обмена
    > sudo mv /etc/etcd/etcd.conf /etc/etcd/etcd.conf.def
    
  3. Используя текстовый редактор создаём новый файл настроек /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", узел при старте будет пытаться установить связь с остальными узлами кластера.

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

    Копировать в буфер обмена
    > sudo systemctl start etcd.service
    
  5. Проверяем результат

  6. Если всё хорошо, то добавляем 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

  1. На 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"
    
  2. Устанавливаем etcd на новый узел и откладываем настройки по умолчанию Копировать в буфер обмена
    > sudo yum install -y etcd
    > sudo mv /etc/etcd/etcd.conf /etc/etcd/etcd.conf.def
    
  3. Создаём файл /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" важен для первого запуска, если в нём будут указаны несуществующие сервера или допущены другие ошибки - запуск завершится сбоем.
  4. Запускаем демон etcd на dbtwo.example Копировать в буфер обмена
    > sudo systemctl start etcd.service
    
  5. Проверяем результат
  6. Если всё хорошо, тогда добавляем 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

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

  1. Создаём пользователя "root" Копировать в буфер обмена
    > etcdctl user add root
    New password:
    User root created
    
    Это первый пользователь и поэтому ему автоматически назначается роль "root". Копировать в буфер обмена
    > etcdctl user get root
    User: root
    Roles: root
    
  2. Включаем проверку логина и пароля Копировать в буфер обмена
    > etcdctl auth enable
    Authentication Enabled
    
  3. Проверяем, что изменения вступили в силу Копировать в буфер обмена
    > etcdctl user get root
    Insufficient credentials
    > etcdctl --username root user get root
    Password:
    User: root
    Roles: root
    

Patroni

Установка Patroni

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

  1. Устанавливаем Python Копировать в буфер обмена
    > sudo yum install -y python3
    > sudo python3 -m pip install --upgrade pip
    
    проверяем результат: Копировать в буфер обмена
    > python3 --version
    Python 3.6.8
    
  2. Устанавливаем зависимости Копировать в буфер обмена
    > sudo yum install -y gcc python3-devel
    > sudo python3 -m pip install psycopg2-binary
    
  3. Устанавливаем Patroni Копировать в буфер обмена
    > sudo python3 -m pip install patroni[etcd]
    
  4. Проверям установку Копировать в буфер обмена
    > patroni --version
    patroni 1.6.4
    

Первый узел Patroni

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

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

    Копировать в буфер обмена
    > sudo mkdir /etc/patroni
    > sudo chown postgres:postgres /etc/patroni
    > sudo chmod 700 /etc/patroni
    
  2. Создаём файл настроек /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
    

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

  3. Создаём сервис для запуска демона Patroni

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

    Копировать в буфер обмена
    > sudo systemctl start patroni.service
    
  5. Проверяем успешность запуска

  6. Если все хорошо, добавляем 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.

  1. Создаём файл ~/.config/patroni/patronictl.yaml со следующим содержимым: Копировать в буфер обмена
    dcs_api:
     etcd://localhost:2379
    namespace: /db/
    scope: postgres
    authentication:
     username: patroni
     password: patroni
    
  2. Проверяем результат выполненных настроек Копировать в буфер обмена
    > patronictl 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 должно быть три узла:

Копировать в буфер обмена
> 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

Установка HAProxy

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

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

  1. Устанавливаем HPAroxy из пакета и откладываем в сторону настройки по умолчанию Копировать в буфер обмена
    > sudo yum install -y haproxy
    > sudo mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.conf.def
    
  2. Создаём файл /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
    
  3. Запускаем демон haproxy
    Если SELinux не выключался, то перед запуском необходимо внести изменения в настройки SELinux, разрешающие HAProxy обращаться на внешние адреса. Копировать в буфер обмена
    > sudo setsebool -P haproxy_connect_any=1
    > sudo systemctl start haproxy.service
    
  4. Проверяем результат запуска Копировать в буфер обмена
    > 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".
  5. Если запуск успешен, добавляем демон в автозагрузку Копировать в буфер обмена
    > 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.

  1. Приводим "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
  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
    
  3. Проверяем изменения настроек Копировать в буфер обмена
    > 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С:Предприятие.

Плановое переключение ведущего сервера

  1. На dbone.example выполняем команду смены ведущего сервера Копировать в буфер обмена
    > patronictl failover
    
  2. Проверяем переключение Копировать в буфер обмена
    > 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 | |
    +----------+------------+---------------+--------+---------+----+-----------+-----------------+
    
  3. Проверяем работу в информационной базе - 1С:Предприятие изменений в инфраструктуре заменить не должно

Сбой ведущего сервера

  1. На dbtwo.example выполняем команду для перезагрузки Копировать в буфер обмена
    > shutdown -r
    
  2. На 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, теми средствами, что идут в его поставке. Резервная копия может быть снята не только на ведущем сервере, но и на резервном сервере.

  1. Создаём резервную копию Копировать в буфер обмена
    > pg_basebackup -D ~/postgres.bak -Ft -z -P -U usr1cv8
    
  2. Проверяем, что копия создана Копировать в буфер обмена
    > ls ~/postgres.bak
    base.tar.gz pg_wal.tar.gz
    

Восстановление после сбоев

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

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

  1. Проверяем текущие роли узлов кластера Patroni Копировать в буфер обмена
    > patronictl list
    
  2. Останавливаем Patroni и PostgreSQL на резервных серверах Копировать в буфер обмена
    > sudo systemctl stop patroni
    > sudo systemctl stop postgresql-11
    
  3. Останавливаем Patroni и PostgreSQL на ведущем сервере Копировать в буфер обмена
    > sudo systemctl stop patroni
    > sudo systemctl stop postgresql-11
    
  4. Удаляем кластер Patroni Копировать в буфер обмена
    > patronictl remove postgres
    
  5. На всех серверах PostgreSQL удаляем каталоги кластера Копировать в буфер обмена
    > sudo rm -rf /var/lib/pgsql/11/data
    

Восстановление

  1. Восстанавливаем каталог кластера 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
    
  2. Переносим файл резервной копии на новый резерный сервер
    Следующие действия выполняются на dbone.example

    Копировать в буфер обмена
    > scp -r ~/postgres.bak dbtwo.example:postgres.bak
    > scp -r ~/postgres.bak dbthree.example:postgres.bak
    
  3. Восстанавливаем каталог кластера PostgreSQL на резервном сервере. На dbtwo.example и dbthree.example, повторяем действия выполненные на dbone.example

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

    Копировать в буфер обмена
    > 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

  1. Инициализируем кластер Копировать в буфер обмена
    > 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
    
  2. Создаём 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
    
  3. В файле /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
    
  4. Обновляем системные настройки и запускаем второй демон PostgreSQL Копировать в буфер обмена
    > sudo systemctl daemon-reload
    > sudo systemctl start postgresql-11-proxy.service
    
  5. Проверяем успешность запуска Копировать в буфер обмена
    > ss -ltn | grep 5433
    LISTEN 0 128 0.0.0.0:5433 0.0.0.0:*
    
  6. Если запуск успешен, добавляем демон в автозапуск Копировать в буфер обмена
    > sudo systemctl enable postgresql-11-proxy.service
    

Настройка кластера

  1. Создаём пользователя для 1С:Предприятие Копировать в буфер обмена
    > sudo -u postgres psql -p 5433
    
    Копировать в буфер обмена
    posgres> CREATE USER usr1cv8 WITH SUPERUSER LOGIN ENCRYPTED PASSWORD 'usr1cv8';
    
  2. Создаём информационную базу в кластере 1С:Предприятие Копировать в буфер обмена
    Имя: test
    Сервер баз данных: dbthree.example port=5433
    Тип СУБД: PostgreSQL
    База данных: test
    Пользователь сервера БД: usr1cv8
    Пароль пользователя сервера БД: usr1cv8
    
    Создание информационной базы необходимо для корректного создания базы данных в кластере PostgreSQL. При создании базы данных 1С:Предприятие выполняет тонкие настройки в создаваемой базе данных, которые могут зависеть от версии платформы 1С:Предприятие.

Настройка промежуточной базы данных

  1. Входим пользователем postgres в созданную базу данных Копировать в буфер обмена
    > sudo -u postgres psql -p 5433 -d test
    
  2. Удаляем все таблицы Копировать в буфер обмена
    select 'DROP TABLE ' || string_agg(tablename, ', ') || ';' from pg_tables where schemaname = 'public' \gexec
    
  3. Создаём в базе расширение postgres_fdw Копировать в буфер обмена
    CREATE EXTENSION postgres_fdw;
    
  4. Создаём сервер, указывая параметры для подключения к резервному серверу Копировать в буфер обмена
    CREATE SERVER standby_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '', dbname 'test', port '5432');
    
  5. Создаём отображение для пользователей промежуточного сервера Копировать в буфер обмена
    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');
    
  6. Импортируем таблицы внешней базы Копировать в буфер обмена
    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.

  1. Входим пользователем postgres в промежуточную базу данных Копировать в буфер обмена
    > sudo -u postgres psql -p 5433 -d test
    
  2. Удаляем импортированные таблицы Копировать в буфер обмена
    select 'DROP FOREIGN TABLE ' || string_agg(table_name, ', ') || ';' from information_schema.tables where table_type = 'FOREIGN' \gexec
    
  3. Импортируем таблицы внешней базы Копировать в буфер обмена
    IMPORT FOREIGN SCHEMA public FROM SERVER standby_server INTO public;