WAL-G: zálohy a obnova databází PostgreSQL

Je již dlouho známo, že provádět zálohy do SQL dumpů (použitím pg_dump nebo pg_dumpall) není nejlepší nápad. Pro zálohování databází PostgreSQL je lepší použít příkaz pg_basebackup, který vytváří binární kopii WAL žurnálů. Ale jakmile začnete studovat celý proces vytváření kopií a obnovy, uvědomíte si, že je potřeba napsat alespoň několik tříkolových bicyklů, aby to všechno fungovalo a nezpůsobovalo vám bolest jak nahoře, tak dole. Aby sloužil k usnadnění utrpení, byl vyvinut WAL-G.

WAL-G – je nástroj napsaný v Go pro zálohování a obnovu databází PostgreSQL (a od nedávné doby také MySQL/MariaDB, MongoDB a FoundationDB). Podporuje práci s úložišti Amazon S3 (i s analogiemi, jako je Yandex Object Storage), a také Google Cloud Storage, Azure Storage, Swift Object Storage a jednoduše s filesystémem. Všechna nastavení se dají shrnout do několika jednoduchých kroků, avšak vzhledem k tomu, že články o něm jsou rozptýlené po internetu – neexistuje kompletní how-to manuál, který by zahrnoval všechny kroky od A do Z (na Habru jsou nějaké příspěvky, ale mnohé aspekty tam jsou opomenuty).

WAL-G: zálohy a obnova databází PostgreSQL

Tento článek je napsán především proto, aby systematizoval mé znalosti. Nejsem DBA a někde se mohu vyjadřovat laicky, proto vítám jakákoli opravná poznámky!

Zvlášť bych chtěl zdůraznit, že všechno níže uvedené je aktuální a ověřené pro PostgreSQL 12.3 na Ubuntu 18.04, všechny příkazy by měly být vykonávány s privilegovaným uživatelským účtem.

Instalace

V okamžiku psaní tohoto článku je stabilní verze WAL-G – v0.2.15 (březen 2020). Tu budeme použivat (ale pokud se rozhodnete ho sestavit sami z master větve, v repozitáři na githubu najdete všechny instrukce k tomu). Pro stažení a instalaci je potřeba 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é je potřeba nakonfigurovat nejprve WAL-G a potom samotný PostgreSQL.

Konfigurace WAL-G

Pro příklad uchovávání záloh budeme používat Amazon S3 (protože je blíž k mým serverům a jeho použití je velmi levné). K práci s ním potřebujete "s3-bucket" a přístupové klíče.

Ve všech předchozích článcích o WAL-G bylo použito konfigurování pomocí proměnných prostředí, ale v této verzi je možné umístit nastavení do .walg.json souboru v domovské složce uživatele postgres. Pro jeho vytvoření vykonáme následující 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 osvětlím všechny parametry:

  • WALG_S3_PREFIX – cesta k vašemu S3 bucketu, kam budou ukládány zálohy (může být jak v kořenovém adresáři, tak ve složce);
  • AWS_ACCESS_KEY_ID – přístupový klíč k S3 (v případě obnovení na testovacím serveru – tato klíče by měly mít politiku ReadOnly! Podrobnosti jsou uvedeny v sekci o obnově);
  • AWS_SECRET_ACCESS_KEY – tajný klíč ve S3 úložišti;
  • WALG_COMPRESSION_METHOD – metoda komprese, doporučuje se používat Brotli (protože je to zlatá střední cesta mezi konečnou velikostí a rychlostí komprese/dekomprese);
  • WALG_DELTA_MAX_STEPS – počet "delta" do vytvoření plné zálohy (šetří čas a velikost nahrávaných dat, ale může mírně zpomalit proces obnovy, proto není doporučeno používat vysoké hodnoty);
  • PGDATA – cesta k adresáři s daty vaší databáze (můžete zjistit provedením příkazu pg_lsclusters);
  • PGHOST – připojení k databázi, při lokální záloze je lepší používat unix-socket, jak je v tomto příkladu.

Ostatní parametry můžete vidět v dokumentaci: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Nastavení PostgreSQL

Aby archivátor uvnitř databáze sám nahrával WAL záznamy do cloudu a mohl se z nich obnovit (v případě potřeby) – je nutné nastavit několik parametrů v konfiguračním souboru /etc/postgresql/12/main/postgresql.conf. Nejprve se ale musíte ujistit, že žádné z níže uvedených nastavení nejsou nastaveny na jiné hodnoty, aby při restartu konfigurace – databázový systém nespadl. 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 nastavovaných parametrů:

  • wal_level – kolik informací se zapisuje do WAL záznamů, „replica“ – zapisovat vše;
  • archive_mode – povolení nahrávání WAL záznamů pomocí příkazu z parametru archive_command;
  • archive_command – příkaz pro archivaci dokončeného WAL záznamu;
  • archive_timeout – archivace záznamů se provádí pouze tehdy, když je dokončen, ale pokud váš server má málo změn/přidání dat do databáze, má smysl nastavit zde limit v sekundách, po jehož uplynutí bude příkaz pro archivaci volán nuceně (mám intenzivní zápis do databáze každou sekundu, takže jsem se vzdal nastavení této hodnoty v produkčním prostředí);
  • restore_command – příkaz pro obnovení WAL záznamu ze zálohy, bude použit v případě, že v „plné záloze“ (base backup) bude chybět poslední změny v databázi.

