WAL-G: архивиране и възстановяване на СУБД на PostgreSQL

Отдавна е известно архивирането в SQL дъмпове (с помощта на pg_dump или pg_dumpall) не е добра идея. За да архивирате PostgreSQL DBMS, е по-добре да използвате командата pg_basebackup, което прави двоично копие на WAL регистрационни файлове. Но когато започнете да изучавате целия процес на архивиране и възстановяване, ще разберете, че трябва да напишете поне няколко триколки, за да работи и да не ви причинява болка отгоре и отдолу. За да се облекчи страданието, WAL-G е разработен.

WAL-G е инструмент, написан на Go за архивиране и възстановяване на бази данни PostgreSQL (и по-скоро MySQL/MariaDB, MongoDB и FoundationDB). Поддържа работа с Amazon S3 хранилища (и аналози, например Yandex Object Storage), както и Google Cloud Storage, Azure Storage, Swift Object Storage и само с файловата система. Цялата настройка се свежда до прости стъпки, но поради факта, че статиите за това са разпръснати в интернет, няма пълно ръководство с инструкции, което да включва всички стъпки от и до (има няколко публикации на Habré, но много точки са пропуснати там).

WAL-G: архивиране и възстановяване на СУБД на PostgreSQL

Тази статия е написана предимно за систематизиране на вашите знания. Не съм DBA и мога да говоря някъде на лаишки език, така че всякакви корекции са добре дошли!

Отделно отбелязвам, че всичко по-долу е подходящо и тествано за PostgreSQL 12.3 на Ubuntu 18.04, всички команди трябва да се изпълняват като привилегирован потребител.

Инсталация

По време на това писане стабилната версия на WAL-G е v0.2.15 (март 2020 г.). Ние ще го използвамено ако искате да го изградите сами от основния клон, тогава хранилището на 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 са използвали конфигурация с променливи на средата, но от тази версия настройките могат да бъдат разположени в .walg.json файл в домашната директория на потребителя на 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-сокет, както в този пример.

Други опции можете да намерите в документацията: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Конфигуриране на 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, "реплика" - запишете всичко;
  • архивен_режим – разрешаване на зареждане на WAL регистрационни файлове чрез командата от параметъра команда_архив;
  • команда_архив – команда за архивиране на попълнения WAL лог;
  • archive_timeout - архивирането на регистрационни файлове се извършва само когато е завършено, но ако вашият сървър промени / добави малко данни към базата данни, тогава има смисъл да зададете ограничение тук за секунди, след което командата за архивиране ще бъде извикана принудително (Имам интензивен запис в базата данни всяка секунда, така че отказах да задам този параметър в производството);
  • команда за_възстановяване – командата за възстановяване на регистрационния файл на WAL от резервно копие ще се използва, ако в „пълното архивиране“ (базово архивиране) липсват последните промени в базата данни.

Можете да прочетете повече за всички тези параметри в превода на официалната документация: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Настройване на график за архивиране

Харесва ви или не, но най-удобният начин за стартиране е 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 - ще ви кажа в този раздел и ще говорим за проверките по-късно.

Трябва да се отбележи отделно че за възстановяване на тестова среда (всичко, което не е производство) - трябва да използвате акаунт само за четене в S3, за да не презапишете случайно архивите. В случай на WAL-G, трябва да зададете на потребителя на S3 следните права в груповите правила (Ефект: Разрешаване): s3: GetObject, s3:ListBucket, s3:GetBucketLocation. И, разбира се, не забравяйте да публикувате archive_mode=изкл във файла с настройки 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 магия е подготвено по-долу, така че в случай на проблеми при възстановяването, скриптът ще се срине с ненулев код за изход. В този пример се правят 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

За проверка на индекси - съществува amcheck модул, sql-заявка към него ще вземем от WAL-G тестове и изградете малко логика около:

#!/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 може да работи и със следните СУБД:

Източник: www.habr.com

Добавяне на нов коментар