Завершающая часть из цикла "Знакомство с pg_probackup" (первая | вторая части).

В предыдущей статье мы решили сразу две задачи: в первой создали архив wal-файлов, перешли к PAGE-архивам, настроили политику удержания wal-файлов; во второй — реплику из бэкапа и настроили pg_probackup на снятие бэкапов с неё.

Сегодня мы с вами продолжим тему восстановления из бэкапов.

Задача 4


Неожиданно вы понимаете, что обладать бэкапами — это ещё не всё: нужно как-то уметь пользоваться ими. Выбиваем себе ещё один сервер для тестирования восстановления — srv_tst и начинаем тестирование (один раз мы с вами уже восстанавливались из бэкапа, когда создавали реплику на srv_db2, что само по себе неплохо, но этого явно недостаточно в общем случае).

Дано: srv_bkp содержит бэкапы, srv_tst — сервер, на который мы будем восстанавливаться. Но в предыдущих задачах мы с вами говорили о кластере как о чем-то целом. Давайте теперь посмотрим, что же у нас есть внутри кластера. Там у нас есть две не служебные базы данных. База данных demo находится в табличном пространстве (ТП) default, а база данных medium — в ТП ts_medium (обе демонстрационные базы можно скачать отсюда). Исходя из этих данных и будем решать поставленные задачи.

Решение:
Настраиваем srv_tst так же, как и srv_db2 (устанавливаем PostgreSQL, pg_probackup, обмениваемся ключами между srv_bkp и srv_tst — подробно эти действия были рассмотрены в решении предыдущих задачах, не будем на них останавливаться).

pg_probackup предоставляет уйму вариантов восстановления — это и восстановление на определённую точку во времени, заданную различными параметрами, и на определённый бэкап, и даже частичное восстановление. Давайте разберём эти варианты (напомню, что один из важных вариантов — создание реплики из бэкапа, мы рассматривали в предыдущей задаче).

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

  • на определённый бэкап
  • на точку во времени
  • частичное восстановление
  • инкрементное восстановление

Казалось бы — задачи поставлены, их нужно выполнять, но сначала давайте чуть-чуть поможем будущим себе. После каждого восстановления нужно будет удалять созданный кластер. Существует ненулевая вероятность того, что вы перепутаете терминалы, и вместо того, чтобы удалить базу на тестовом сервере — удалите её на реплике или, ещё того хуже, на боевом сервере. Обезопасим себя от этого. На время тестов давайте забудем про то, что кластера можно удалять руками через команду rm: напишем скрипт (clear.sh), который будет удалять старые данные. Поместим скрипт на srv_tst, и тут становится всё просто — даже если вы перепутаете терминалы, то на srv_db1 и srv_db2 нет такого скрипта, а раз нет, то и запускать нечего. Соответственно, нужные данные будут в безопасности!

Запускать под пользователем, который имеет право на остановку кластера

