В Яндекс.Деньгах хранится масса важной для комфортной работы пользователя информации. Настройки профилей и подписки на штрафы тоже нужно бэкапить, чем и занимается у нас связка из Barman Backup & Recovery for PostgreSQL и pg_receivexlog.
В статье расскажу о том, почему архитектура стала такой, какой стала, а также расскажу, как реализовать подобный сценарий для вашей базы PostgreSQL.
Как было, и почему это не круто
До описываемых в статье изменений бэкап платежной системы снимался с мастер-базы в одном из наших дата-центров (ДЦ). Вообще, платежная система состоит из множества отдельных сервисов, баз и приложений. Все это добро резервируется сообразно важности данных. Так как статья более практическая, рассмотрю в качестве примера одну из множества СУБД без повышенных требований к безопасности – в ней хранятся настройки пользовательских профилей, история переводов, предпочтения по аутентификации и прочее.
В качестве инструмента резервного копирования у нас в эксплуатации любят Barman, гибкий и стабильный продукт. Кроме того, его делают люди, участвующие в разработке нашего любимого PostgreSQL.
Проблема резервного копирования только с активных узлов в том, что если мастер переключали на второй ДЦ (где резервных копий не было), то бэкап-сервер первого ДЦ медленно и долго собирал копии с нового мастера по сети. И потенциальные потери самых последних транзакций при восстановлении стали вызывать все больше опасений по мере роста числа пользователей.
Делаем правильно
Итого, будем решать следующие задачи:
-
Обеспечение доступности резервных копий при отказе ДЦ или бэкап-сервера.
- Недопущение потери транзакций при выходе мастер-сервера из строя.
Масштаб нашей задачи составляли 20 серверов PostgreSQL 9.5 (10 мастеров и 10 слейвов), обслуживающих 36 баз данных суммарным объемом около 3 ТБ. Но описываемый сценарий подойдет и для пары серверов с критичными БД.
С версии 2.1 в Barman Backup & Recovery появилась возможность записывать поток транзакций не только с мастера, но и со слейв-серверов (реплик).
Общая схема нового решения представлена ниже, она довольно незамысловата:
По одному серверу резервного копирования в каждом ДЦ, конфигурация у них общая (один из плюсов 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 об успешном копировании транзакции.
Без этого сохранялась бы вероятность следующего сценария:
-
Допустим, Иннокентий платит 100 рублей за сотовую связь, операция успешно проводится нашей платежной системой (ПС).
-
Сразу после проведения в бэкенде происходит авария, и мы откатываем бэкап, включая журналы транзакций.
- Но так как перевод произошел прямо перед аварией, эта транзакция не попала в резервные копии и больше не отразится в истории операций по кошельку Иннокентия.
Получается, что ПС знает о проведении платежа, деньги не потеряны, но саму операцию в истории не посмотреть. Конечно, это неудобно и обескураживает. Операцию пришлось бы в ручном режиме вносить через службу поддержки.
Теперь как настроить чудо механизм синхронного переноса логов:
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 часов. Кроме того, выполняется постоянный бэкап журналов транзакций с помощью перемещения их файлов в хранилище.
Автор: Яндекс.Деньги