WAL-G:PostgreSQL DBMS 的备份和恢复

众所周知,将备份制作成 SQL 转储(使用 pg_dump или pg_dumpall)不是一个好主意。 要备份 PostgreSQL DBMS,最好使用命令 pg_basebackup,它生成 WAL 日志的二进制副本。 但是,当您开始研究创建副本和恢复的整个过程时,您就会明白,您需要至少编写几个三轮车才能使其工作,而不会让您上下都感到痛苦。 为了减轻痛苦,开发了 WAL-G。

沃尔沃 是一个用 Go 编写的工具,用于备份和恢复 PostgreSQL 数据库(以及最近的 MySQL/MariaDB、MongoDB 和 FoundationDB)。 它支持使用 Amazon S3 存储(以及类似的存储,例如 Yandex 对象存储)以及 Google 云存储、Azure 存储、Swift 对象存储以及简单的文件系统。 整个设置归结为简单的步骤,但由于有关它的文章散布在互联网上,因此没有完整的操作手册包含从开始到结束的所有步骤(有几篇关于 Habré 的帖子,但那里遗漏了很多要点)。

WAL-G:PostgreSQL DBMS 的备份和恢复

写这篇文章主要是为了系统化我的知识。 我不是DBA,我可以在某个地方用外行语言表达自己,所以欢迎任何指正!

另外,我注意到以下所有内容都与 Ubuntu 12.3 上的 PostgreSQL 18.04 相关并经过测试,所有命令都必须以特权用户身份执行。

安装

在撰写本文时,WAL-G 的稳定版本是 v0.2.15(2020 年 XNUMX 月)。 这就是我们将使用的(但是如果你想从 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 的文章都使用环境变量进行配置,但在此版本中,设置可以位于 .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 中的访问密钥(如果在测试服务器上进行恢复,这些密钥必须具有只读策略! 关于恢复的部分对此进行了更详细的描述。);
  • AWS_SECRET_ACCESS_KEY – S3 存储中的密钥;
  • WALG_COMPRESSION_METHOD – 压缩方法,最好使用Brotli(因为这是最终大小和压缩/解压速度之间的黄金分割);
  • WALG_DELTA_MAX_STEPS – 创建完整备份之前的“增量”数量(它们节省时间和下载数据的大小,但会稍微减慢恢复过程,因此不建议使用较大的值);
  • 数据 – 包含数据库数据的目录路径(你可以通过运行命令来找出 pg_lsclusters);
  • PG主机 – 连接到数据库,最好通过 unix-socket 进行本地备份,如本例所示。

其他参数可以在文档中找到: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

设置 PostgreSQL

为了让数据库内的归档器将 WAL 日志上传到云端并从中恢复(如果需要),您需要在配置文件中设置几个参数 /etc/postgresql/12/main/postgresql.conf。 仅供初学者使用 你需要确保以下设置均未设置为任何其他值,以便重新加载配置时,DBMS 不会崩溃。 您可以使用以下方法添加这些参数:

#!/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 日志中写入多少信息,“副本” – 写入所有内容;
  • 归档模式 – 使用参数中的命令启用 WAL 日志下载 归档命令;
  • 归档命令 – 用于归档完整的 WAL 日志的命令;
  • 归档超时 – 日志归档仅在完成时执行,但如果您的服务器向数据库更改/添加很少的数据,那么在这里设置一个以秒为单位的限制是有意义的,之后将强制调用归档命令(我每秒都会密集地写入数据库,因此我决定不在生产中设置此参数);
  • 恢复命令 – 如果“完整备份”(基础备份)缺少数据库中的最新更改,则将使用从备份恢复 WAL 日志的命令。

您可以在官方文档的翻译中阅读有关所有这些参数的更多信息: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

设置备份计划

不管人们怎么说,运行它的最方便的方法是 cron。 这就是我们将配置来创建备份的内容。 让我们从创建完整备份的命令开始:在 wal-g 中,这是启动参数 备份推送。 但首先,最好从 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 运行此任务,删除除过去 10 天的副本之外的所有内容(完整备份、增量备份和 WAL),但至少保留一个备份 指定日期以便任何点 日期已包含在 PITR 中。

从备份恢复

众所周知,健康数据库的关键是定期恢复和验证内部数据的完整性。 我将在本节中告诉您如何使用 WAL-G 进行恢复,稍后我们将讨论检查。

另外值得注意的是 要在测试环境(非生产环境)中恢复,您需要在 S3 中使用只读帐户,以免意外覆盖备份。 对于WAL-G,您需要在组策略中为S3用户设置以下权限(效果:允许): s3:获取对象, s3:列表桶, s3:获取桶位置。 当然,不要忘记设置 存档模式=关闭 在设置文件中 配置文件,这样你的测试数据库就不想被悄悄备份了。

只需轻轻移动手即可进行修复 删除所有 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

检查索引 - 是否存在 检测模块,让我们从以下位置获取它的 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

总结

我谨向 Andrey Borodin 表示感谢,感谢他为准备本书提供的帮助,并特别感谢他为 WAL-G 的发展做出的贡献!

本笔记到此结束。 我希望我能够向您传达设置的简便性以及在贵公司使用此工具的巨大潜力。 我听说过很多关于 WAL-G 的事,但从来没有足够的时间坐下来弄清楚。 当我在家里实现它之后,这篇文章就出来了。

另外,值得注意的是,WAL-G 还可以与以下 DBMS 配合使用:

来源: habr.com

添加评论