Многоярусный бэкап PostgreSQL с помощью Barman и синхронного переноса журналов транзакций

в 9:33, , рубрики: barman, postgresql, Администрирование баз данных, Блог компании Яндекс.Деньги, бэкап, резервное копирование, системное администрирование, метки:

Многоярусный бэкап PostgreSQL с помощью Barman и синхронного переноса журналов транзакций - 1

В Яндекс.Деньгах хранится масса важной для комфортной работы пользователя информации. Настройки профилей и подписки на штрафы тоже нужно бэкапить, чем и занимается у нас связка из Barman Backup & Recovery for PostgreSQL и pg_receivexlog.

В статье расскажу о том, почему архитектура стала такой, какой стала, а также расскажу, как реализовать подобный сценарий для вашей базы PostgreSQL.

Как было, и почему это не круто

До описываемых в статье изменений бэкап платежной системы снимался с мастер-базы в одном из наших дата-центров (ДЦ). Вообще, платежная система состоит из множества отдельных сервисов, баз и приложений. Все это добро резервируется сообразно важности данных. Так как статья более практическая, рассмотрю в качестве примера одну из множества СУБД без повышенных требований к безопасности – в ней хранятся настройки пользовательских профилей, история переводов, предпочтения по аутентификации и прочее.

В качестве инструмента резервного копирования у нас в эксплуатации любят Barman, гибкий и стабильный продукт. Кроме того, его делают люди, участвующие в разработке нашего любимого PostgreSQL.

Проблема резервного копирования только с активных узлов в том, что если мастер переключали на второй ДЦ (где резервных копий не было), то бэкап-сервер первого ДЦ медленно и долго собирал копии с нового мастера по сети. И потенциальные потери самых последних транзакций при восстановлении стали вызывать все больше опасений по мере роста числа пользователей.

Делаем правильно

Итого, будем решать следующие задачи:

  1. Обеспечение доступности резервных копий при отказе ДЦ или бэкап-сервера.

  2. Недопущение потери транзакций при выходе мастер-сервера из строя.

Масштаб нашей задачи составляли 20 серверов PostgreSQL 9.5 (10 мастеров и 10 слейвов), обслуживающих 36 баз данных суммарным объемом около 3 ТБ. Но описываемый сценарий подойдет и для пары серверов с критичными БД.

С версии 2.1 в Barman Backup & Recovery появилась возможность записывать поток транзакций не только с мастера, но и со слейв-серверов (реплик).

Общая схема нового решения представлена ниже, она довольно незамысловата:
Многоярусный бэкап PostgreSQL с помощью Barman и синхронного переноса журналов транзакций - 2

По одному серверу резервного копирования в каждом ДЦ, конфигурация у них общая (один из плюсов Barman).

Процесс конфигурации выглядит следующим образом:

1. Подключаемся к серверу БД и с помощью пакетного менеджера устанавливаем postgresql-9.5-pgespresso – расширение для интеграции Barman Backup & Recovery c PostgreSQL. Все команды приводятся для Ubuntu, для других дистрибутивов могут быть отличия:

apt-get install postgresql-9.5-pgespresso

2. Устанавливаем расширение pg_espresso в PostgreSQL:

postgres@db1:5432 (postgres) # CREATE EXTENSION pgespresso

3. Теперь на сервере с Barman нужно создать файл, описывающий настройки резервного копирования для сервера db1 – db1.conf в каталоге /etc/barman.d/ со следующим содержимым:

[db1]
ssh_command = ssh postgres@db1
conninfo = host=pgdb1 user=postgres port=5432
backup_options = concurrent_backup ; позволяет выполнять бэкап с реплики при наличии расширения pg_espresso

archiver = off ; выключаем доставку логов через archive_command
streaming_archiver = on ; включаем доставку логов через механизм репликации
slot_name = barman ; имя слота репликации

4. Далее нужно создать слот репликации, чтобы гарантировать, что мастер не удалит журналы транзакций, которые еще не были получены репликой:

$ barman receive-wal --create-slot db1

5. И проверить, что Barman видит сервер и забирает с него журналы транзакций:

$ barman check db1

Если все команды выполнены правильно, то в результате выполнения команды проверки должно получиться нечто похожее:

Server db1:
        PostgreSQL: OK
        superuser: OK
        PostgreSQL streaming: OK
        wal_level: OK
        replication slot: OK
        directories: OK
        retention policy settings: OK
        backup maximum age: OK (no last_backup_maximum_age provided)
        compression settings: OK
        failed backups: OK (there are 0 failed backups)
        minimum redundancy requirements: OK (have 7 backups, expected at least 7)
        ssh: OK (PostgreSQL server)
        pgespresso extension: OK
        pg_receivexlog: OK
        pg_receivexlog compatible: OK
        receive-wal running: OK
        archiver errors: OK

Теперь Barman будет забирать журналы и делать бэкапы с реплик. В бэкап попадают все базы PostgreSQL c профилями пользователей, историей платежей, подписками на штрафы, настройками аутентификации и т.п.

Отряд не заметил потери бойца

Напомню, что нулевое RPO при восстановлении нам очень важно, поэтому решили подстраховать Barman дополнительным механизмом – pg_receivexlog. Этот процесс находится на отдельном сервере – не на сервере с базой данных – и непрерывно переносит копии журналов транзакций из мастер-ноды в отдельное хранилище журналов транзакций, доступное для реплики. И так для каждого ДЦ отдельно.

Особенность механизма в том, что СУБД не подтвердит приложению запись данных в базу, пока не получит подтверждение от pg_receivexlog об успешном копировании транзакции.

Без этого сохранялась бы вероятность следующего сценария:

  1. Допустим, Иннокентий платит 100 рублей за сотовую связь, операция успешно проводится нашей платежной системой (ПС).

  2. Сразу после проведения в бэкенде происходит авария, и мы откатываем бэкап, включая журналы транзакций.

  3. Но так как перевод произошел прямо перед аварией, эта транзакция не попала в резервные копии и больше не отразится в истории операций по кошельку Иннокентия.

Получается, что ПС знает о проведении платежа, деньги не потеряны, но саму операцию в истории не посмотреть. Конечно, это неудобно и обескураживает. Операцию пришлось бы в ручном режиме вносить через службу поддержки.

Теперь как настроить чудо механизм синхронного переноса логов:

1. Создаем слот для сборщика логов:

pg_receivexlog --create-slot -S pgreceiver --if-not-exists -h db1

2. Запускаем службы сбора логов с сервера PostgreSQL:

pg_receivexlog -S pgreceiver -d "host=db1 user=postgres application_name=logreceiver" -D /var/lib/postgresql/log/db1/ --verbose --synchronous

*-S: используемый слот репликации. Должен быть создан на первом шаге;*
*-d: строка подключения к базе;*
*-D: каталог, куда будут сохраняться журналы;*
*—synchronous: записывать данные в синхронном режиме;*

3. После запуска pg_receivexlog будет пытаться в синхронном режиме сохранять журналы транзакций в каталог. Таких сборщиков может быть несколько, по одному на сервер PostreSQL. Но чтобы синхронный режим заработал, нужно в настройках СУБД на мастер-ноде указать параметр в конфигурации PostgreSQL:

synchronous_standby_names = 'logreceiver, standby'

4. Но чтобы при отказе сервера со сборщиком логов мастер-нода могла продолжать обработку пользовательских транзакций, вторым сервером лучше указать реплику. На реплике в recovery.conf нужно указать, откуда брать журналы транзакций в случае недоступности мастер-ноды:

restore_command = 'scp postgres@logreceiver:/var/lib/postgresql/log/db1/%f* %p'

Конечно, при выходе из строя сервера сбора логов скорость работы всей системы немного снизится, потому что вместо синхронной отправки журналов на сборщик логов в том же ДЦ, что и мастер, придется отправлять их с подтверждением в другой ДЦ. Тем не менее стабильность важнее.

В качестве резюме приведу немного конкретики о времени и объеме бэкапов по описанной в статье схеме. Полный бэкап упомянутых серверов снимается в Яндекс.Деньгах ежедневно и весит около 2 ТБ, на обработку которых бэкап-серверу нужно 5-10 часов. Кроме того, выполняется постоянный бэкап журналов транзакций с помощью перемещения их файлов в хранилище.

Автор: Яндекс.Деньги

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js