Привет, я Александр Никитин, главный системный администратор компании «БАРС Груп». В этой статье я хочу познакомить вас с инструментом pg_probackup.
Pg_probackup — разработка компании Postgres Professional, которая помогает делать резервные копии СУБД PostgreSQL. В отличие от стандартной утилиты pg_basebackup этот инструмент позволяет создавать инкрементные резервные копии на уровне блоков данных (по умолчанию 8Kb), производить валидацию резервных копий и СУБД, задавать политики хранения и многое другое.
В этой статье я не ставлю перед собой цели описать все возможные аспекты работы с pg_probackup, я лишь хочу дать понимание того, как вы можете использовать этот инструмент в своей работе.
Будут рассмотрены следующие варианты использования:
- создание автономных бэкапов на отдельном сервере
- создание архива wal-файлов и создание бэкапов в этом режиме
- развёртывание реплики из бэкапа и настройка создания бэкапов с реплики
- различные варианты восстановления
Задача 1
Дано: У нас есть два сервера (OS CentOS 7), на первом у нас располагается наша база данных (имя хоста srv_db1, пользователь postgres, установлен PostgreSQL 12.4), а на втором мы будем хранить бэкапы (имя хоста srv_bkp, пользователь backup_user).
Необходимо настроить резервное копирование таким образом, чтобы копии кластера PostgreSQL хранились на сервере srv_bkp.
Решение:
pg_probackup умеет работать через ssh, запуская на удаленном хосте агентов, которые используют ssh соединение как канал связи и транспорта. Соответственно, нужно сделать так, чтобы пользователь backup_user на хосте srv_bkp мог подключаться к пользователю postgres на хосте srv_db1.
1) Подключаемся к srv_bkp пользователем backup_user и выполняем следующие команды:
ssh-keygen -t rsa
ssh-copy-id postgres@srv_db1
Включим режим параноика и отредактируем файл ~/.ssh/authorized_keys на сервере srv_db1
Перед строчкой с ключами вставим следующее:
command="pg_probackup-12 agent"
Таким образом, должно получиться что-то вроде этого:
command="pg_probackup-12 agent" ssh-rsa AAAA....
Этим мы говорим, что ничего кроме «pg_probackup-12 agent» запускать через SSH нельзя (подробнее об этом можно прочитать тут).
2) Производим установку pg_probackup на обе машины (в примере мы работаем на CentOS, но для остальных дистрибутивов процесс установки описан в документации):
yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
rpm -ivh https://repo.postgrespro.ru/pg_probackup/keys/pg_probackup-repo-centos.noarch.rpm
yum install pg_probackup-12
yum install pg_probackup-12-debuginfo
Установится последняя доступная версия pg_probackup, на момент написания статьи — 2.4.2.
3) на srv_bkp создаём алиас (это не обязательно, но так удобнее) и определяем переменную, содержащую путь до директории с резервными копиями (предварительно создав эту директорию):
mkdir /home/backup_user/backup_db
echo "BACKUP_PATH=/home/backup_user/backup_db">>~/.bash_profile
echo "export BACKUP_PATH">>~/.bash_profile
echo "alias pg_probackup='pg_probackup-12'">>~/.bash_profile
подгружаем профиль
. .bash_profile
4) на srv_bkp инициализируем каталог резервных копий и добавляем новый экземпляр:
pg_probackup init
Добавляем экземпляр PostgreSQL в каталог, даём ему имя db1, указываем параметры подключения по ssh — хост, имя пользователя, и путь до PGDATA.
pg_probackup add-instance --instance=db1 --remote-host=srv_db1 --remote-user=postgres --pgdata=/var/lib/pgsql/12/data
Если появилась строка
INFO: Instance 'db1' successfully inited
Значит инициализация прошла успешно.
Определим политику удержания резервных копий:
pg_probackup set-config --instance db1 --retention-window=7 --retention-redundancy=2
Получившая политика сводится к следующему:
необходимо удерживать все резервные копии младше 7 дней
при этом кол-во полных резервный копий должно быть не меньше двух
5) Для создания резервных копий необходимо подключение к СУБД, поэтому создаём в кластере PostgreSQL базу, к которой будет происходить подключение для управления процессом резервного копирования (с точки зрения безопасности это лучше, чем подключаться к продуктовой БД), а также изменяем параметры базы данных:
$ createdb backupdb
присоединяемся к новой базе данных
psql -d backupdb
создаём роль БД, от имени которой будет осуществляться управление процессом резервного копирования:
BEGIN;
CREATE ROLE backup WITH LOGIN REPLICATION password 'Strong_PWD';
GRANT USAGE ON SCHEMA pg_catalog TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.current_setting(text) TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_is_in_recovery() TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_start_backup(text, boolean, boolean) TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_stop_backup(boolean, boolean) TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_create_restore_point(text) TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_switch_wal() TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_last_wal_replay_lsn() TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.txid_current() TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.txid_current_snapshot() TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.txid_snapshot_xmax(txid_snapshot) TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_control_checkpoint() TO backup;
COMMIT;
Обратим внимание на параметр listen_addresses:
show listen_addresses;
если localhost, то меняем на *
alter system set listen_addresses='*';
Если вы изменили listen_addresses то необходимо рестартовать PostgreSQL.
Посмотрим, где у нас расположен файл pg_hba.conf
psql –c 'show hba_file'
Добавим следующие правила в pg_hba.conf:
# pg_probackup access permission
host backupdb backup srv_bkp md5
host replication backup srv_bkp md5
Перечитываем конфигурацию:
psql -c 'select pg_reload_conf()'
Проверяем — не было ли опечаток:
psql -c 'select * from pg_hba_file_rules'
На srv_bkp в домашней директории пользователя backup_user cоздаём файл, в котором прописываем имя сервера или его ip-адрес, порт, имя базы, имя пользователя и его пароль. Этот файл нужен для того, чтобы при создании резервной копии пароль вводился автоматически.
echo "srv_db1:5432:replication:backup:Strong_PWD">>~/.pgpass
echo "srv_db1:5432:backupdb:backup:Strong_PWD">>~/.pgpass
Устанавливаем необходимые права на доступ к этому файлу
chmod 600 ~/.pgpass
И создаём первую резервную копию:
pg_probackup backup --instance=db1 -j2 --backup-mode=FULL --compress --stream --delete-expired --pguser=backup --pgdatabase=backupdb --remote-host=srv_db1 --remote-user=postgres
Если мы всё сделали правильно, то на экране появится что-то вроде этого:
INFO: Backup start, pg_probackup version: 2.4.2, instance: db1, backup ID: QFERC9, backup mode: FULL, wal mode: STREAM, remote: true, compress-algorithm: zlib, compress-level: 1
WARNING: This PostgreSQL instance was initialized without data block checksums. pg_probackup have no way to detect data block corruption without them. Reinitialize PGDATA with option '--data-checksums'.
INFO: PGDATA size: 3373MB
INFO: Start transferring data files
INFO: Data files are transferred, time elapsed: 1m:0s
INFO: wait for pg_stop_backup()
INFO: pg_stop backup() successfully executed
INFO: Syncing backup files to disk
INFO: Backup files are synced, time elapsed: 1s
INFO: Validating backup QFERC9
INFO: Backup QFERC9 data files are valid
INFO: Backup QFERC9 resident size: 975MB
INFO: Backup QFERC9 completed
INFO: Evaluate backups by retention
INFO: Backup QFERC9, mode: FULL, status: OK. Redundancy: 1/2, Time Window: 0d/7d. Active
INFO: There are no backups to merge by retention policy
INFO: There are no backups to delete by retention policy
INFO: There is no WAL to purge by retention policy
Обратите внимание на предупреждение, которое выдал pg_probackup — на нашем кластере не включена проверка контрольных сумм, таким образом, он не может проверить блоки данных на корректность. Другими словами вы не защищены от того, что сбойные блоки данных не попадут в бэкап.
Ознакомимся с текущими настройками:
pg_probackup show-config --instance db1
Теперь давайте сократим команду создания бэкапов — пропишем часть параметров в конфигурацию экземпляра db1:
pg_probackup set-config --instance=db1 --remote-host=srv_db1 --remote-user=postgres --pguser=backup --pgdatabase=backupdb --log-filename=backup_cron.log --log-level-file=log --log-directory=/home/backup_user/backup_db/log
Сравним: как было и как стало
pg_probackup show-config --instance db1
Было | Стало |
---|---|
# Backup instance information pgdata = /var/lib/pgsql/12/data system-identifier = 6863327140287059463 xlog-seg-size = 16777216 # Connection parameters pgdatabase = backup_user # Replica parameters replica-timeout = 5min # Archive parameters archive-timeout = 5min # Logging parameters log-level-console = INFO log-level-file = OFF log-filename = pg_probackup.log log-rotation-size = 0TB log-rotation-age = 0d # Retention parameters retention-redundancy = 2 retention-window = 7 wal-depth = 0 # Compression parameters compress-algorithm = none compress-level = 1 # Remote access parameters remote-proto = ssh |
# Backup instance information pgdata = /var/lib/pgsql/12/data system-identifier = 6863327140287059463 xlog-seg-size = 16777216 # Connection parameters pgdatabase = backupdb pghost = srv_db1 pguser = backup # Replica parameters replica-timeout = 5min # Archive parameters archive-timeout = 5min # Logging parameters log-level-console = INFO log-level-file = LOG log-filename = backup_cron.log log-directory = /home/backup_user/backup_db/log log-rotation-size = 0TB log-rotation-age = 0d # Retention parameters retention-redundancy = 2 retention-window = 7 wal-depth = 0 # Compression parameters compress-algorithm = none compress-level = 1 # Remote access parameters remote-proto = ssh remote-host = srv_db1 remote-user = postgres |
Сделаем инкрементальную копию в DELTA режиме, не указывая установленные в конфиге параметры:
pg_probackup backup --instance=db1 -j2 --progress -b DELTA --compress --stream --delete-expired
Посмотрим, что у нас получилось
BACKUP INSTANCE 'db1'
=====================================================================================================================================
Instance Version ID Recovery Time Mode WAL Mode TLI Time Data WAL Zratio Start LSN Stop LSN Status
=====================================================================================================================================
db1 12 QFERKZ 2020-08-21 05:55:52-04 DELTA STREAM 1/1 9s 136kB 32MB 1.00 0/B0000028 0/B0000168 OK
db1 12 QFERC9 2020-08-21 05:51:34-04 FULL STREAM 1/0 1m:3s 959MB 16MB 3.52 0/AE000028 0/AE0001A0 OK
Информации довольно много, подробное описание можно встретить в документации, тем не менее давайте остановимся на некоторых пунктах:
Recovery Time — минимальное время, на которое можно восстановиться, используя эту резервную копию
Mode — режим, в котором была сделана копия, на данный момент доступно 4 режима — один полный (FULL) и три инкрементальных (PAGE, DELTA и PTRACK)
WAL Mode — тут возможны следующие варианты — STREAM и ARCHIVE. Копии, созданные в режиме STREAM содержат внутри себя все необходимые для восстановления до консистентного состояния WAL файлы. Как раз для работы этого режима и нужно было дать роль REPLICATION нашему пользователю backup. Режим ARCHIVE подразумевает, что мы настроили архивирование WAL, и тогда необходимые WAL файлы будут лежать по известному pg_probackup пути.
Status — статусов достаточно много, все они описаны в документации, но если мы видим что-то отличное от OK, то есть смысл сходить в документацию и посмотреть что же пошло не так.
Как вы уже догадались, мы можем распарсить вывод команды pg_probackup show и достать статус последнего сделанного бэкапа, обработать его и послать в систему мониторинга, а там уже настроить правила, по которым будет срабатывать оповещение ответственных за СУБД сотрудников.
Создадим скрипт bkp_base.sh, который будет запускать резервное копирование и отправлять результат в систему мониторинга:
#! /bin/sh
# подгружаем профиль с настройками
. /home/backup_user/.bash_profile
# вызываем команду создания копии, в качестве параметра передаем метод создания бэкапа (FULL, DELTA и т.д.)
pg_probackup backup --instance=db1 -j 2 --progress -b $1 --compress --stream --delete-expired
# Получаем статус последней копии и отправляем его в zabbix.
if [ "$(pg_probackup show --instance=db1 --format=json | jq -c '.[].backups[0].status')" == '"OK"' ]; then
result=0;
else
result=1;
fi
# в zabbix уже должен быть создан элемент данных типа zabbix_trapper и ключом pg.db_backup
# Логику обработки полученного значения я предлагаю вам настроить самостоятельно, например может учитываться не только значение, которое пришло, но и время с момента последнего обновления этого элемента.
/opt/zabbix/bin/zabbix_sender -c /opt/zabbix/etc/pg.zabbix_agentd.conf -k pg.db_backup -o $result
Записываем в crontab вызов полученного скрипта, например, можно задать такое расписание:
00 01 * * 7 /home/backup_user/scr/bkp_base.sh FULL
00 01 * * 1,2,3,4,5,6 /home/backup_user/scr/bkp_base.sh DELTA
На этом решение первой задачи окончено — мы настроили резервное копирование, определили политику удержания копий. Также мы отправляем в систему мониторинга состояние резервных копий.
В следующей части мы рассмотрим создание архива wal-файлов и создание архивных резервных копий.
evn
Самое важное забыли. Разворачивание резервных копий при условии тотального уничтожения сервера постгрес
a_nikitin Автор
Ну почему забыл, «это только присказка, сказка впереди». Это первая часть, вся последняя часть будет про то как восстанавливаться.
passing1by
проблема в том, что сам бэкап делается быстро и pg_dumpall, а вот restore это отдельная боль, особенно, если база стоит на hdd и у вас медленный(низкой частоты) процессор, и в итоге мы приходим к тому, что при базах свыше 40 тб большая часть способов бэкапа пг бесполезные практически
gsmol
Ну чудес не бывает, Вам как-то нужно принести на машину 40TB данных, какой-то постгресовой специфики в этом вопросе нет.
В зависимости от ситуации и используемого инструмента возможны различные оптимизации, которые позволяют ускорить этот процесс, например, инкрементальное восстановление в уже существующий кластер. Переиспользуя валидные страницы можно здорово сократить объем данных, который надо прокачать по сети и записать на диск, но за это придется заплатить полным перечитыванием уже имеющегося кластера.
passing1by
проблема в том, что даже это не до конца помогает, причём даже на небольших объёмах, например pg_dump иногда игнорирует некоторые записи, и при переносе базы на новый сервер через backup вы имеете отставание на n записей, а настроить репликацию тоже можно не всегда.
Приведу пример, есть бд, размер 30 гб, делается pg_dumpall чтобы переехать на более новую версию пг и на новый сервер, делается pg_dumpall, после рестора оказывается не хватает 5 юзеров и 8 счетов на оплату, хотя они были сделаны ещё 5 часов назад. С просто pg_dump выходит таже история, магическим образом не попадают в дамп 10-16 записей.
Не знаю как с выше описанным инструментом дела обстоят, но причуды бывают везде. А когда говорим ещё о базах где iowait зачастую под 80%, тк индекс существенно больше оперативной памяти, и там идёт сложный запрос с джойнами, то многие способы бэкапа могут вообще поставить бд раком, и проблема в статье автора, что по факту пользы она не несёт никакой, кроме рекламы инструмента, который даже не ясно как себя ведёт на нагруженных бд, а не где 2 кб данных.
Не говоря уже про то, что использование cron в 2020 году уже в принципе так себе затея и лучше использовать systemd-timer, который в отличии от крона имеет нормальные отчёты и нормально мониторится.
gsmol
Так pg_dump — это инструмент для создания логического дампа. Его, конечно, можно использовать для резервного копирования, но это не его основное предназначение.
Ну если эти изменения приехали после старта снятия дампа, то вполне логично, что они не попали в дамп, т.к. дамп берет снапшот. Если же дамп снимался с базы, к которой не были подключены клиенты, то описанные Вами симптомы смахивают на баг.
Эту проблему решают инкрементальные бэкапы в режиме PAGE и PTRACK(про них видимо будет в следующих статьях), т.к. вычитываются только те страницы, которые поменялись со времени предыдущего бэкапа, т.е. подтребление I/O будет минимальным.
passing1by
Клиенты были подключены на чтение, данные, как было написано старые, записям более 5 часов. Более подробно не распишу, т.к. доступа к серверам того клиента которому выполняли работу более нет. Руками перенесли 10 записей. Причём каждый раз не хватало разных записей, если говорим про удалить таблицы и залить заново.
Проблема ещё во времени процессора, и не всегда разрешены или будут использоваться инкрементальные бэкапы, зачастую клиент хочет полный бэкап данных раз в 1-2-4-6-8-12 часов.
gsmol
А как это было реализовано?
Звучит как конкурентное обновление со стороны клиентов.
А зачем? Инкрементальный бэкап даёт все те же гарантии, что и полный.
a_nikitin Автор
Хм, ну не знаю как по поводу «рекламы», поделиться своим опытом это реклама? Про systemd-timer вместо крона — да пожалуйста, используйте. Я же не призываю делать один в один, но вы же поняли, что когда я говорил про крон, то речь шла о планировщике. По поводу нагруженных БД, действительно в следующих частях перейдём к режму PAGE.