WAL-G: zálohovanie a obnova PostgreSQL DBMS

Už dlho je známe, že vytváranie záloh do výpisov SQL (pomocou pg_dump alebo pg_dumpall) nie je dobrý nápad. Na zálohovanie PostgreSQL DBMS je lepšie použiť príkaz pg_basebackup, ktorý vytvára binárnu kópiu denníkov WAL. Ale keď začnete študovať celý proces vytvárania kópie a obnovy, pochopíte, že musíte napísať aspoň pár trojkoliek, aby to fungovalo a nespôsobovalo vám bolesť hore aj dole. Na zmiernenie utrpenia bol vyvinutý WAL-G.

WAL-G je nástroj napísaný v Go na zálohovanie a obnovu PostgreSQL databáz (a nedávno MySQL/MariaDB, MongoDB a FoundationDB). Podporuje prácu s úložiskom Amazon S3 (a analógmi, napríklad Yandex Object Storage), ako aj Google Cloud Storage, Azure Storage, Swift Object Storage a jednoducho so súborovým systémom. Celé nastavenie pozostáva z jednoduchých krokov, no vzhľadom na to, že články o ňom sú roztrúsené po internete, neexistuje žiadny kompletný návod, ktorý by obsahoval všetky kroky od začiatku do konca (na Habré je niekoľko príspevkov, ale chýba tam veľa bodov).

WAL-G: zálohovanie a obnova PostgreSQL DBMS

Tento článok bol napísaný predovšetkým preto, aby som systematizoval moje vedomosti. Nie som DBA a viem sa niekde vyjadrovať aj laicky, takže prípadné opravy sú vítané!

Samostatne poznamenávam, že všetko nižšie je relevantné a testované pre PostgreSQL 12.3 na Ubuntu 18.04, všetky príkazy musia byť spustené ako privilegovaný používateľ.

Inštalácia

V čase písania tohto článku je stabilná verzia WAL-G v0.2.15 (marec 2020). Toto budeme používať (ale ak si to chcete zostaviť sami z hlavnej vetvy, tak úložisko github má na to všetky pokyny). Ak chcete stiahnuť a nainštalovať, musíte urobiť:

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

Potom musíte najskôr nakonfigurovať WAL-G a potom samotný PostgreSQL.

Nastavenie WAL-G

Ako príklad ukladania záloh bude použitý Amazon S3 (pretože je bližšie k mojim serverom a jeho použitie je veľmi lacné). Na prácu s ním potrebujete „vedro s3“ a prístupové kľúče.

Všetky predchádzajúce články o WAL-G používali konfiguráciu pomocou premenných prostredia, ale v tomto vydaní sa nastavenia dajú nájsť súbor .walg.json v domovskom adresári používateľa postgres. Ak ho chcete vytvoriť, spustite nasledujúci bash skript:

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

Dovoľte mi vysvetliť všetky parametre:

  • WALG_S3_PREFIX – cestu k vášmu bucketu S3, kam sa budú nahrávať zálohy (môžete buď do koreňového adresára alebo do priečinka);
  • AWS_ACCESS_KEY_ID – prístupový kľúč v S3 (v prípade obnovy na testovacom serveri musia mať tieto kľúče politiku iba na čítanie! Toto je podrobnejšie popísané v časti o obnove.);
  • AWS_SECRET_ACCESS_KEY – tajný kľúč v úložisku S3;
  • WALG_COMPRESSION_METHOD – kompresná metóda, je lepšie použiť Brotli (keďže toto je zlatý stred medzi konečnou veľkosťou a rýchlosťou kompresie/dekompresie);
  • WALG_DELTA_MAX_STEPS – počet „delt“ pred vytvorením úplnej zálohy (šetria čas a veľkosť sťahovaných dát, ale môžu mierne spomaliť proces obnovy, preto nie je vhodné používať veľké hodnoty);
  • PGDATA – cesta k adresáru s vašimi databázovými údajmi (môžete to zistiť spustením príkazu pg_lsclusters);
  • PGHOST – pripojenie k databáze, s lokálnou zálohou je lepšie to urobiť cez unix-socket ako v tomto príklade.

Ďalšie parametre nájdete v dokumentácii: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Nastavenie PostgreSQL

Aby mohol archivátor vo vnútri databázy nahrať protokoly WAL do cloudu a v prípade potreby ich z nich obnoviť, musíte v konfiguračnom súbore nastaviť niekoľko parametrov /etc/postgresql/12/main/postgresql.conf. Len na úvod musíte sa uistiťže žiadne z nižšie uvedených nastavení nie je nastavené na žiadne iné hodnoty, takže pri opätovnom načítaní konfigurácie DBMS nepadne. Tieto parametre môžete pridať pomocou:

#!/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 parametrov, ktoré sa majú nastaviť:

  • wal_level – koľko informácií zapísať do denníkov WAL, „replika“ – zapísať všetko;
  • archívny režim – povoliť sťahovanie WAL protokolov príkazom z parametra archív_príkaz;
  • archív_príkaz – príkaz na archiváciu vyplneného protokolu WAL;
  • archive_timeout – archivácia protokolov sa vykoná až po jej dokončení, ale ak váš server zmení/pridá do databázy málo údajov, potom má zmysel nastaviť tu limit v sekundách, po ktorom bude vynútene vyvolaný príkaz na archiváciu (Do databázy intenzívne zapisujem každú sekundu, preto som sa rozhodol tento parameter nenastavovať vo výrobe);
  • príkaz obnovenia – príkaz na obnovenie protokolu WAL zo zálohy sa použije, ak v „úplnej zálohe“ (základnej zálohe) chýbajú najnovšie zmeny v databáze.

