WAL-G: zálohy a obnova databázového systému PostgreSQL

Už dávno sa vie, že robiť zálohy do SQL-dumpov (používajúc pg_dump alebo pg_dumpall) – nie je najlepší nápad. Pre zálohovanie databázy PostgreSQL je lepšie použiť príkaz pg_basebackup, ktorý vytvára binárnu kópiu WAL žurnálov. Ale keď začnete študovať celý proces vytvárania kópie a obnovovania, pochopíte, že potrebujete napísať aspoň pár trojkolesových bicyklov, aby to všetko fungovalo a nespôsobovalo vám bolesť ako nahoře, tak aj dole. Aby sa zmiernilo utrpenie, bol vyvinutý WAL-G.

WAL-G – je nástroj napísaný v Go na zálohovanie a obnovovanie databáz PostgreSQL (a od nedávna aj MySQL/MariaDB, MongoDB a FoundationDB). Podporuje prácu s úložiskami Amazon S3 (a analogami, ako 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 spočíva na jednoduchých krokoch, ale kvôli tomu, že články o ňom sú roztrúsené po internete – neexistuje úplný how-to manuál, ktorý by obsahoval všetky kroky od začiatku do konca (na Habré je niekoľko príspevkov, ale mnohé aspekty tam chýbajú).

WAL-G: zálohy a obnova databázového systému PostgreSQL

Tento článok bol napísaný predovšetkým na to, aby som systematizoval svoje vedomosti. Nie som DBA a niekde sa môžem vyjadrovať bežným vývojárskym jazykom, preto sú vítané akékoľvek opravy!

Samostatne zdôrazním, že všetko nižšie uvedené je relevantné a overené pre PostgreSQL 12.3 na Ubuntu 18.04, všetky príkazy by mali byť vykonávané od privilegovaného používateľa.

Inštalácia

V čase písania tohto článku stabilná verzia WAL-G – v0.2.15 (marec 2020). Tú budeme používať (ale ak sa rozhodnete zostaviť ho sami z master vetvy, v úložisku na github sú všetky inštrukcie na to). Pre stiahnutie a inštaláciu je potrebné vykonať:

#!/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 je potrebné najprv nakonfigurovať WAL-G, a potom samotný PostgreSQL.

Nastavenie WAL-G

Ako príklad úložiska záloh sa použije Amazon S3 (pretože je bližšie k mojim serverom a jeho použitie je veľmi lacné). Na jeho prácu je potrebný „s3-bucket“ a prístupové kľúče.

Vo všetkých predchádzajúcich článkoch o WAL-G sa používalo konfigurácie pomocou environmentálnych premenných, ale od tohto vydania je možné nastavenia umiestniť do .walg.json súboru v domovskej priečinke používateľa postgres. Na jeho vytvorenie vykonáme 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

Trochu vysvetlím všetky parametre:

  • WALG_S3_PREFIX – cesta k vašemu S3-buketu, kam budú nahrávané zálohy (môže byť ako do koreňa, tak aj do priečinka);
  • AWS_ACCESS_KEY_ID – prístupový kľúč do S3 (pri obnove na testovacom serveri – tieto kľúče by mali mať ReadOnly Policy! O tom je podrobnejšie písané v časti o obnove);
  • AWS_SECRET_ACCESS_KEY – tajný kľúč v úložisku S3;
  • WALG_COMPRESSION_METHOD – metóda kompresie, najlepšie použiť Brotli (pretože to je zlatý stred medzi konečnou veľkosťou a rýchlosťou kompresie/dekompresie);
  • WALG_DELTA_MAX_STEPS – počet „delta“ pred vytvorením úplnej zálohy (umožňuje šetriť čas a veľkosť nahrávaných dát, ale môže mierne spomaliť proces obnovy, preto nie je žiaduce používať veľké hodnoty);
  • PGDATA – cesta k priečinku s údajmi vašej databázy (môžete zistiť vykonaním príkazu pg_lsclusters);
  • PGHOST – pripojenie k databáze, pri lokálnej zálohe je lepšie to robiť cez unix-socket ako v tomto príklade.

Ostatné parametre si môžete pozrieť v dokumentácii: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Nastavenie PostgreSQL

Aby archivátor vo vnútri databázy sám nahrával WAL-žurnály do cloudu a obnovoval sa z nich (v prípade potreby) – treba nastaviť niekoľko parametrov v konfiguračnom súbore /etc/postgresql/12/main/postgresql.conf. Lenže na začiatok musíte sa uistiť, že žiadne z nižšie uvedených nastavení nemajú priradené iné hodnoty, aby pri reštartovaní konfigurácie – SDB netrpela. 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 nastavovaných parametrov:

  • wal_level – koľko informácií písať do WAL žurnálov, „replica“ – písať všetko;
  • archive_mode – zapnutie nahrávania WAL-žurnálov pomocou príkazu z parametra archive_command;
  • archive_command – príkaz na archiváciu dokončeného WAL-žurnálu;
  • archive_timeout – archivácia žurnálov prebieha iba v prípade, že je dokončený, ale ak váš server má málo zmien/pridaní údajov do DB, má zmysel nastaviť tu limit v sekundách, po uplynutí ktorého sa príkaz na archiváciu vyvolá nútene (u mňa intenzívny zápis do databázy každú sekundu, preto som sa vzdal nastavenia tohto parametra v produkcii);
  • restore_command – príkaz na obnovenie WAL-žurnálu zo zálohy sa použije v prípade, že v „úplnej zálohe“ (base backup) budú chýbať posledné zmeny v DB.

Podrobnejšie o všetkých týchto parametroch si môžete prečítať v preklade oficiálnej dokumentácie: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Nastavenie rozvrhu zálohovania

Ako sa hovorí, najužitočnejší spôsob spúšťania je cron. Práve ten si nastavíme na vytváranie záloh. Začnime príkazom na vytvorenie úplného zálohovania: v wal-g je to spúšťací argument. backup-push. Ale najprv je lepšie spustiť tento príkaz ručne ako používateľ postgres, aby sme sa uistili, že všetko funguje správne (a že nie sú žiadne prístupové chyby):

#!/bin/bash

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

V spúšťacích argumentoch je uvedená cesta k adresáru s údajmi – pripomínam, že ju môžete zistiť vykonaním pg_lsclusters.

Ak prebehlo všetko bez chýb a údaje sa nahrali do úložiska S3, môžete ďalej nastaviť 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 spúšťa každý deň o 4:15 ráno.

Odstraňovanie starých záloh

Pravdepodobne nepotrebujete uchovávať všetky zálohy z druhohôr, preto bude užitočné pravidelne 'upratať' svoje úložisko (ako 'úplné zálohy', tak aj WAL záznamy). Urobíme to opäť cez cron úlohu:

#!/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 túto úlohu vykoná každý deň o 6:30 ráno, pričom odstráni všetko (úplné zálohy, delta a WAL) okrem kópií za posledných 10 dní, pričom aspoň jedna záloha z uvedeného dátumu zostane. do tak, aby akýkoľvek čas po bol zahrnutý do PITR.

Obnovenie zo zálohy

Nie je tajomstvom, že kľúčom k zdravej databáze je pravidelné obnovenie a kontrola integrity údajov. Ako sa obnoviť pomocou WAL-G – to vysvetlím v tejto časti, a o kontrolách budeme diskutovať neskôr.

Osobitne stojí za zmienku že na obnovenie v testovacom prostredí (všetko, čo nie je produkcia) – treba použiť Read Only účet v S3, aby sa náhodou neprepísali zálohy. V prípade WAL-G je potrebné zadať používateľovi S3 nasledujúce práva v Group Policy (Effect: Allow): s3:GetObject, s3:ListBucket, s3:GetBucketLocation. A samozrejme, nezabudnúť nastaviť archive_mode=off v konfiguračnom súbore postgresql.conf, aby sa vaša testovacia databáza nechcela ticho zálohovať.

Obnovenie sa vykonáva jednoduchým pohybom ruky s odstránením všetkých údajov PostgreSQL (vrátane používateľov), preto buďte prosím mimoriadne opatrní, keď budete spúšťať nasledujúce príkazy.

#!/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ú sledovať proces obnovy — nižšie je pripravený malý kúsok bash-magie, aby sa v prípade problémov pri obnove skript zastavil s nenulovým exit kódom. V tomto príklade sa vykonáva 120 kontrol s časovým limitom 5 sekúnd (celkovo 10 minút na obnovu), aby sa zistil, či sa oznamovací súbor odstránil (to by znamenalo, že obnova prebehla úspešne):

#!/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šnej obnove nezabudnite znovu spustiť všetky procesy (pgbouncer/monit a pod.).

Kontrola údajov po obnove

Je nevyhnutné skontrolovať integritu databázy po obnove, aby sa predišlo situácii s poškodenou/zlomenou zálohou. A je lepšie to robiť s každým vytvoreným archívom, ale kde a ako — to závisí len od vašej fantázie (môžete spúšťať samostatné servery na hodinovú platbu alebo vykonávať kontrolu v CI). Ale aspoň je potrebné kontrolovať údaje a indexy v databáze.

Na kontrolu údajov stačí prehnať ich cez dump, ale je lepšie, ak ste pri vytvorení databázy mali zapnuté kontrolné sumy (data checksums):

#!/bin/bash

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

Na kontrolu indexov existuje modul amcheck, sql-dotaz naň vezmeme z testov WAL-G a okolo to vybudujeme malú logiku:

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

Zhrňujúc

Vyjadrujem vďačnosť Andrejovi 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 končí. Dúfam, že som dokázal preniesť jednoduchosť nastavenia a obrovský potenciál pre využitie tohto nástroja vo vašej spoločnosti. Počul som veľa o WAL-G, ale nikdy nebolo času sadnúť si a pochopiť to. A po tom, ako som ho zaviedol u seba — vznikol z toho tento článok.

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

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster