WAL-G: zálohování a obnova PostgreSQL DBMS

Již dlouho je známo, že vytváření záloh do výpisů SQL (pomocí pg_dump nebo pg_dumpall) není dobrý nápad. Pro zálohování PostgreSQL DBMS je lepší použít příkaz pg_basebackup, který vytváří binární kopii protokolů WAL. Když ale začnete studovat celý proces tvorby kopie a obnovy, pochopíte, že je potřeba napsat alespoň pár tříkolek, aby to fungovalo a nezpůsobovalo vám bolest jak nahoře, tak dole. Pro zmírnění utrpení byl vyvinut WAL-G.

WAL-G je nástroj napsaný v Go pro zálohování a obnovu databází PostgreSQL (a nověji MySQL/MariaDB, MongoDB a FoundationDB). Podporuje práci s úložištěm Amazon S3 (a analogy, například Yandex Object Storage), dále Google Cloud Storage, Azure Storage, Swift Object Storage a jednoduše se systémem souborů. Celé nastavení se skládá z jednoduchých kroků, ale vzhledem k tomu, že články o něm jsou roztroušeny po internetu, neexistuje žádný kompletní návod, který by obsahoval všechny kroky od začátku do konce (na Habré je několik příspěvků, ale mnoho bodů tam chybí).

WAL-G: zálohování a obnova PostgreSQL DBMS

Tento článek byl napsán především proto, aby systematizoval mé znalosti. Nejsem DBA a umím se někde vyjádřit laicky, takže případné opravy vítám!

Samostatně poznamenávám, že vše níže je relevantní a testováno pro PostgreSQL 12.3 na Ubuntu 18.04, všechny příkazy musí být prováděny jako privilegovaný uživatel.

Instalace

V době psaní tohoto článku je stabilní verze WAL-G v0.2.15 (březen 2020). To je to, co budeme používat (ale pokud to chcete sestavit sami z hlavní větve, pak úložiště github má k tomu všechny pokyny). Chcete-li stáhnout a nainstalovat, musíte provést:

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

Poté musíte nejprve nakonfigurovat WAL-G a poté samotný PostgreSQL.

Nastavení WAL-G

Pro příklad ukládání záloh bude použit Amazon S3 (protože je blíže k mým serverům a jeho použití je velmi levné). Chcete-li s ním pracovat, potřebujete „kbelík s3“ a přístupové klíče.

Všechny předchozí články o WAL-G používaly konfiguraci pomocí proměnných prostředí, ale v této verzi lze nastavení najít soubor .walg.json v domovském adresáři uživatele postgres. Chcete-li jej vytvořit, spusťte následující skript 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

Dovolte mi vysvětlit trochu všechny parametry:

  • WALG_S3_PREFIX – cestu k vašemu bucketu S3, kam se budou nahrávat zálohy (můžete buď do kořenového adresáře, nebo do složky);
  • AWS_ACCESS_KEY_ID – přístupový klíč v S3 (v případě obnovy na testovacím serveru musí mít tyto klíče zásady pouze pro čtení! To je podrobněji popsáno v části o obnově.);
  • AWS_SECRET_ACCESS_KEY – tajný klíč v úložišti S3;
  • WALG_COMPRESSION_METHOD – kompresní metoda, je lepší použít Brotli (protože to je zlatý střed mezi konečnou velikostí a rychlostí komprese/dekomprese);
  • WALG_DELTA_MAX_STEPS – počet „rozdílů“ před vytvořením plné zálohy (šetří čas a velikost stahovaných dat, ale mohou mírně zpomalit proces obnovy, proto není vhodné používat velké hodnoty);
  • PGDATA – cesta k adresáři s daty vaší databáze (můžete to zjistit spuštěním příkazu pg_lsclusters);
  • PGHOST – připojení k databázi, s lokální zálohou je lepší to udělat přes unix-socket jako v tomto příkladu.

Další parametry naleznete v dokumentaci: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Konfigurace PostgreSQL

Aby mohl archivátor v databázi nahrát protokoly WAL do cloudu a obnovit je z nich (v případě potřeby), musíte v konfiguračním souboru nastavit několik parametrů /etc/postgresql/12/main/postgresql.conf. Jen pro začátek musíte se ujistitže žádné z níže uvedených nastavení není nastaveno na žádné jiné hodnoty, takže když se konfigurace znovu načte, DBMS nespadne. Tyto parametry můžete přidat pomocí:

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

Popis parametrů, které je třeba nastavit:

  • wal_level – kolik informací zapsat do protokolů WAL, „replika“ – zapsat vše;
  • archivní_režim – povolit stahování WAL logů příkazem z parametru archiv_příkaz;
  • archiv_příkaz – příkaz pro archivaci vyplněného WAL logu;
  • archive_timeout – archivace logů se provádí až po jejím dokončení, ale pokud váš server mění/přidává do databáze málo dat, pak má smysl zde nastavit limit v sekundách, po kterém bude vynuceně zavolán příkaz pro archivaci (Do databáze intenzivně zapisuji každou vteřinu, proto jsem se rozhodl tento parametr ve výrobě nenastavovat);
  • příkaz obnovení – příkaz k obnovení protokolu WAL ze zálohy se použije, pokud „úplná záloha“ (základní záloha) neobsahuje poslední změny v databázi.

Více o všech těchto parametrech si můžete přečíst v překladu oficiální dokumentace: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Nastavení plánu zálohování

Ať si někdo může říct cokoli, nejpohodlnějším způsobem, jak to spustit, je cron. To je to, co nakonfigurujeme pro vytváření záloh. Začněme příkazem pro vytvoření úplné zálohy: ve wal-g je to argument spuštění záloha-push. Nejprve je však lepší spustit tento příkaz ručně od uživatele postgres, abyste se ujistili, že je vše v pořádku (a nejsou žádné chyby přístupu):

#!/bin/bash

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

Spouštěcí argumenty udávají cestu k datovému adresáři – připomínám, že ji zjistíte spuštěním pg_lsclusters.

Pokud vše proběhlo bez chyb a data byla načtena do úložiště S3, pak můžete nakonfigurovat pravidelné spouštění v 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

V tomto příkladu proces zálohování začíná každý den ve 4:15.

Mazání starých záloh

S největší pravděpodobností nemusíte uchovávat absolutně všechny zálohy z druhohor, takže bude užitečné pravidelně „uklízet“ vaše úložiště (jak „úplné zálohy“, tak protokoly WAL). To vše provedeme prostřednictvím úlohy 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 spustí tuto úlohu každý den v 6:30, smaže vše (úplné zálohy, delty a WAL) kromě kopií za posledních 10 dní, ale ponechá alespoň jednu zálohu na zadané datum tak, že jakýkoli bod po data byla zařazena do PITR.

Obnovení ze zálohy

Není žádným tajemstvím, že klíčem ke zdravé databázi je pravidelné obnovování a ověřování integrity dat uvnitř. V této části vám řeknu, jak obnovit pomocí WAL-G, a o kontrolách si povíme později.

Je třeba poznamenat samostatně že pro obnovu v testovacím prostředí (vše, co není produkční) je potřeba v S3 použít účet jen pro čtení, aby nedošlo k náhodnému přepsání záloh. V případě WAL-G je potřeba nastavit následující práva pro uživatele S3 v Zásadách skupiny (Efekt: Povolit): s3:GetObject, s3:ListBucket, s3:GetBucketLocation. A samozřejmě nezapomeňte nastavit archive_mode=off v souboru nastavení postgresql.conf, aby vaše testovací databáze nechtěla být potichu zálohována.

Restaurování se provádí mírným pohybem ruky smazání všech dat PostgreSQL (včetně uživatelů), proto buďte velmi opatrní při spouštění následujících příkazů.

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

Pro ty, kteří chtějí zkontrolovat proces obnovy, je níže připraven malý kousek bash magie, takže v případě problémů při obnově skript spadne s nenulovým výstupním kódem. V tomto příkladu je provedeno 120 kontrol s časovým limitem 5 sekund (celkem 10 minut na obnovu), aby se zjistilo, zda byl soubor signálu smazán (to znamená, že obnova byla úspěšná):

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

Po úspěšné obnově nezapomeňte všechny procesy spustit zpět (pgbouncer/monit atd.).

Kontrola dat po obnovení

Po obnově je bezpodmínečně nutné zkontrolovat integritu databáze, aby nedošlo k situaci s poškozenou/pokřivenou zálohou. A je lepší to udělat s každým vytvořeným archivem, ale kde a jak záleží pouze na vaší fantazii (můžete zvyšovat jednotlivé servery na hodinové bázi nebo spustit kontrolu v CI). Minimálně je ale nutné kontrolovat data a indexy v databázi.

Pro kontrolu dat je stačí spustit přes výpis, ale je lepší mít při vytváření databáze povolené kontrolní součty (kontrolní součty dat):

#!/bin/bash

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

Chcete-li zkontrolovat indexy - existuje modul amcheck, vezměme na to dotaz SQL Testy WAL-G a postavit kolem toho trochu logiky:

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

Shrnutí

Rád bych vyjádřil svou vděčnost Andrey Borodinovi za jeho pomoc při přípravě publikace a zvláštní poděkování za jeho příspěvek k rozvoji WAL-G!

Tím tato poznámka končí. Doufám, že se mi podařilo zprostředkovat jednoduchost nastavení a obrovský potenciál použití tohoto nástroje ve vaší společnosti. Slyšel jsem toho o WAL-G hodně, ale nikdy jsem neměl dost času si sednout a přijít na to. A poté, co jsem to implementoval doma, vyšel ze mě tento článek.

Samostatně stojí za zmínku, že WAL-G může také pracovat s následujícími DBMS:

Zdroj: www.habr.com

Přidat komentář