Viac o všetkých týchto parametroch sa dočítate v preklade oficiálnej dokumentácie: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Nastavenie plánu zálohovania

Čokoľvek môže niekto povedať, najpohodlnejší spôsob, ako to spustiť, je cron. Toto nakonfigurujeme na vytváranie záloh. Začnime príkazom na vytvorenie úplnej zálohy: vo wal-g je to argument spustenia záloha-push. Najprv je však lepšie spustiť tento príkaz manuálne od používateľa postgres, aby ste sa uistili, že je všetko v poriadku (a nie sú žiadne chyby prístupu):

#!/bin/bash

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

Argumenty spustenia označujú cestu k adresáru údajov – pripomínam, že ju zistíte spustením pg_lsclusters.

Ak všetko prebehlo bez chýb a údaje boli načítané do úložiska S3, potom môžete nakonfigurovať pravidelné spúšťanie 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 príklade sa proces zálohovania začína každý deň o 4:15.

Odstránenie starých záloh

S najväčšou pravdepodobnosťou nemusíte uchovávať úplne všetky zálohy z obdobia druhohôr, takže bude užitočné pravidelne „vyčistiť“ svoje úložisko (tak „úplné zálohy“, ako aj protokoly WAL). Toto všetko urobíme prostredníctvom ú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í túto úlohu každý deň o 6:30, vymaže všetko (úplné zálohy, delty a WAL) okrem kópií za posledných 10 dní, ale ponechá aspoň jednu zálohu na určený dátum tak, že akýkoľvek bod po dátumy boli zahrnuté v PITR.

Obnova zo zálohy

Nie je žiadnym tajomstvom, že kľúčom k zdravej databáze je pravidelné obnovovanie a overovanie integrity údajov vo vnútri. V tejto časti vám poviem, ako obnoviť pomocou WAL-G, a o kontrolách si povieme neskôr.

Je potrebné poznamenať osobitne že na obnovu v testovacom prostredí (všetko, čo nie je produkčné) je potrebné použiť účet Iba na čítanie v S3, aby ste náhodou neprepísali zálohy. V prípade WAL-G je potrebné nastaviť nasledujúce práva pre používateľa S3 v Zásadách skupiny (Efekt: Povoliť): s3:GetObject, s3:ListBucket, s3:GetBucketLocation. A, samozrejme, nezabudnite nastaviť archive_mode=off v súbore nastavení postgresql.conf, aby vaša testovacia databáza nechcela byť potichu zálohovaná.

Obnova sa vykonáva miernym pohybom ruky vymazanie všetkých údajov PostgreSQL (vrátane používateľov), preto buďte mimoriadne opatrní pri spúšťaní nasledujúcich príkazov.

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

Pre tých, ktorí chcú skontrolovať proces obnovy, je nižšie pripravený malý kúsok bash mágie, takže v prípade problémov pri obnove skript spadne s nenulovým výstupným kódom. V tomto príklade sa vykoná 120 kontrol s časovým limitom 5 sekúnd (celkovo 10 minút na obnovenie), aby sa zistilo, či bol súbor so signálom vymazaný (to znamená, že obnovenie bolo úspeš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 úspešnom zotavení nezabudnite spustiť všetky procesy späť (pgbouncer/monit atď.).

Kontrola údajov po obnovení

Po obnove je bezpodmienečne nutné skontrolovať integritu databázy, aby nenastala situácia s nefunkčnou/pokrivenou zálohou. A je lepšie to urobiť s každým vytvoreným archívom, ale kde a ako závisí len od vašej fantázie (môžete zvýšiť jednotlivé servery na hodinovej báze alebo spustiť kontrolu v CI). Ale minimálne je potrebné kontrolovať údaje a indexy v databáze.

Na kontrolu dát stačí spustiť ich cez výpis, ale je lepšie, aby ste pri vytváraní databázy mali povolené kontrolné súčty (kontrolné súčty údajov):

#!/bin/bash

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

Ak chcete skontrolovať indexy - existuje modul amcheck, zoberme si na to SQL dotaz z testy WAL-G a vybudovať okolo 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

Zhrnutie

Rád by som vyjadril svoju vďaku Andreymu Borodinovi za pomoc pri príprave publikácie a špeciálne poďakovanie za jeho príspevok k rozvoju WAL-G!

Týmto sa táto poznámka uzatvára. Dúfam, že sa mi podarilo sprostredkovať jednoduchosť nastavenia a obrovský potenciál využitia tohto nástroja vo vašej spoločnosti. Počul som veľa o WAL-G, ale nikdy som nemal dosť času sadnúť si a prísť na to. A po tom, čo som to implementoval doma, vyšiel zo mňa tento článok.

Samostatne stojí za zmienku, že WAL-G môže pracovať aj s nasledujúcimi DBMS:

Zdroj: hab.com

Pridať komentár