Отказоустойчивый PostgreSQL кластер. Часть 4. Высокая доступность. Бэкапирование. Демоны repmgr. Barman.


Часть 1. Введение.
Часть 2. Репликация. Repmgr.
Часть 3. Auto-failover. Демоны repmgr.
Часть 4. Высокая доступность. Бэкапирование. Демоны repmgr. Barman.
Часть 5. Тестирование. Ansible. Вместо послесловия.

В этой части мы:

  • сделаем PostgreSQL высоко доступным, используя демоны repmgr
  • применим barman для потокового бэкапирования
  • используем высокую доступность и barman вместе чтобы хранить бэкапы мастера и резервного сервера вместе, чтобы сэкономить место на диске

По умолчанию, barman может бэкапировать данные с мастера и резервного сервера параллельно, храня бэкапы в разных местах на диске, а не в одном. Это единственное решение при асинхронной репликации. Если мастер упадет, резервный сервер останется с неактуальными данными, часть данных будет потеряна.

При синхронной репликации мы можем продолжить получение WAL с нового мастера. Это означает, что мы можем хранить бэкапы в одном месте, экономя дисковое пространство. Чтобы сделать это прозрачно, мы воспользуемся фичей высокой доступности.

Демоны repmgr

Для начала сделаем кластер высокодоступным. Мы использем события демонов repmgr чтобы поймать переключения серверов кластера. Правила iptables помогут нам перенаправить траффик barman и сервера приложений с портов 54321 и 54322 соответственно на мастер.
Создайте скрипт с кастомными цепочками iptables на сервере backup.
/usr/local/bin/repmgr_iptables_chains.sh

#!/bin/bash
/sbin/sysctl -w net.ipv4.ip_forward=1
/sbin/iptables -t nat -N repmgr_dnat
/sbin/iptables -t nat -A OUTPUT -j repmgr_dnat
/sbin/iptables -t nat -A PREROUTING -j repmgr_dnat
/sbin/iptables -t nat -N repmgr_snat
/sbin/iptables -t nat -A POSTROUTING -j repmgr_snat
if [[ -f /usr/local/bin/repmgr_master ]]; then
        master=$(cat /usr/local/bin/repmgr_master)
        /usr/bin/sudo -u postgres /usr/local/bin/repmgr_handler.sh $master standby_promote
fi

Убедитесь, что скрипт будет запущен при каждой загрузке сервера.

crontab -e
@reboot /usr/local/bin/repmgr_iptables_chains.sh

Создайте скрипт-помощник. Демоны будут вызывать его при каждом переключении резервного сервера на режим мастера, что сделает наш кластер высокодоступным.
/usr/local/bin/repmgr_iptables.sh

#!/bin/bash
readonly DB="$1"
readonly BACKUP="$2"
readonly PG_PORT="$3"
readonly IN_PORT="$4"

if [ -z "$5" ]; then
        iptables -t nat --flush repmgr_dnat
        iptables -t nat --flush repmgr_snat
        iptables -t nat -A repmgr_snat -p tcp -d $DB --dport $PG_PORT -j SNAT --to-source $BACKUP
fi
iptables -t nat -A repmgr_dnat -p tcp -d $BACKUP --dport $IN_PORT -j DNAT --to-destination $DB:$PG_PORT

Создайте скрипт-обработчик событий демонов.
/usr/local/bin/repmgr_handler.sh

#!/bin/bash
readonly DB1="10.8.1.1"
readonly DB2="10.8.2.1"
readonly BACKUP="10.8.3.1"
readonly SLOT_NAME="barman"
readonly BARMAN_NAME="main_backup"
readonly PG_PORT="5432"
readonly BARMAN_PORT="54321"
readonly MASTER_PORT="54322"
readonly NODE_ID="$1"
EVENT="$2"

if [[ "$NODE_ID" == "1" ]]; then
        IP="$DB1"
else
        IP="$DB2"
fi