Podrobnosti 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í

Jak se říká, nejpohodlnějším způsobem spuštění je cron. Ten nastavíme pro vytváření záloh. Začneme příkazem pro vytvoření úplné zálohy: v wal-g je to spouštěcí argument. backup-push. Ale nejprve je lepší provést tento příkaz ručně jako uživatel postgres, abyste se ujistili, že vše funguje správně (a že nejsou žádné chyby přístupu):

#!/bin/bash

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

Ve spouštěcích argumentech je uvedena cesta k adresáři s daty – připomínám, že ji lze zjistit provedením pg_lsclusters.

Pokud vše proběhlo bez chyb a data byla nahrána do úložiště S3, můžete dále nastavit periodické 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 se proces zálohy spouští každý den ve 4:15 ráno.

Odstranění starých záloh

Pravděpodobně nepotřebujete uchovávat všechny zálohy odmezozoické éry, proto bude užitečné občas "uklidit" vaše úložiště (jak "úplné zálohy", tak i WAL deníky). To všechno uděláme také pomocí úlohy cronu:

#!/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 tuto úlohu provede každý den v 6:30 ráno, přičemž odstraní vše (úplné zálohy, delta a WAL) kromě kopií za posledních 10 dní, avšak ponechá alespoň jednu zálohu do uvedeného data, aby jakýkoli bod po data spadal do PITR.

Obnova ze zálohy

Je známo, že klíčem k zdravé databázi je pravidelná obnova a kontrola integrity dat uvnitř. Jak se obnovit pomocí WAL-G – popíšu v této části, a o kontrolách budeme mluvit později.

Je dobré poznamenat že pro obnovu na testovacím prostředí (cokoliv, co není produkci) – je třeba použít účet Read Only v S3, abyste náhodou nepřepsali zálohy. V případě WAL-G je potřeba přidělit uživateli S3 následující práva v Group Policy (Effect: Allow): s3:GetObject, s3:ListBucket, s3:GetBucketLocation. A samozřejmě nezapomeňte nastavit archive_mode=off ve konfiguračním souboru postgresql.conf, aby se vaše testovací databáze náhodou nesnažila tiše zálohovat.

Obnova se provádí snadným pohybem ruky s odstraněním všech dat PostgreSQL (včetně uživatelů), proto prosím buďte maximálně opatrní při spuš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í kontrolovat proces obnovy – níže je připravený malý kousek bash magie, aby v případě problémů s obnovou – skript skončil s nenulovým exit kódem. V tomto příkladu se provádí 120 kontrol s časovým limitem 5 sekund (celkem 10 minut na obnovu), abychom zjistili, zda byl signální soubor odstraněn (to by znamenalo, že obnova proběhla ú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 znovu spustit všechny procesy (pgbouncer/monit atd.).

Kontrola dat po obnově

Je nutné zkontrolovat integritu databáze po obnově, aby se předešlo situaci s poškozenou/křivou zálohou. A je lepší to dělat u každého vytvořeného archivu, ale kde a jak – záleží pouze na vaší fantazii (můžete zvedat zvláštní servery na hodinové sazbě nebo spouštět kontrolu v CI). Ale minimálně – je nutné kontrolovat data a indexy v databázi.

Pro kontrolu dat stačí je projít dumpem, ale bylo by lepší, aby při vytváření databáze byly zapnuty kontrolní součty (data checksums):

#!/bin/bash

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

Pro kontrolu indexů – existuje modul amcheck, sql dotaz k němu vezmeme z testů WAL-G a kolem toho postavíme malou 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

Shrnuto

Vyjadřuji vděčnost Andrey Borodinovi za pomoc při přípravě publikace a zvláštní díky za jeho přínos při vývoji WAL-G!

Na tomto bodě tato poznámka dospěla ke konci. Doufám, že jsem dokázal přenést jednoduchost nastavení a obrovský potenciál pro aplikaci tohoto nástroje ve vaší společnosti. O WAL-G jsem hodně slyšel, ale nikdy jsem neměl čas se posadit a s tím se seznámit. A poté, co jsem ho implementoval, ze mě vznikl tento článek.

Je třeba poznamenat, že WAL-G také může fungovat s následujícími DBMS:

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster