WAL-G: pag-backup at pag-restore ng PostgreSQL DBMS

Matagal na itong kilala sa pag-backup sa mga SQL dumps (gamit ang pg_dump o pg_dumpall) ay hindi magandang ideya. Upang i-back up ang PostgreSQL DBMS, mas mahusay na gamitin ang command pg_basebackup, na gumagawa ng binary na kopya ng mga WAL log. Ngunit kapag sinimulan mong matutunan ang buong proseso ng paglikha ng isang kopya at pagpapanumbalik, mauunawaan mo na kailangan mong sumulat ng kahit man lang ilang tricycle para gumana ang lahat ng ito at hindi magdulot sa iyo ng sakit mula sa itaas at ibaba. Upang maibsan ang pagdurusa, binuo ang WAL-G.

WAL-G ay isang tool na nakasulat sa Go para sa pag-back up at pagpapanumbalik ng mga database ng PostgreSQL (at mas kamakailang MySQL/MariaDB, MongoDB at FoundationDB). Sinusuportahan nito ang pagtatrabaho sa mga storage ng Amazon S3 (at mga analogue, halimbawa, Yandex Object Storage), pati na rin ang Google Cloud Storage, Azure Storage, Swift Object Storage at gamit lamang ang file system. Ang buong setup ay bumaba sa mga simpleng hakbang, ngunit dahil sa katotohanan na ang mga artikulo tungkol dito ay nakakalat sa Internet, walang kumpletong manu-manong how-to na magsasama ng lahat ng mga hakbang mula at patungo sa (may ilang mga post sa Habré, ngunit maraming puntos ang hindi nakuha doon).

WAL-G: pag-backup at pag-restore ng PostgreSQL DBMS

Ang artikulong ito ay pangunahing isinulat upang ma-systematize ang iyong kaalaman. Hindi ako isang DBA at nakakapagsalita ako sa lenggwahe ng karaniwang tao sa isang lugar, kaya anumang mga pagwawasto ay malugod na tinatanggap!

Hiwalay, tandaan ko na ang lahat sa ibaba ay may kaugnayan at nasubok para sa PostgreSQL 12.3 sa Ubuntu 18.04, ang lahat ng mga utos ay dapat isagawa bilang isang may pribilehiyong gumagamit.

Instalasyon

Sa oras ng pagsulat na ito, ang matatag na bersyon ng WAL-G ay v0.2.15 (Marso 2020). Gagamitin natin itongunit kung nais mong buuin ito sa iyong sarili mula sa master branch, kung gayon ang github repository ay mayroong lahat ng mga tagubilin para dito). Upang i-download at i-install, kailangan mong patakbuhin ang:

#!/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/

Pagkatapos nito, kailangan mong i-configure muna ang WAL-G, at pagkatapos ay ang PostgreSQL mismo.

Pag-setup ng WAL-G

Para sa isang halimbawa ng pag-iimbak ng mga backup, gagamitin ang Amazon S3 (dahil mas malapit ito sa mga server ko at napakamura gamitin). Upang magawa ito, kailangan mo ng "s3-bucket" at mga access key.

Ang lahat ng nakaraang artikulo sa WAL-G ay gumamit ng configuration na may mga variable ng kapaligiran, ngunit simula sa paglabas na ito, maaaring makita ang mga setting sa .walg.json file sa home directory ng postgres user. Upang gawin ito, patakbuhin ang sumusunod na script ng 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

Hayaan akong magpaliwanag ng kaunti tungkol sa lahat:

  • WALG_S3_PREFIX - ang landas patungo sa iyong S3 bucket kung saan ia-upload ang mga backup (maaari mong alinman sa root o sa folder);
  • AWS_ACCESS_KEY_ID – access key sa S3 (sa kaso ng pagpapanumbalik sa isang test server - ang mga key na ito ay dapat na may ReadOnly Policy! Ito ay tinalakay nang mas detalyado sa seksyon ng pagbawi.);
  • AWS_SECRET_ACCESS_KEY – lihim na susi sa imbakan ng S3;
  • WALG_COMPRESSION_METHOD - paraan ng compression, mas mainam na gumamit ng Brotli (dahil ito ang ginintuang ibig sabihin sa pagitan ng huling sukat at ang bilis ng compression / decompression);
  • WALG_DELTA_MAX_STEPS - ang bilang ng "deltas" bago lumikha ng isang buong backup (nagbibigay-daan sa iyo na makatipid ng oras at laki ng na-download na data, ngunit maaaring bahagyang pabagalin ang proseso ng pagbawi, kaya hindi ipinapayong gumamit ng malalaking halaga);
  • PGDATA – landas patungo sa direktoryo kasama ang iyong data ng database (ay matatagpuan sa pamamagitan ng pagpapatakbo ng command pg_lsclusters);
  • PGHOST - koneksyon sa database, na may isang lokal na backup, mas mahusay na gawin ito sa pamamagitan ng unix-socket, tulad ng sa halimbawang ito.

Ang iba pang mga opsyon ay matatagpuan sa dokumentasyon: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Pag-configure ng PostgreSQL

Upang ang archiver sa loob ng database ay mag-upload ng mga WAL log sa cloud at mabawi mula sa kanila (kung kinakailangan), kailangan mong magtakda ng ilang mga parameter sa configuration file /etc/postgresql/12/main/postgresql.conf. For starters lang kailangan mong siguraduhinna wala sa mga setting sa ibaba ang nakatakda sa anumang iba pang value, para kapag na-reload ang configuration, hindi bumaba ang DBMS. Maaari mong idagdag ang mga opsyong ito gamit ang:

#!/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

Paglalarawan ng mga parameter na itatakda:

  • wal_level - gaano karaming impormasyon ang isusulat sa mga log ng WAL, "replica" - isulat ang lahat;
  • archive_mode – paganahin ang pag-load ng mga WAL log gamit ang command mula sa parameter archive_command;
  • archive_command – isang utos para sa pag-archive ng nakumpletong WAL log;
  • archive_timeout - Ang pag-archive ng mga log ay isinasagawa lamang kapag ito ay nakumpleto, ngunit kung ang iyong server ay nagbabago/nagdagdag ng kaunting data sa database, pagkatapos ay makatuwiran na magtakda ng isang limitasyon dito sa ilang segundo, pagkatapos kung saan ang pag-archive na utos ay tatawagin nang pilit (Mayroon akong masinsinang pagsulat sa database bawat segundo, kaya tumanggi akong itakda ang parameter na ito sa produksyon);
  • restore_command – ang utos na ibalik ang WAL log mula sa isang backup ay gagamitin kung ang “full backup” (base backup) ay kulang sa pinakabagong mga pagbabago sa database.

Maaari kang magbasa nang higit pa tungkol sa lahat ng mga parameter na ito sa pagsasalin ng opisyal na dokumentasyon: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Pagse-set up ng backup na iskedyul

Gusto o hindi, ngunit ang pinaka-maginhawang paraan upang patakbuhin ito ay cron. Ito ang aming iko-configure upang lumikha ng mga backup. Magsimula tayo sa utos na lumikha ng isang buong backup: sa wal-g, ito ang argumento sa paglulunsad backup-push. Ngunit una, mas mahusay na patakbuhin ang command na ito nang manu-mano bilang isang gumagamit ng postgres upang matiyak na maayos ang lahat (at walang mga error sa pag-access):

#!/bin/bash

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

Ang mga argumento sa paglunsad ay naglalaman ng landas patungo sa direktoryo ng data - Ipinaaalala ko sa iyo na mahahanap mo ito sa pamamagitan ng pagtakbo pg_lsclusters.

Kung ang lahat ay napunta nang walang mga error at ang data ay na-load sa imbakan ng S3, maaari mong higit pang i-configure ang pana-panahong paglulunsad sa 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

Sa halimbawang ito, ang proseso ng pag-backup ay magsisimula araw-araw sa 4:15 am.

Tinatanggal ang mga lumang backup

Malamang na hindi mo kailangang panatilihing ganap ang lahat ng mga backup mula sa panahon ng Mesozoic, kaya magandang ideya na pana-panahong "linisin" ang iyong storage (parehong "mga buong backup" at WAL log). Gagawin din namin ang lahat sa pamamagitan ng cron task:

#!/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