if [[ "$EVENT" =~ ^(primary_register|standby_promote|repmgrd_failover_promote)$ ]]; then
        sudo /usr/local/bin/repmgr_iptables.sh $IP $BACKUP $PG_PORT $BARMAN_PORT
        echo "$NODE_ID" > /usr/local/bin/repmgr_master
        psql -h $IP -p $PG_PORT -U repmgr -d repmgr -c "select pg_create_physical_replication_slot('$SLOT_NAME') where not exists(select 1 from pg_replication_slots where slot_name = '$SLOT_NAME');"
        sudo -u barman barman receive-wal --stop $BARMAN_NAME 2>/dev/null
        sudo -u barman barman switch-wal --force --archive --archive-timeout=90 $BARMAN_NAME
        sudo /usr/local/bin/repmgr_iptables.sh $IP $BACKUP $PG_PORT $MASTER_PORT "app"
fi

if [[ "$EVENT" =~ ^(standby_register|standby_follow|repmgrd_failover_follow|repmgrd_standby_reconnect)$ ]]; then
        psql -h $IP -p $PG_PORT -U repmgr -d repmgr -c "select pg_drop_replication_slot('$SLOT_NAME') from pg_replication_slots where slot_name = '$SLOT_NAME';"
fi

Посмторим на два интересных момента в скрипте.
#1. Следуя классическому сценарию бэкапирования barman, вам нужно два слота реплицации — один на мастере, другой на резервном сервере. В нашем же случае, мы должны удалить слот с каждого экс-мастера и создать слот на новом мастере после переключения серверов. Если мы оставим слот репликации на экс-мастере, то он будет разростаться пока не забьет всю память сервера БД, потому что в нем будут храниться WAL ожидающие бэкапирования.
#2. Прежде чем открыть порт для соединений от сервера приложений используя скрипт repmgr_iptables.sh, мы принудительно переключаем WAL на новый и архивируем его. Так мы сбрасываем состояние barman и убеждаемся, что ни один WAL не пропущен пока barman не сбросится сам по себе.

Добавьте права на выполнение всем файлам.

chmod +x /usr/local/bin/repmgr_iptables_chains.sh
chmod +x /usr/local/bin/repmgr_iptables.sh
chmod +x /usr/local/bin/repmgr_handler.sh

Разрешите демонам запускать barman и скрипт-помощник без пароля.
/etc/sudoers.d/pg_sudoers

postgres ALL = (barman) NOPASSWD: /usr/bin/barman
postgres ALL = NOPASSWD: /usr/local/bin/repmgr_iptables.sh

Добавьте следующую строку в конфиги repmgr.
/etc/repmgrd_db1.conf
/etc/repmgrd_db2.conf

event_notification_command='/usr/local/bin/repmgr_handler.sh %n %e'

Перезапустите демоны.

systemctl restart repmgrd_db1
systemctl restart repmgrd_db2

Откройте порты для barman и сервера приложений вручную.

/usr/local/bin/repmgr_iptables_chains.sh
/usr/local/bin/repmgr_iptables.sh 10.8.1.1 10.8.3.1 5432 54321
/usr/local/bin/repmgr_iptables.sh 10.8.1.1 10.8.3.1 5432 54322 "app"

Добавьте id мастера в файл.

echo 1 > /usr/local/bin/repmgr_master
chown postgres:postgres /usr/local/bin/repmgr_master

Проверьте подключение к обоим портам.

su - postgres
psql -U repmgr -d repmgr -h 10.8.3.1 -p 54321
psql -U repmgr -d repmgr -h 10.8.3.1 -p 54322

Barman

Теперь barman.
Создайте пользователей БД barman на сервере db1.

su - postgres
createuser -s barman
psql -c "alter user barman with password 'ast84kd';"
createuser --replication streaming_barman
psql -c "alter user streaming_barman with password 'nxs62jk';"

Создайте файл pgpass для пользователя ОС barman на сервере backup.

su - barman
echo "*:*:*:barman:ast84kd" > ~/.pgpass
echo "*:*:*:streaming_barman:nxs62jk" >> ~/.pgpass
chmod 600 ~/.pgpass

Добавьте следующие строки в начало файла pg_hba.conf на серверах db1 и db2
/etc/postgresql/11/main/pg_hba.conf

local replication streaming_barman md5
host replication streaming_barman 127.0.0.1/32 md5
host replication streaming_barman 10.8.3.1/32 md5

local postgres barman md5
host postgres barman 127.0.0.1/32 md5
host postgres barman 10.8.3.1/32 md5

