WAL-G: sikkerhetskopiering og gjenoppretting av PostgreSQL-databaser

Det har lenge vært kjent at det ikke er den beste ideen å lage sikkerhetskopier i SQL-dumper (ved hjelp av pg_dump eller pg_dumpall) – det finnes bedre alternativer. For sikkerhetskopiering av PostgreSQL-database, er det best å bruke kommandoen pg_basebackup, som lager et binært forøk av WAL-loggene. Når du begynner å studere hele prosessen med å lage sikkerhetskopier og gjenopprette, vil du forstå at det krever en god del tid og krefter for å få det til å fungere uten å påføre deg selv unødvendig stress. For å gjøre denne prosessen lettere, ble WAL-G utviklet.

WAL-G – et verktøy skrevet i Go for sikkerhetskopiering og gjenoppretting av PostgreSQL-databaser (, og fra nylig også MySQL/MariaDB, MongoDB og FoundationDB). Det støtter lagringstjenester som Amazon S3 (og tilsvarende, som Yandex Object Storage), samt Google Cloud Storage, Azure Storage, Swift Object Storage, og til og med lokale fildisker. All konfigurasjon kan gjøres med noen enkle trinn, men fordi artikler om det er spredt rundt omkring på internett, finnes det ikke en komplett manual som dekker alle trinn fra A til Å (det er flere innlegg på Habré, men mange aspekter er utelatt der).

WAL-G: sikkerhetskopiering og gjenoppretting av PostgreSQL-databaser

Artikkelen er skrevet først og fremst for å systematisere mine egne kunnskaper. Jeg er ikke DBA, så jeg kan noen ganger bruke et mer utviklervennlig språk, så alle korrigeringer er velkomne!

Jeg vil spesielt påpeke at alt nedenfor er relevant og verifisert for PostgreSQL 12.3 på Ubuntu 18.04, alle kommandoer skal utføres av en privilegert bruker.

Installasjon

Ved tidspunktet for skrivingen av denne artikkelen, var den stabile versjonen av WAL-G – v0.2.15 (mars 2020). Det er den vi skal bruke (, men hvis du vil bygge det selv fra master-branchen, har repositoryet på GitHub all nødvendig informasjon for dette). For nedlasting og installasjon må du utføre:

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

Deretter må du konfigurere først WAL-G, og deretter PostgreSQL selv.

Konfigurering av WAL-G

For eksempel vil Amazon S3 bli brukt som lagringssted for sikkerhetskopier (, fordi det er nærmere serverne mine og det er svært rimelig). For å bruke det, trenger du en «s3-bucket» og tilgangsnøkler.

I alle tidligere artikler om WAL-G ble konfigurasjonen gjort med miljøvariabler, men fra denne utgivelsen kan innstillingene plasseres i .walg.json-filen i hjemmemappen til brukeren postgres. For å opprette den, kan vi kjøre følgende 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

La meg forklare alle parametrene:

  • WALG_S3_PREFIX – stien til S3-bøtten din hvor backupene skal lagres (kan være enten i roten eller i en mappe);
  • AWS_ACCESS_KEY_ID – tilgangsnøkkel i S3 (ved gjenoppretting på testserver, må disse nøklene ha ReadOnly Policy! Mer informasjon finnes i avsnittet om gjenoppretting);
  • AWS_SECRET_ACCESS_KEY – hemmelig nøkkel i S3-lageret;
  • WALG_COMPRESSION_METHOD – kompresjonsmetode, det anbefales å bruke Brotli (siden dette er en god balanse mellom sluttstørrelse og hastighet for komprimering/ dekomprimering);
  • WALG_DELTA_MAX_STEPS – antall "deltaer" før full backup lages (de bidrar til å spare tid og størrelsen på lastede data, men kan forsinke gjenopprettingsprosessen litt, så det anbefales ikke å bruke store verdier);
  • PGDATA – stien til katalogen med dataene til databasen din (kan finnes ved å kjøre kommandoen pg_lsclusters);
  • PGHOST – tilkobling til databasen, ved lokal backup er det bedre å bruke unix-socket som i dette eksempelet.

De andre parameterne kan sees i dokumentasjonen: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Konfigurasjon av PostgreSQL

For at arkivprogrammet inni databasen selv skal laste opp WAL-loggene til skyen og gjenopprette fra dem (om nødvendig) – må flere parametere settes i konfigurasjonsfilen /etc/postgresql/12/main/postgresql.conf. Men først må du sørge for, at ingen av de nedenfor nevnte innstillingene er satt til andre verdier, slik at DBMS ikke krasjer ved omstart av konfigurasjonen. Disse parameterne kan legges til med:

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