Patakbuhin ng Cron ang gawaing ito araw-araw sa 6:30 am, tatanggalin ang lahat (mga buong backup, deltas at WAL) maliban sa huling 10 araw, ngunit nag-iiwan ng kahit isang backup. sa tinukoy na petsa sa anumang punto pagkatapos nakapasok ang mga petsa sa PITR.

Pagpapanumbalik mula sa isang backup

Hindi lihim na ang susi sa isang malusog na database ay ang pana-panahong ibalik at suriin ang integridad ng data sa loob. Paano mag-recover gamit ang WAL-G - Sasabihin ko sa iyo sa seksyong ito, at pag-uusapan natin ang tungkol sa mga tseke sa ibang pagkakataon.

Dapat itong pansinin nang magkahiwalay na ibalik sa isang pagsubok na kapaligiran (lahat ng bagay na hindi produksyon) - kailangan mong gumamit ng Read Only account sa S3, upang hindi aksidenteng ma-overwrite ang mga backup. Sa kaso ng WAL-G, kailangan mong itakda ang S3 user ng mga sumusunod na karapatan sa Group Policy (Epekto: Payagan): s3:GetObject, s3: ListBucket, s3:GetBucketLocation. At, siyempre, huwag kalimutang mag-post archive_mode=off sa file ng mga setting postgresql.confupang ang iyong test base ay hindi gustong mag-back up nang tahimik.

Ang pagpapanumbalik ay ginagawa sa pamamagitan ng bahagyang paggalaw ng kamay gamit ang pagtanggal ng lahat ng data ng PostgreSQL (kabilang ang mga user), kaya mangyaring maging lubhang maingat kapag pinapatakbo mo ang mga sumusunod na command.

#!/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

Para sa mga gustong suriin ang proseso ng pagbawi, ang isang maliit na piraso ng bash magic ay inihanda sa ibaba upang sa kaso ng mga problema sa pagbawi, ang script ay nag-crash na may isang non-zero exit code. Sa halimbawang ito, 120 na pagsusuri ang ginawa na may timeout na 5 segundo (kabuuang 10 minuto para sa pagbawi) upang malaman kung ang signal file ay tinanggal (ito ay nangangahulugan na ang pagbawi ay matagumpay):

#!/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

Pagkatapos ng matagumpay na pagpapanumbalik, huwag kalimutang i-restart ang lahat ng mga proseso (pgbouncer / monit, atbp.).

Pag-verify ng data pagkatapos ng pagbawi

Siguraduhing suriin ang integridad ng database pagkatapos ng pagbawi, upang walang sitwasyon na may sira / baluktot na backup. At mas mahusay na gawin ito sa bawat nilikha na archive, ngunit kung saan at paano nakasalalay sa iyong imahinasyon (maaari kang magtaas ng hiwalay na mga server sa isang oras-oras na batayan o magpatakbo ng isang tseke sa CI). Ngunit sa pinakamababa, kinakailangang suriin ang data at mga index sa database.

Upang suriin ang data, ito ay sapat na upang patakbuhin ito sa pamamagitan ng isang dump, ngunit ito ay mas mahusay na mayroon kang mga checksum na pinagana kapag lumilikha ng database (mga checksum ng data):

#!/bin/bash

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

Upang suriin ang mga index - umiiral module ng amcheck, sql-query dito na aming kukunin Mga pagsubok sa WAL-G at bumuo ng isang maliit na lohika sa paligid:

#!/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

Upang ibuod

Ipinapahayag ko ang aking pasasalamat kay Andrey Borodin para sa kanyang tulong sa paghahanda ng publikasyon at espesyal na pasasalamat sa kanyang kontribusyon sa pagbuo ng WAL-G!

Ito ang nagtatapos sa talang ito. Umaasa ako na naihatid ko ang kadalian ng pagpapasadya at ang malaking potensyal para sa paggamit ng tool na ito sa iyong kumpanya. Marami akong narinig tungkol sa WAL-G, ngunit walang oras para maupo at alamin ito. At pagkatapos kong ipatupad ito sa bahay, lumabas sa akin ang artikulong ito.

Hiwalay, nararapat na tandaan na ang WAL-G ay maaari ding gumana sa sumusunod na DBMS:

Pinagmulan: www.habr.com

Magdagdag ng komento