Det har længe været kendt, at det ikke er den bedste idé at tage backups i SQL-dumps (ved hjælp af pg_dump eller pg_dumpall), da det ikke er en optimal metode. For at lave backup af PostgreSQL-databaser er det bedre at bruge kommandoen pg_basebackup, som tager en binær kopi af WAL-logfilerne. Men når du begynder at undersøge hele processen med at lave kopier og gendanne dem, vil du indse, at du skal skrive mindst et par tre-hjul cykler for at få det til at fungere uden at forårsage smerte både ovenfra og nedenfra. For at lette lidelserne blev WAL-G udviklet.
– dette er et værktøj skrevet i Go til backup og gendannelse af PostgreSQL databaser (og siden for nylig også MySQL/MariaDB, MongoDB og FoundationDB). Det understøtter arbejde med Amazon S3-lagring (og dets ækvivalenter, såsom Yandex Object Storage), samt Google Cloud Storage, Azure Storage, Swift Object Storage og almindelig filsystem. Hele opsætningen kan reduceres til enkle trin, men på grund af at artikler om det er spredt på internettet – findes der ingen komplet how-to manual, der inkluderer alle trin fra start til slut (der er flere indlæg på Habr, men mange aspekter er der blevet overset).

Denne artikel er skrevet primært for at systematisere mine egne viden. Jeg er ikke DBA, og nogle gange kan jeg udtrykke mig med et udviklerperspektiv, så alle rettelser er velkomne!
Jeg vil særligt bemærke, at alt det følgende er relevant og verificeret for PostgreSQL 12.3 på Ubuntu 18.04, alle kommandoer skal udføres af en privilegeret bruger.
Installation
På tidspunktet for skrivningen er den stabile version af WAL-G – . Den vil vi bruge (men hvis du ønsker selv at kompilere det fra master-grenen, så er der alle instruktioner tilgængelige i repoen på github). For at downloade og installere skal du udfø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/
Derefter skal du først konfigurere WAL-G og derefter PostgreSQL.
Opsætning af WAL-G
For eksempel vil Amazon S3 blive brugt til opbevaring af backups (fordi det er tættere på mine servere og dets brug er meget billigt). For at arbejde med det kræves en 's3-bucket' og adgangsnøgler.
I alle tidligere artikler om WAL-G blev konfigurationen udført ved hjælp af miljøvariabler, men fra denne version kan indstillingerne placeres i i hjemmemappen til brugeren postgres. For at oprette den udfører vi følgende bash-script:
#!/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
Lad mig forklare alle parametre:
- WALG_S3_PREFIX – sti til din S3-bucket, hvor backups vil blive gemt (kan være i roden eller i en mappe);
- AWS_ACCESS_KEY_ID – adgangsnøgle til S3 (i tilfælde af gendannelse på en testserver – disse nøgler skal have ReadOnly Policy! Mere om dette er forklaret i afsnittet om gendannelse.);
- AWS_SECRET_ACCESS_KEY – hemmelig nøgle i S3-lageret;
- WALG_COMPRESSION_METHOD – kompressionsmetode, det anbefales at bruge Brotli (da det er en god balance mellem slutstørrelse og komprimerings-/dekomprimeringshastighed);
- WALG_DELTA_MAX_STEPS – antal "deltaer" før oprettelse af et komplet backup (de sparer tid og størrelsen på de uploadede data, men kan forsinke gendannelsesprocessen en smule, så store værdier bør undgås);
- PGDATA – sti til mappen med data fra din database (kan findes ved at udføre kommandoen pg_lsclusters);
- PGHOST – forbindelse til databasen, ved lokal backup anbefales det at bruge unix-socket som i dette eksempel.
De andre parametre kan findes i dokumentationen: .
Konfiguration af PostgreSQL
For at arkiveren inden i databasen selv uploadede WAL-logfiler til skyen og gendanner fra dem (hvis nødvendigt) – nogle parametre skal angives i konfigurationsfilen /etc/postgresql/12/main/postgresql.conf. Men først skal du sikre dig, at ingen af de følgende indstillinger er sat til andre værdier, så databasesystemet ikke falder ved genstart af konfigurationen. Du kan tilføje disse parametre ved hjælp af:
#!/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 af de installerede parametre:
- wal_level – hvor meget information der skal skrives i WAL-logfilerne, "replica" – skrive alt;
- archive_mode – aktivering af upload af WAL-logfiler ved hjælp af kommandoen fra parameteren archive_command;
- archive_command – kommando til at arkivere den afsluttede WAL-log;
- archive_timeout – logføringen foregår kun når den er afsluttet, men hvis din server sjældent ændrer/tilføjer data til databasen, kan det give mening at sætte en grænse i sekunder, hvorefter arkiveringskommandoen vil blive kaldt tvunget (jeg har intensiv skrivning til databasen hvert sekund, derfor har jeg valgt ikke at sætte denne parameter i produktion.);
- restore_command – kommando til at gendanne WAL-log fra backupen, vil blive brugt hvis der mangler de seneste ændringer i databasen i "den komplette backup" (base backup).
Mere om alle disse parametre kan læses i oversættelsen af den officielle dokumentation: .
Indstilling af backupplan
Uanset hvordan man ser på det, er cron den mest praktiske måde at starte en proces på. Det er den, vi vil bruge til at oprette backups. Lad os starte med kommandoen til at oprette en fuld backup: i wal-g er dette startargumentet. backup-push. Men det er bedst at udføre denne kommando manuelt fra brugeren postgres først for at sikre, at alt fungerer korrekt (og der ikke er adgangen fejl):
#!/bin/bash
su - postgres -c '/usr/local/bin/wal-g backup-push /var/lib/postgresql/12/main'
I startargumenterne er stien til datadirectory'en angivet – jeg minder om, at den kan findes ved at udføre pg_lsclusters.
Hvis alt gik uden fejl og dataene er blevet uploadet til S3-lageret, kan du derefter planlægge en periodisk kørsel 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 eksempel starter backup-processen hver dag kl. 4:15.
Sletning af gamle sikkerhedskopier
Du har sandsynligvis ikke brug for at gemme alle sikkerhedskopier tilbage fra mesozoikum, så det vil være nyttigt at «rense» dit lager periodisk (både «fulde backups» og WAL-logfiler). Vi vil gøre dette også via en cron-opgave:
#!/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 udføre denne opgave hver dag kl. 6:30 om morgenen og slette alt (fulde backups, deltas og WAL'er) undtagen kopier fra de seneste 10 dage, men vil altid bevare mindst én backup fra den angivne dato, så enhver dato op til falder inden for PITR. efter Restaurering fra backup
Det er ingen hemmelighed, at nøglen til en sund database ligger i periodisk restaurering og kontrol af dataintegritet. Hvordan man gendanner med WAL-G vil jeg beskrive i dette afsnit, og vi vil tale om kontrollerne efterfølgende.
Det er værd at nævne
at for restaureringen på et testmiljø (alt hvad der ikke er produktion) – skal du bruge en Read Only-konto i S3 for at undgå utilsigtet at overskrive backups. I tilfælde af WAL-G skal du tildele S3-brugeren følgende rettigheder i Group Policy ( Effekt: Tillads3:GetObject): s3:ListBucket, s3:GetBucketLocation, . Og selvfølgelig, glem ikke at indstillearchive_mode=off i konfigurationsfilen , så din testdatabase ikke utilsigtet forsøger at tage backup i baggrunden. postgresql.confRestaureringen udføres med et let håndgreb med
sletningen af alle PostgreSQL-data (inklusive brugere), så vær venligst ekstremt forsigtig, når du kører de næste kommandoer. (в том числе пользователей), поэтому, пожалуйста, будьте предельно аккуратны когда будете запускать следующие команды.
#!/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 recovery process, here’s a small piece of bash magic prepared so that in case of recovery issues, the script will fail with a non-zero exit code. In this example, 120 checks are performed with a timeout of 5 seconds (a total of 10 minutes for recovery) to see if the signal file has been deleted (this would indicate that the recovery 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 recovery, don't forget to restart all processes (pgbouncer/monit, etc.).
Data verification after recovery
It is essential to check the integrity of the database after recovery to avoid situations with corrupted/incorrect backups. It's best to do this with each created archive, but where and how depends entirely on your imagination (you can spin up separate servers on an hourly basis or run checks in CI). But at a minimum, it’s necessary to check the data and indexes in the database.
To check the data, it’s enough to run them through a dump, but it’s better if checksums were enabled when creating the database ():
#!/bin/bash
if ! su - postgres -c 'pg_dumpall > /dev/null'
then
echo 'pg_dumpall failed'
exit 125
fi
To verify the indexes, there is a , let's take the SQL query from and 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
Sammenfatning
I would like to express my gratitude to Andrey Borodin for his assistance in preparing this publication and a special thanks for his contribution to the development of WAL-G!
This note has come to an end. I hope I was able to convey the ease of setup and the vast potential for this tool's application in your company. I have heard a lot about WAL-G, but never found the time to sit down and understand it. After implementing it myself, this article came out.
It is also worth noting that WAL-G can work with the following databases:
- ;
- ;
- ;
- And judging by the commits, several more are expected!
Kilde: habr.com