Beskrivelse av oppsatte parametere:

  • wal_level – hvor mye informasjon som skal skrives til WAL-loggene, «replica» – skriv alt;
  • archive_mode – aktivering av opplasting av WAL-loggene ved å bruke kommandoen fra parameteren archive_command;
  • archive_command – kommando for arkivering av fullført WAL-logg;
  • archive_timeout – arkivering av loggene finner kun sted når de er fullført, men hvis serveren din endrer/legger til lite data i DB, kan det være lurt å sette en grense her i sekunder, etter utløpet av hvilken arkiveringskommandoen vil bli kalt tvingende (jeg har intensiv skriving i databasen hvert sekund, så jeg droppet å sette denne parameteren i produksjon);
  • restore_command – kommando for gjenoppretting av WAL-logg fra backup, blir brukt dersom det mangler de siste endringene i DB i "full backup" (base backup).

For mer informasjon om alle disse parameterne kan du lese oversettelsen av den offisielle dokumentasjonen: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Konfigurasjon av sikkerhetskopieringsplan

Uansett hvordan du ser på det, er den mest praktiske måten å kjøre en oppgave på cron. Det er nettopp dette vi skal konfigurere for å opprette sikkerhetskopier. La oss begynne med kommandoen for å lage en full sikkerhetskopi: i wal-g er dette oppstartsargumentet. backup-push. Men for å begynne med, er det best å kjøre denne kommandoen manuelt som brukeren postgres, for å være sikker på at alt er i orden (og det ikke er noen tilgangsfeil):

#!/bin/bash

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

I oppstartsargumentene er stien til katalogen med data oppgitt – jeg minner om at du kan finne den ved å kjøre pg_lsclusters.

Hvis alt gikk uten feil og data ble lastet opp til S3-lageret, kan du deretter sette opp periodisk kjøring i 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

I dette eksempelet blir sikkerhetskopieringsprosessen kjørt hver dag kl. 04:15.

Fjerning av gamle sikkerhetskopier

Du trenger sannsynligvis ikke å lagre alle sikkerhetskopier helt tilbake fra mesozoikum, så det vil være nyttig å jevnlig "rense" lageret ditt (både "fulle sikkerhetskopier" og WAL-loggene). Vi skal gjøre dette også via en cron-oppgave:

#!/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 vil kjøre denne oppgaven hver dag kl. 06:30, og fjerne alt (fulle sikkerhetskopier, deltas og WAL-er) unntatt kopiene fra de siste 10 dagene, men vil beholde minst én sikkerhetskopi til fra den angitte datoen, slik at enhver dato etter blir inkludert i PITR.

Gendannelse fra sikkerhedskopi

Det er ingen hemmelighet at nøkkelen til en sunn database ligger i periodisk gjenoppretting og kontroll av dataintegriteten. Hvordan man gjenoppretter med WAL-G – vil jeg forklare i dette avsnittet, og vi vil snakke om kontroller etterpå.

Det er verdt å merke seg at for gjenoppretting i et testmiljø (alt som ikke er produksjon) – må du bruke en Read Only-konto i S3, for å unngå å overskrive sikkerhetskopiene ved et uhell. I tilfelle av WAL-G må du tildele S3-brukeren følgende rettigheter i Group Policy (Effect: Allow): s3:GetObject, s3:ListBucket, s3:GetBucketLocation. Og, selvfølgelig, ikke glem å sette archive_mode=off i konfigurasjonsfilen postgresql.conf, slik at testdatabasen din ikke av ren refleks prøver å ta sikkerhetskopi i bakgrunnen.

Gjenopprettingen skjer med en enkel håndbevegelse ved å fjerne all PostgreSQL-data (inkludert brukere), så vær vennlig å være ytterst forsiktig når du kjører de følgende kommandoene.

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

For those who want to check the restoration process, a small piece of bash magic is prepared below, so that in case of problems during restoration, the script will exit with a non-zero exit code. In this example, 120 checks are made with a timeout of 5 seconds (10 minutes in total for restoration) to find out if the signal file has been deleted (this will indicate that the restoration was successful):

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

After a successful restoration, don't forget to restart all processes (pgbouncer/monit, etc.).

Data verification after restoration

It's essential to check the integrity of the database after restoration to avoid situations with corrupted or incorrect backups. Ideally, this should be done with every created archive, but how and where depends only on your imagination (you can spin up separate servers on an hourly basis or run checks in CI). But at the very least, it is necessary to verify the data and indexes in the database.

To verify the data, it is sufficient to run it through a dump, but it is better if checksums were enabled when creating the database (data checksums):

#!/bin/bash

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

For checking indexes, there is a module amcheck, the SQL query for it can be taken from WAL-G tests and we will build a small logic around it:

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

To summarize

I would like to thank Andrey Borodin for his help in preparing the publication and a special thanks for his contribution to the development of WAL-G!

This note has now come to an end. I hope I have conveyed the ease of setup and the enormous potential for using this tool in your company. I have heard a lot about WAL-G, but I never found the time to sit down and figure it out. After I implemented it myself, this article came out of me.

It is also worth noting that WAL-G can work with the following DBMS:

Kilde: habr.com

Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere 🔥 Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster