WAL-G: säkerhetskopiering och återställning av PostgreSQL DBMS

Det har länge varit känt att göra säkerhetskopior till SQL-dumpar (med pg_dump eller pg_dumpall) är ingen bra idé. För att säkerhetskopiera PostgreSQL DBMS är det bättre att använda kommandot pg_basebackup, som gör en binär kopia av WAL-loggar. Men när du börjar studera hela processen med att skapa en kopia och återställa, kommer du att förstå att du måste skriva minst ett par trehjulingar för att detta ska fungera och inte orsaka smärta både ovanför och under. För att lindra lidande utvecklades WAL-G.

WAL-G är ett verktyg skrivet i Go för att säkerhetskopiera och återställa PostgreSQL-databaser (och på senare tid MySQL/MariaDB, MongoDB och FoundationDB). Den stöder arbete med Amazon S3-lagring (och analoger, till exempel Yandex Object Storage), samt Google Cloud Storage, Azure Storage, Swift Object Storage och helt enkelt med filsystemet. Hela installationen handlar om enkla steg, men på grund av det faktum att artiklar om det är utspridda över Internet, finns det ingen komplett instruktionsmanual som skulle innehålla alla steg från början till slut (det finns flera inlägg på Habré, men många poäng saknas där).

WAL-G: säkerhetskopiering och återställning av PostgreSQL DBMS

Den här artikeln skrevs främst för att systematisera min kunskap. Jag är inte en DBA och jag kan uttrycka mig på lekmannaspråk någonstans, så alla korrigeringar är välkomna!

Separat noterar jag att allt nedan är relevant och testat för PostgreSQL 12.3 på Ubuntu 18.04, alla kommandon måste köras som en privilegierad användare.

Installation

Vid tidpunkten för att skriva denna artikel är den stabila versionen av WAL-G v0.2.15 (mars 2020). Detta är vad vi kommer att använda (men om du vill bygga det själv från mastergrenen, så har github-förvaret alla instruktioner för detta). För att ladda ner och installera måste du göra:

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

Efter detta måste du först konfigurera WAL-G och sedan PostgreSQL själv.

Konfigurera WAL-G

För ett exempel på att lagra säkerhetskopior kommer Amazon S3 att användas (eftersom det är närmare mina servrar och det är väldigt billigt att använda det). För att arbeta med det behöver du en "s3 hink" och åtkomstnycklar.

Alla tidigare artiklar om WAL-G använde konfiguration med miljövariabler, men med den här utgåvan kan inställningarna placeras i .walg.json-fil i hemkatalogen för postgres-användaren. För att skapa det, kör följande 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

Låt mig förklara lite om alla parametrar:

  • WALG_S3_PREFIX – sökvägen till din S3-bucket där säkerhetskopior kommer att laddas upp (du kan antingen till roten eller till en mapp);
  • AWS_ACCESS_KEY_ID – åtkomstnyckel i S3 (i händelse av återställning på en testserver måste dessa nycklar ha ReadOnly Policy! Detta beskrivs mer i detalj i avsnittet om återhämtning.);
  • AWS_SECRET_ACCESS_KEY – hemlig nyckel i S3-lagring;
  • WALG_COMPRESSION_METHOD – kompressionsmetod, det är bättre att använda Brotli (eftersom detta är den gyllene medelvägen mellan den slutliga storleken och kompressions-/dekompressionshastigheten);
  • WALG_DELTA_MAX_STEPS – antalet "deltas" innan du skapar en fullständig säkerhetskopia (de sparar tid och storleken på nedladdade data, men kan sakta ner återställningsprocessen något, så det är inte tillrådligt att använda stora värden);
  • PGDATA – sökväg till katalogen med dina databasdata (du kan ta reda på det genom att köra kommandot pg_lskluster);
  • PGHOST – ansluta till databasen, med en lokal backup är det bättre att göra det via en unix-socket som i detta exempel.

Andra parametrar finns i dokumentationen: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Konfigurera PostgreSQL

För att arkiveraren i databasen ska ladda upp WAL-loggar till molnet och återställa dem från dem (om det behövs), måste du ställa in flera parametrar i konfigurationsfilen /etc/postgresql/12/main/postgresql.conf. Bara till att börja med du måste se tillatt ingen av inställningarna nedan är inställda på några andra värden, så att DBMS inte kraschar när konfigurationen laddas om. Du kan lägga till dessa parametrar 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

Beskrivning av parametrarna som ska ställas in:

  • wal_level – hur mycket information som ska skrivas i WAL-loggar, “replika” – skriv allt;
  • arkivläge – aktivera nedladdning av WAL-loggar med kommandot från parametern arkiv_kommando;
  • arkiv_kommando – kommando för att arkivera en färdig WAL-logg;
  • arkiv_timeout – arkivering av loggar utförs först när den är klar, men om din server ändrar/lägger till lite data till databasen, är det vettigt att sätta en gräns här i sekunder, varefter arkiveringskommandot kommer att anropas med tvång (Jag skriver intensivt till databasen varje sekund, så jag bestämde mig för att inte ställa in denna parameter i produktionen);
  • restore_command – kommandot för att återställa WAL-loggen från en säkerhetskopia kommer att användas om "fullständig säkerhetskopia" (basbackup) saknar de senaste ändringarna i databasen.

Du kan läsa mer om alla dessa parametrar i översättningen av den officiella dokumentationen: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Skapa ett backupschema

Vad man än kan säga, är det bekvämaste sättet att köra det cron. Detta är vad vi kommer att konfigurera för att skapa säkerhetskopior. Låt oss börja med kommandot för att skapa en fullständig säkerhetskopia: i wal-g är detta startargumentet backup-push. Men först är det bättre att köra det här kommandot manuellt från postgres-användaren för att se till att allt är bra (och det finns inga åtkomstfel):

#!/bin/bash

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

Startargumenten indikerar sökvägen till datakatalogen - jag påminner dig om att du kan ta reda på det genom att köra pg_lskluster.

Om allt gick utan fel och data laddades in i S3-lagring, kan du sedan konfigurera periodisk start 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 det här exemplet startar säkerhetskopieringsprocessen varje dag klockan 4:15.

Ta bort gamla säkerhetskopior

Troligtvis behöver du inte behålla absolut alla säkerhetskopior från mesozoiken, så det kommer att vara användbart att regelbundet "städa upp" din lagring (både "fullständiga säkerhetskopior" och WAL-loggar). Vi kommer att göra allt detta genom en cron-uppgift:

#!/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 kommer att köra den här uppgiften varje dag kl. 6 och raderar allt (fullständiga säkerhetskopior, delta och WAL) utom kopior för de senaste 30 dagarna, men lämnar minst en säkerhetskopia до angivet datum så att någon punkt efter datum ingick i PITR.

Återställer från en säkerhetskopia

Det är ingen hemlighet att nyckeln till en hälsosam databas är periodisk återställning och verifiering av integriteten hos data inuti. Jag kommer att berätta hur du återställer med WAL-G i det här avsnittet, och vi kommer att prata om kontroller senare.

Separat är det värt att notera att för att återställa i en testmiljö (allt som inte är produktion) behöver man använda ett Read Only-konto i S3 för att inte av misstag skriva över säkerhetskopior. I fallet med WAL-G måste du ställa in följande rättigheter för S3-användaren i grupprincip (Effekt: Tillåt): s3:GetObject, s3:ListBucket, s3:GetBucketLocation. Och, naturligtvis, glöm inte att ställa in archive_mode=av i inställningsfilen postgresql.conf, så att din testdatabas inte vill säkerhetskopieras tyst.

Restaurering utförs med en lätt rörelse av handen radera alla PostgreSQL-data (inklusive användare), så var extremt försiktig när du kör följande kommandon.

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

För den som vill kontrollera återställningsprocessen har en liten bit bash-magi förberetts nedan, så att vid problem med återställningen kommer skriptet att krascha med en exit-kod som inte är noll. I det här exemplet görs 120 kontroller med en timeout på 5 sekunder (totalt 10 minuter för återställning) för att ta reda på om signalfilen raderades (detta betyder att återställningen lyckades):

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

Efter framgångsrik återställning, glöm inte att starta alla processer tillbaka (pgbouncer/monit, etc.).

Kontrollerar data efter återställning

Det är absolut nödvändigt att kontrollera databasens integritet efter återställning, så att en situation med en trasig/skev säkerhetskopia inte uppstår. Och det är bättre att göra detta med varje skapat arkiv, men var och hur beror bara på din fantasi (du kan höja individuella servrar på timbasis eller köra en check i CI). Men som ett minimum är det nödvändigt att kontrollera data och index i databasen.

För att kontrollera data räcker det att köra den genom en dump, men det är bättre att när du skapar databasen har du kontrollsummor aktiverade (datakontrollsummor):

#!/bin/bash

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

För att kontrollera index - finns amcheck-modul, låt oss ta sql-frågan för det från WAL-G-tester och bygg lite logik kring det:

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

För att sammanfatta

Jag vill uttrycka min tacksamhet till Andrey Borodin för hans hjälp med att förbereda publikationen och särskilt tack för hans bidrag till utvecklingen av WAL-G!

Detta avslutar denna anteckning. Jag hoppas att jag kunde förmedla den enkla installationen och den enorma potentialen för att använda detta verktyg i ditt företag. Jag hörde mycket om WAL-G, men hade aldrig tillräckligt med tid att sätta mig ner och ta reda på det. Och efter att jag implementerade det hemma kom den här artikeln ur mig.

Separat är det värt att notera att WAL-G också kan fungera med följande DBMS:

Källa: will.com

Lägg en kommentar