#! /bin/sh
echo -n "Подтвердите удаление кластера. (y/n)"
read item
case "$item" in
y|Y)
  systemctl stop postgresql-12.service
  rm -rf /var/lib/pgsql/12/data*
  rm -rf /var/lib/pgsql/ts_medium_new/*
  ;;
n|N)
  echo "Операция очистки отменена"
  ;;
*) echo "Ничего не выбрано, очистка не произойдёт"
;;
esac

1) Восстановление на определенный бэкап

Начнём с самого простого — у вас есть набор бэкапов, и вам необходимо просто восстановиться на один из них. Но просто так восстанавливаться не интересно. Давайте ради интереса изменим путь, по которому восстановится наше табличное пространство ts_medium — пусть оно восстановится не туда, где лежало раньше, а по новому пути.

Переходим на srv_bkp, смотрим доступные бэкапы через команду

pg_probackup show

Запоминаем идентификатор нужного нам бэкапа и вводим команду:

time pg_probackup restore --instance=db1 -D /var/lib/pgsql/12/data -i QFEZBK -j 2 --recovery-target='immediate' -T /var/lib/pgsql/ts_medium=/var/lib/pgsql/ts_medium_new --progress --remote-proto=ssh --remote-host=srv_tst  --archive-host=srv_bkp --archive-user=backup_user --log-level-console=log --log-level-file=verbose --log-filename=restore_imm.log

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

Убедимся в том, что восстановление прошло успешно, посмотрим, куда восстановилось наше табличное пространство, используя команду \db, и если всё в порядке, то выполним

select pg_wal_replay_resume();

Таким образом мы выйдем из режима восстановления.

Команда восстановления получилась достаточно громоздкой, давайте уменьшим её:

srv_bkp: pg_probackup set-config --instance db1 --archive-host=srv_bkp --archive-user=backup_user

Теперь эти параметры можно не упоминать.

Перед каждым из тестов будем запускать ./clear.sh, очищающий старые данные для новых тестов.

2) Восстановление на точку времени (Point-in-time recovery).

Если посмотреть на список вариантов выбора точки восстановления в документации, то глаза разбегаются от их обилия: recovery-target-time, recovery-target-xid, recovery-target-name… Помимо этого есть параметр recovery-target-inclusive, который определяет включать ли эту точку в восстановление или останавливать восстановление непосредственно перед ней.

Давайте остановимся на самом понятном варианте — восстановление на определённое время. Для начала посмотрим через pg_probackup show, какой временной диапазон нам доступен. Мы помним, что настроили наш комплекс таким образом, что PITR возможен на любой момент времени в течение последних 3х бэкапов.

Для определённости восстановимся на 12:00:03 предыдущего дня.

time pg_probackup restore --instance=db1 -D /var/lib/pgsql/12/data -j 2 --recovery-target-time='2020-08-23 12:00:03-04' -T /var/lib/pgsql/ts_medium=/var/lib/pgsql/ts_medium_new --progress --remote-proto=ssh --remote-host=srv_tst --log-level-console=log --log-level-file=verbose --log-filename=restore_time.log

Обратимся к таблице timing, созданной во время решения второй задачи. Если мы сделаем выборку из неё, то увидим, на нужное ли время произошло восстановление.

После этого теста мы можем удалить из crontab на сервере srv_db1 команду, вставляющую данные в таблицу timing, а также саму таблицу.

Не забываем выполнять очистку на сервере srv_tst путём запуска clear.sh!

3) Частичное восстановление

Очень интересным вариантом является частичное восстановление — если в вашем кластере несколько баз данных, но какие-то из них не нужно восстанавливать, то вы можете сделать так, чтобы данные из этих баз не восстанавливались (сами базы данных тем не менее восстановятся, но данных в них не будет, и сразу же после такого восстановления такие пустые базы нужно будет удалить).

Чтобы иметь такую возможность, необходимо перейти на srv_db1 и выдать ещё одно разрешение пользователю, под которым осуществляется восстановление:

GRANT SELECT ON TABLE pg_catalog.pg_database TO backup;

Используя ключ --db-include=database_name, можно указать, какую именно базу данных мы хотим восстановить, есть и антоним такого ключа --db-exclude=database_name, указывает какие именно базы данных мы не хотим восстанавливать.

Как мы помним, в нашем кластере четыре базы данных — demo, medium, postgres и backupdb. Давайте восстановим всё, кроме medium (шаблоны template0 и template1 восстанавливаются в любом случае).

time pg_probackup restore --instance=db1 -D /var/lib/pgsql/12/data -i QFEZBK -j 2 --recovery-target='immediate' -T /var/lib/pgsql/ts_medium=/var/lib/pgsql/ts_medium_new --progress  --db-exclude=medium --remote-proto=ssh --remote-host=srv_tst --log-level-console=log --log-level-file=verbose --log-filename=restore_partial.log

После восстановления необходимо выйти из режима восстановления:

select pg_wal_replay_resume();

и удалить базу данных medium:

drop database medium;

4) Инкрементное восстановление

Последний пример, который мы рассмотрим — инкрементное восстановление.
Давайте представим ситуацию — у вас есть мастер и реплика. Произошло нечто и ваша реплика безнадёжно отстала.

Создадим такую ситуацию:

Переходим на srv_db2 и останавливаем кластер;
Переходим на srv_db1 и уменьшим количество сохраняемых wal-файлов, а также изменяем команду копирования wal-сегментов в архив.

psql -c "alter system set max_wal_size='48MB'"
psql -c "alter system set archive_command = '/bin/true'"
psql -c 'select pg_reload_conf()'
psql -c 'checkpoint'

Создаём таблицу и вставляем в неё значительное количество записей

psql -c 'create table t1(i integer)'
psql -c 'insert into t1 select i from generate_series(1,5000000) as i'

Если сейчас мы запустим реплику, то увидим в логах сообщение об ошибке, в которой говорится об отсутствии необходимого сегмента WAL:

2020-08-22 01:37:06.268 EDT [12170] FATAL: could not receive data from WAL stream: ERROR: requested WAL segment 00000001000000020000001A has already been removed
Итак, мы пришли к ситуации, когда мастер убежал вперёд, а реплике не хватает WAL файлов, чтобы догнать его.

Останавливаем кластер на реплике

systemctl stop postgresql-12.service

Необходимо снять бэкап с мастера в режиме DELTA, но сейчас у нас настроен сбор данных с srv_db2, изменим это:

srv_bkp: pg_probackup set-config --instance db1 --remote-host=srv_db1

Cоздаём бэкап в режиме DELTA, для этого воспользуемся следующей командой:

pg_probackup backup --instance=db1 -j 2 --progress -b DELTA --stream  --compress

После того, как создание бэкапа завершится вызовем команду:

time pg_probackup restore --instance=db1 -D /var/lib/pgsql/12/data -j 2 --restore-as-replica --remote-proto=ssh --remote-host=srv_db2 -I CHECKSUM

включаем сервис postgresql на реплике:

systemctl start postgresql-12.service

Проверяем — создадим таблицу на мастере:

create table t2();

после чего подключимся к реплике (srv_db2) и проверим — есть ли она

\dt 

таблица появилась, значит реплика работает!

Вернём обратно значение archive_command на srv_db1. Напомню, мы изменили его только для того, чтобы создать разрыв между мастером и репликой. После чего перечитаем конфигурацию:

psql -c "alter system set archive_command = 'pg_probackup-12 archive-push -B /home/backup_user/backup_db --instance=db1 --wal-file-path=%p --wal-file-name=%f --remote-host=srv_bkp --remote-user=backup_user --compress'"
psql -c 'select pg_reload_conf()'

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

Заключение


В этом цикле статей я постарался охватить значимый, но далеко не полный объём операций, который предлагает утилита pg_probackup. За кадром остались объединение и валидация бэкапов, использование PTRACK, возможность включения в резервную копию сторонних директорий, не связанных с кластером PostgreSQL, и очень много других интересных вещей.

Благодарю компанию Postgres Professional за этот замечательный инструмент и ведущего инженера техподдержки Григория Смолкина, который выступал в качестве технического консультанта при написании этих статей.