Сделаем процесс репликации синхронным.
Добавьте следующие строки в файл postgresql.conf на сервере db1.
/etc/postgresql/11/main/postgresql.conf

synchronous_commit = on
synchronous_standby_names = 'FIRST 1 (db2, barman_receive_wal)'

И следующие строки в файл postgresql.conf на сервере db2.
/etc/postgresql/11/main/postgresql.conf

synchronous_commit = on
synchronous_standby_names = 'FIRST 1 (db1, barman_receive_wal)'

Применяя параметр «FIRST 1» мы указываем серверу на то, что процесс репликации должен быть синхронным только с одним сервером. Сервер БД имеет приоритет №1.
Если один сервер упадет, кластер продолжит работу синхронно с сервером бэкапов.

Перезапустите PostgreSQL на серверах db1 и db2 чтобы применить изменения.

systemctl restart postgresql

Отредактируйте глобальный конфиг barman на сервере backup.
/etc/barman.conf

[barman]
barman_user = barman
configuration_files_directory = /etc/barman.d
barman_home = /var/lib/barman
log_file = /var/log/barman/barman.log
log_level = INFO
compression = pygzip ; сжимать WAL, сжатие основного бэкапа не реализовано в barman
backup_options = concurrent_backup ; использовать новеший(9.6+) конкурентный бэкап
recovery_options = 'get-wal' ; восстанавливать сервер, используя WAL с сервера barman, запрашивая их утилитами get-wal или barman-wal-restore, а не копируя их классически сразу в директорию pg_wal
minimum_redundancy = 2 ; всегда хранить как минимум 2 полных бэкапа
retention_policy = RECOVERY WINDOW OF 2 WEEKS ; хранить бэкапы как минимум две недели

Создайте конфигурацию бэкапирования нашего кластера.
/etc/barman.d/main_backup.conf

[main_backup]
description =  "Main backup"

conninfo = host=10.8.3.1 port=54321 user=barman dbname=postgres
backup_method = postgres

streaming_conninfo = host=10.8.3.1 port=54321 user=streaming_barman
streaming_archiver = on
slot_name = barman
;streaming_archiver_name = barman_receive_wal

Проверьте подключение к пользователям БД barman.

su - barman
psql -c 'SELECT version()' -U barman -h 10.8.1.1 postgres
psql -U streaming_barman -h 10.8.1.1 -c "IDENTIFY_SYSTEM" replication=1

Создайте две задачи в cron.

crontab -e
* * * * * /usr/bin/barman cron
0 2 * * 1 /usr/bin/barman backup main_backup

barman cron — выполняется каждую минуту, удаляет старые бэкапы, запускает архивацию WAL, и т.д.
barman backup main_backup — выполняется каждый понедельник в 2 часа ночи, выполняет базовый бэкап.

Чтобы выполнять потоковое бэкапирование, barman использует слот реплицирования. Создайте один, если он еще не создан. Затем, запустите barman cron вручную чтобы начать архивирование WAL. Принудительно переключите WAL. Проверьте, что barman работает как нужно. Создайте 2 первых полных бэкапа согласно параметру minimum_redundancy.

barman receive-wal --create-slot main_backup
barman cron
barman switch-wal --force --archive main_backup
barman check main_backup
barman backup main_backup
barman backup main_backup

(опционально) Можно настроить repmgr так, чтобы он использовал barman для клонирования и восстановления. Это поможет нам восстанавливать БД не обращаясь к мастеру. Добавьте следующие строки в файл repmgr.conf на серверах db1 и db2.
/etc/repmgr.conf

barman_host=barman@10.8.3.1
barman_server=main_backup
restore_command=/usr/bin/barman-wal-restore -U barman 10.8.3.1 main_backup %f %p

На сервере db1 запустите:

su - postgres
ssh-keygen -b 2048 -t rsa -N "" -C "postgres@10.8.1.1"
ssh-copy-id barman@10.8.3.1

На сервере db2 запустите:

su - postgres
ssh-keygen -b 2048 -t rsa -N "" -C "postgres@10.8.2.1"
ssh-copy-id barman@10.8.3.1

Теперь мы готовы к тестированию кластера.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *