WAL-G:PostgreSQL DBMS 的備份與還原

眾所周知,將備份製作成 SQL 轉儲(使用 pg_dumppg_dumpall)不是一個好主意。 要備份 PostgreSQL DBMS,最好使用指令 pg_basebackup,它產生 WAL 日誌的二進位副本。 但是當你開始研究創建副本和恢復的整個過程時,你就會明白你需要至少編寫幾個三輪車才能使其工作,而不會讓你上下都感到痛苦。 為了減輕痛苦,開發了 WAL-G。

沃爾-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數據 – 包含資料庫資料的目錄路徑(你可以透過運行命令來找出 pg_lsclusters);
  • PGGHOST – 連接到資料庫,最好透過 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 搭配使用:

來源: www.habr.com

添加評論