Отдавна е известно, че правенето на резервни копия в SQL дъмпове (използвайки pg_dump или pg_dumpall) – не е най-добрата идея. За архивиране на PostgreSQL бази данни е по-добре да се използва командата pg_basebackup, която прави бинарно копие на WAL журналите. Но когато започнете да изучавате целия процес на създаване на копие и възстановяване, ще разберете, че трябва да напишете поне няколко триколесни велосипеда, за да всичко това работи и да не ви причинява болка както отгоре, така и отдолу. За да облекчим страданията, беше разработен WAL-G.
– това е инструмент, написан на Go за резервно копиране и възстановяване на PostgreSQL бази данни (а от скоро и MySQL/MariaDB, MongoDB и FoundationDB). Той поддържа работа с хранилища като Amazon S3 (и аналози, например Yandex Object Storage), както и Google Cloud Storage, Azure Storage, Swift Object Storage и просто с файловата система. Цялата настройка се състои в прости стъпки, но тъй като статии за него са разпръснати из интернет – няма пълен how-to мануел, който да включва всички стъпки от и до (в Хабр има няколко поста, но много моменти там липсват).

Тази статия е написана на първо място, за да систематизира моите знания. Аз не съм DBA и някъде мога да се изразявам обикновено-разработнически, затова всякакви корекции са добре дошли!
Отделно ще отбележа, че всичко по-долу е актуално и проверено за PostgreSQL 12.3 на Ubuntu 18.04, всички команди трябва да се изпълняват от потребител с привилегии.
Инсталиране
В момента на написването на тази статия стабилната версия на WAL-G е . Тази версия ще използваме (но ако искате сами да я съберете от master-клон, в репозитория на github има всички инструкции за това). За сваляне и инсталиране трябва да изпълните:
#!/bin/bash
curl -L "https://github.com/wal-g/wal-g/releases/download/v0.2.15/wal-g.linux-amd64.tar.gz" -o "wal-g.linux-amd64.tar.gz"
tar -xzf wal-g.linux-amd64.tar.gz
mv wal-g /usr/local/bin/
След това е нужно да конфигурирате първо WAL-G, а след това самия PostgreSQL.
Настройка на WAL-G
За пример за съхранение на резервни копия ще се използва Amazon S3 (защото е по-близо до моите сървъри и използването му е много евтино). За работа с него е нужен 's3-бакет' и ключове за достъп.
Във всички предишни статии относно WAL-G се използваше конфигуриране чрез променливи на средата, но от този релиз настройките могат да бъдат разположени в в домашната директория на потребителя postgres. За неговото създаване ще изпълним следния bash-скрипт:
#!/bin/bash
cat > /var/lib/postgresql/.walg.json << EOF
{
"WALG_S3_PREFIX": "s3://your_bucket/path",
"AWS_ACCESS_KEY_ID": "key_id",
"AWS_SECRET_ACCESS_KEY": "secret_key",
"WALG_COMPRESSION_METHOD": "brotli",
"WALG_DELTA_MAX_STEPS": "5",
"PGDATA": "/var/lib/postgresql/12/main",
"PGHOST": "/var/run/postgresql/.s.PGSQL.5432"
}
EOF
# обязательно меняем владельца файла:
chown postgres: /var/lib/postgresql/.walg.json
Нека обясня малко относно всички параметри:
- WALG_S3_PREFIX – пътят към вашия S3-бакет, където ще се съхраняват резервните копия (може да бъде както в корена, така и в папка);
- AWS_ACCESS_KEY_ID – ключ за достъп в S3 (в случай на възстановяване на тестовия сървър – тези ключове трябва да имат ReadOnly Policy! За повече информация, прочетете раздела за възстановяване);
- AWS_SECRET_ACCESS_KEY – таен ключ в хранилището S3;
- WALG_COMPRESSION_METHOD – метод на компресия, най-добре е да се използва Brotli (тъй като това е златната среда между крайния размер и скоростта на компресия/разкомпресия);
- WALG_DELTA_MAX_STEPS – брой „делти“ до създаването на пълно резервно копие (позволяват да се спести време и размер на качваните данни, но могат да забавят процеса на възстановяване, затова не е желателно да се използват големи стойности);
- PGDATA – път към директорията с данните на вашата база (можете да разберете, като изпълните командата pg_lsclusters);
- PGHOST – свързване с базата данни, при локално резервно копие е по-добре да се прави чрез unix-сокет, както в този пример.
Останалите параметри можете да видите в документацията: .
Настройка на PostgreSQL
За да архиваторът вътре в базата сам изпраща WAL-журналите в облака и се възстановява от тях (в случай на нужда) – трябва да зададете няколко параметъра в конфигурационния файл /etc/postgresql/12/main/postgresql.conf. Само че първо трябва да се уверите, че нито един от долупосочените настройки не е зададен на някакви други стойности, за да не се срине СУБД при повторно зареждане на конфигурацията. Тези параметри могат да се добавят с помощта на:
#!/bin/bash
echo "wal_level=replica" >> /etc/postgresql/12/main/postgresql.conf
echo "archive_mode=on" >> /etc/postgresql/12/main/postgresql.conf
echo "archive_command='/usr/local/bin/wal-g wal-push "%p" >> /var/log/postgresql/archive_command.log 2>&1' " >> /etc/postgresql/12/main/postgresql.conf
echo “archive_timeout=60” >> /etc/postgresql/12/main/postgresql.conf
echo "restore_command='/usr/local/bin/wal-g wal-fetch "%f" "%p" >> /var/log/postgresql/restore_command.log 2>&1' " >> /etc/postgresql/12/main/postgresql.conf
# перезагружаем конфиг через отправку SIGHUP сигнала всем процессам БД
killall -s HUP postgres
Описание на зададените параметри:
- wal_level – колко информация да пише в WAL журналите, „replica“ – да записва всичко;
- archive_mode – включване на зареждането на WAL-журналите, използвайки командата от параметъра archive_command;
- archive_command – команда за архивиране на завършения WAL-журнал;
- archive_timeout – архивирането на журналите се извършва само когато той е завършен, но ако вашият сървър изменя/добавя малко данни в БД, има смисъл да се зададе лимит в секунди, след като командата за архивиране ще бъде извикана принудително (имам интензивно записване в базата всяка секунда, затова отказах да задавам този параметър в продукция);
- restore_command – команда за възстановяване на WAL-журнал от резервно копие, ще се използва, ако в „пълното резервно копие“ (base backup) липсват последните промени в БД.
Повече за всички тези параметри можете да прочетете в превода на официалната документация: .
Настройка на графика за резервно копиране
Каквото и да е, най-удобният начин за стартиране е cron. Точно него ще настроим за създаване на резервни копия. Нека да започнем с командата за създаване на пълен бекъп: в wal-g това е аргумент за стартиране. backup-push. Но първо е по-добре да изпълните тази команда ръчно от потребителя postgres, за да се уверите, че всичко е наред (и няма грешки в достъпа):
#!/bin/bash
su - postgres -c '/usr/local/bin/wal-g backup-push /var/lib/postgresql/12/main'
В аргументите за стартиране е посочен пътят към директорията с данни – напомням, че можете да го разберете, като изпълните pg_lsclusters.
Ако всичко е минало без грешки и данните са качени в хранилището S3, можете да настроите периодично стартиране в crontab:
#!/bin/bash
echo "15 4 * * * /usr/local/bin/wal-g backup-push /var/lib/postgresql/12/main >> /var/log/postgresql/walg_backup.log 2>&1" >> /var/spool/cron/crontabs/postgres
# задаем владельца и выставляем правильные права файлу
chown postgres: /var/spool/cron/crontabs/postgres
chmod 600 /var/spool/cron/crontabs/postgres
В този пример процесът на бекъп се стартира всеки ден в 4:15 сутринта.
Изтриване на стари резервни копия
Скорее всего, не е необходимо да съхранявате абсолютно всички бекъпи от мезозойската ера, затова ще бъде полезно периодично да „почиствате“ вашето хранилище (както „пълните бекъпи“, така и WAL-журналите). Ще направим всичко това също така чрез cron задача:
#!/bin/bash
echo "30 6 * * * /usr/local/bin/wal-g delete before FIND_FULL $(date -d '-10 days' '+%FT%TZ') --confirm >> /var/log/postgresql/walg_delete.log 2>&1" >> /var/spool/cron/crontabs/postgres
# ещё раз задаем владельца и выставляем правильные права файлу (хоть это обычно это и не нужно повторно делать)
chown postgres: /var/spool/cron/crontabs/postgres
chmod 600 /var/spool/cron/crontabs/postgres
Cron ще изпълнява тази задача всеки ден в 6:30 сутринта, изтривайки всичко (пълни бекъпи, делти и WAL'и) освен копията от последните 10 дни, но ще остави поне един бекъп до от указаната дата, така че всяка точка след на дата да попада в PITR.
Възстановяване от резервно копие
Не е тайна, че залогът за здрава база данни е периодичното възстановяване и проверка на целостта на данните. Как да се възстановим с помощта на WAL-G – ще обясня в този раздел, а проверките ще разгледаме след това.
Отделно трябва да се отбележи че за възстановяване в тестова среда (всичко, което не е продукция) – трябва да използвате Read Only акаунт в S3, за да не запишете по погрешка бекъпи. В случай с WAL-G трябва да зададете на потребителя S3 следните права в Group Policy (Effect: Allow): s3:GetObject, s3:ListBucket, s3:GetBucketLocation. И, разбира се, предварително не забравяйте да зададете archive_mode=off в конфигурационния файл postgresql.conf, за да не искате вашата тестова база да се бекъпва потайно.
Възстановяването се извършва с леко движение на ръката с изтриване на всички данни на PostgreSQL (включително потребители), затова, моля, бъдете изключително внимателни, когато стартирате следните команди.
#!/bin/bash
# если есть балансировщик подключений (например, pgbouncer), то вначале отключаем его, чтобы он не нарыгал ошибок в лог
service pgbouncer stop
# если есть демон, который перезапускает упавшие процессы (например, monit), то останавливаем в нём процесс мониторинга базы (у меня это pgsql12)
monit stop pgsql12
# или останавливаем мониторинг полностью
service monit stop
# останавливаем саму базу данных
service postgresql stop
# удаляем все данные из текущей базы (!!!); лучше предварительно сделать их копию, если есть свободное место на диске
rm -rf /var/lib/postgresql/12/main
# скачиваем резервную копию и разархивируем её
su - postgres -c '/usr/local/bin/wal-g backup-fetch /var/lib/postgresql/12/main LATEST'
# помещаем рядом с базой специальный файл-сигнал для восстановления (см. https://postgrespro.ru/docs/postgresql/12/runtime-config-wal#RUNTIME-CONFIG-WAL-ARCHIVE-RECOVERY ), он обязательно должен быть создан от пользователя postgres
su - postgres -c 'touch /var/lib/postgresql/12/main/recovery.signal'
# запускаем базу данных, чтобы она инициировала процесс восстановления
service postgresql start
За тези, които искат да проверят процеса на възстановяване — по-долу е подготвен малък кус от bash-магия, за да може, в случай на проблеми по време на възстановяване, скриптът да приключи с ненулев exit code. В този пример се правят 120 проверки с таймаут от 5 секунди (общо 10 минути за възстановяване), за да се установи дали сигналният файл е бил изтрит (това ще означава, че възстановяването е преминало успешно):
#!/bin/bash
CHECK_RECOVERY_SIGNAL_ITER=0
while [ ${CHECK_RECOVERY_SIGNAL_ITER} -le 120 ]
do
if [ ! -f "/var/lib/postgresql/12/main/recovery.signal" ]
then
echo "recovery.signal removed"
break
fi
sleep 5
((CHECK_RECOVERY_SIGNAL_ITER+1))
done
# если после всех проверок файл всё равно существует, то падаем с ошибкой
if [ -f "/var/lib/postgresql/12/main/recovery.signal" ]
then
echo "recovery.signal still exists!"
exit 17
fi
След успешното възстановяване, не забравяйте да стартирате отново всички процеси (pgbouncer/monit и т.н.).
Проверка на данните след възстановяване
Задължително е да се провери целостта на базата след възстановяване, за да не се получи ситуация с повредена/крива резервна копия. И е по-добре да се прави това с всеки създаден архив, но къде и как – зависи само от вашата фантазия (можете да стартирате отделни сървъри на почасова основа или да стартирате проверка в CI). Но поне – задължително е да се проверят данните и индексите в базата.
За проверка на данните е достатъчно да ги пуснете през дъмп, но е по-добре, ако при създаването на базата имате включени контролни суми ():
#!/bin/bash
if ! su - postgres -c 'pg_dumpall > /dev/null'
then
echo 'pg_dumpall failed'
exit 125
fi
За проверка на индексите – съществува , SQL-запроса към който ще вземем от и около него ще изградим малка логика:
#!/bin/bash
# добавляем sql-запрос для проверки в файл во временной директории
cat > /tmp/amcheck.sql << EOF
CREATE EXTENSION IF NOT EXISTS amcheck;
SELECT bt_index_check(c.oid), c.relname, c.relpages
FROM pg_index i
JOIN pg_opclass op ON i.indclass[0] = op.oid
JOIN pg_am am ON op.opcmethod = am.oid
JOIN pg_class c ON i.indexrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE am.amname = 'btree'
AND c.relpersistence != 't'
AND i.indisready AND i.indisvalid;
EOF
chown postgres: /tmp/amcheck.sql
# добавляем скрипт для запуска проверок всех доступных баз в кластере
# (обратите внимание что переменные и запуск команд – экранированы)
cat > /tmp/run_amcheck.sh << EOF
for DBNAME in $(su - postgres -c 'psql -q -A -t -c "SELECT datname FROM pg_database WHERE datistemplate = false;" ')
do
echo "Database: ${DBNAME}"
su - postgres -c "psql -f /tmp/amcheck.sql -v 'ON_ERROR_STOP=1' ${DBNAME}" && EXIT_STATUS=$? || EXIT_STATUS=$?
if [ "${EXIT_STATUS}" -ne 0 ]
then
echo "amcheck failed on DB: ${DBNAME}"
exit 125
fi
done
EOF
chmod +x /tmp/run_amcheck.sh
# запускаем скрипт
/tmp/run_amcheck.sh > /tmp/amcheck.log
# для проверки что всё прошло успешно можно проверить exit code или grep’нуть ошибку
if grep 'amcheck failed' "/tmp/amcheck.log"
then
echo 'amcheck failed: '
cat /tmp/amcheck.log
exit 125
fi
В обобщение
Изразявам благодарност на Андрей Боридин за помощта при подготовката на публикацията и отделно благодаря за неговия принос в разработката на WAL-G!
На това място, тази бележка приключва. Надявам се, че успях да предам леснотата на настройката и огромния потенциал за приложение на този инструмент във вашата компания. Чувал съм много за WAL-G, но никога не ми стигаше времето да седна и да се запозная. А след като го внедрих при себе си – от мен излезе тази статия.
По-специално, трябва да се отбележи, че WAL-G също може да работи с следните СУБД:
- ;
- ;
- ;
- И според комитите – се очакват още няколко!
Източник: habr.com
