Русский
Русский
English
Статистика
Реклама

Из песочницы WAL-G бэкапы и восстановление СУБД PostgreSQL

Уже давно известно, что делать бэкапы в SQL-дампы (используя pg_dump или pg_dumpall) не самая хорошая идея. Для резервного копирования СУБД PostgreSQL лучше использовать команду pg_basebackup, которая делает бинарную копию WAL-журналов. Но когда вы начнёте изучать весь процесс создания копии и восстановления, то поймёте что нужно написать как минимум пару трёхколёсных велосипедов, чтобы всё это работало и не вызывало у вас боль как сверху, так и снизу. Дабы облегчить страдания был разработан WAL-G.

WAL-G это инструмент, написанный на Golang для резервного копирования и восстановления PostgreSQL баз данных (а с недавнего времени и MySQL/MariaDB, MongoDB и FoundationDB). Он поддерживает работу с хранилищами Amazon S3 (и аналогами, например, Yandex Object Storage), а также Google Cloud Storage, Azure Storage, Swift Object Storage и просто с файловой системой. Вся настройка сводится к простым шагам, но из-за того что статьи о нём разрозненны по интернету нет полного how-to мануала, который бы включал все шаги от и до (на Хабре есть несколько постов, но многие моменты там упущены).

postgresql backup

Данная статья написана в первую очередь чтобы систематизировать свои знания. Я не являюсь DBA и где-то могу выражаться обывательско-разработческим языком, поэтому приветствуются любые исправления!

Отдельно отмечу что всё нижеприведенное актуально и проверено для PostgreSQL 12.3 на Ubuntu 18.04, все команды должны выполняться от привилегированного пользователя.

Установка


В момент написания данной статьи стабильная версия WAL-G v0.2.15 (март 2020). Её мы и будем использовать (но если вы захотите самостоятельно собрать его из master-ветки, то в репозитории на github есть все инструкции для этого). Для скачивания и установки нужно выполнить:

#!/bin/bashcurl -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.gzmv wal-g /usr/local/bin/

После этого нужно сконфигурировать вначале WAL-G, а потом сам PostgreSQL.

Настройка WAL-G


Для примера хранения бэкапов будет использоваться Amazon S3 (потому что он ближе к моим серверам и его использование обходится очень дёшево). Для работы с ним нужен s3-бакет и ключи доступа.

Во всех предыдущих статьях о WAL-G использовалось конфигурирование с помощью переменных окружения, но с этого релиза настройки можно расположить в .walg.json файле в домашней директории пользователя postgres. Для его создания выполним следующий bash-скрипт:

#!/bin/bashcat > /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-socket как в этом примере.

Остальные параметры можно посмотреть в документации: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Настройка PostgreSQL


Чтобы архиватор внутри базы сам заливал WAL-журналы в облако и восстанавливался из них (в случае необходимости) нужно задать несколько параметров в конфигурационном файле /etc/postgresql/12/main/postgresql.conf. Только для начала вам нужно убедиться, что никакие из нижеприведенных настроек не заданы в какие-то другие значения, чтобы при перезагрузке конфигурации СУБД не упала. Добавить эти параметры можно с помощью:

#!/bin/bashecho "wal_level=replica" >> /etc/postgresql/12/main/postgresql.confecho "archive_mode=on" >> /etc/postgresql/12/main/postgresql.confecho "archive_command='/usr/local/bin/wal-g wal-push \"%p\" >> /var/log/postgresql/archive_command.log 2>&1' " >> /etc/postgresql/12/main/postgresql.confecho archive_timeout=60 >> /etc/postgresql/12/main/postgresql.confecho "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) будет недоставать последних изменений в БД.

Подробнее обо всех этих параметрах можно прочитать в переводе официальной документации: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Настройка расписания резервного копирования


Как ни крути, но самым удобным способом для запуска является cron. Именно его мы и настроим для создания резервных копий. Начнём с команды создания полного бэкапа: в wal-g это аргумент запуска backup-push. Но для начала лучше выполнить эту команду вручную от пользователя postgres, чтобы убедиться что всё хорошо (и нет каких-то ошибок доступа):

#!/bin/bashsu - postgres -c '/usr/local/bin/wal-g backup-push /var/lib/postgresql/12/main'

В аргументах запуска указан путь к директории с данными напоминаю что его можно узнать, выполнив pg_lsclusters.

Если всё прошло без ошибок и данные загрузились в хранилище S3, то далее можно настроить периодический запуск в crontab:
#!/bin/bashecho "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/postgreschmod 600 /var/spool/cron/crontabs/postgres

В данном примере процесс бэкапа запускается каждый день в 4:15 утра.

Удаление старых резервных копий


Скорее всего вам не нужно хранить абсолютно все бэкапы с мезозойской эры, поэтому будет полезно периодически подчищать ваше хранилище (как полные бэкапы, так и WAL-журналы). Мы сделаем это всё также через cron задачу:

#!/bin/bashecho "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/postgreschmod 600 /var/spool/cron/crontabs/postgres

Cron будет выполнять эту задачу каждый день в 6:30 утра, удаляя всё (полные бэкапы, дельты и WALы) кроме копий за последние 10 дней, но оставит как минимум один бэкап до указанной даты, чтобы любая точка после даты попадала в PITR.

Восстановление из резервной копии


Ни для кого не секрет что залог здоровой базы в периодическом восстановлении и проверке целостности данных внутри. Как восстановиться с помощью WAL-G расскажу в этом разделе, а о проверках поговорим после.

Отдельно стоит отметить что для восстановления на тестовом окружении (всё то, что не production) нужно использовать 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 ), он обязательно должен быть создан от пользователя postgressu - postgres -c 'touch /var/lib/postgresql/12/main/recovery.signal'# запускаем базу данных, чтобы она инициировала процесс восстановленияservice postgresql start

Для тех, кто хочет проверять процесс восстановления ниже подготовлен небольшой кусок bash-магии, чтобы в случае проблем в восстановлении скрипт упал с ненулевым exit code. В данном примере делается 120 проверок с таймаутом в 5 секунд (всего 10 минут на восстановление), чтобы узнать удалился ли сигнальный файл (это будет означать что восстановление прошло успешно):

#!/bin/bashCHECK_RECOVERY_SIGNAL_ITER=0while [ ${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 17fi

После успешного восстановления не забудьте запустить обратно все процессы (pgbouncer/monit и тд).

Проверка данных после восстановления


Обязательно нужно проверить целостность базы после восстановления, чтобы не возникло ситуации с побитой/кривой резервной копией. И лучше делать это с каждым созданным архивом, но где и как зависит только от вашей фантазии (можно поднимать отдельные сервера на почасовой оплате или запускать проверку в CI). Но как минимум необходимо проверять данные и индексы в базе.

Для проверки данных достаточно прогнать их через дамп, но лучше чтобы при создании базы у вас были включены контрольные суммы (data checksums):

#!/bin/bashif ! su - postgres -c 'pg_dumpall > /dev/null'then    echo 'pg_dumpall failed'    exit 125fi

Для проверки индексов существует модуль amcheck, sql-запрос к нему возьмём из тестов WAL-G и вокруг выстроим небольшую логику:

#!/bin/bash# добавляем sql-запрос для проверки в файл во временной директорииcat > /tmp/amcheck.sql << EOFCREATE EXTENSION IF NOT EXISTS amcheck;SELECT bt_index_check(c.oid), c.relname, c.relpagesFROM pg_index iJOIN pg_opclass op ON i.indclass[0] = op.oidJOIN pg_am am ON op.opcmethod = am.oidJOIN pg_class c ON i.indexrelid = c.oidJOIN pg_namespace n ON c.relnamespace = n.oidWHERE am.amname = 'btree'AND c.relpersistence != 't'AND i.indisready AND i.indisvalid;EOFchown postgres: /tmp/amcheck.sql# добавляем скрипт для запуска проверок всех доступных баз в кластере# (обратите внимание что переменные и запуск команд  экранированы)cat > /tmp/run_amcheck.sh << EOFfor 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    fidoneEOFchmod +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 125fi

Резюмируя


Выражаю благодарность Андрею Бородину за помощь в подготовке публикации и отдельное спасибо за его вклад в разработку WAL-G!

На этом данная заметка подошла к концу. Надеюсь что смог донести легкость настройки и огромный потенциал для применения этого инструмента у вас в компании. Я очень много слышал о WAL-G, но никак не хватало времени сесть и разобраться. А после того как внедрил его у себя из меня вышла эта статья.

Отдельно стоит заметить, что WAL-G также может работать со следующими СУБД:

Источник: habr.com
К списку статей
Опубликовано: 14.06.2020 14:16:56
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Postgresql

Sql

Администрирование баз данных

Резервное копирование

Системное администрирование

Wal-g

Wal-e

Backup

Wal

S3

Бекап

Бэкап

Категории

Последние комментарии

© 2006-2020, personeltest.ru