De mult timp este cunoscut faptul că realizarea de backup-uri în SQL dump-uri (folosind pg_dump sau pg_dumpall) nu este cea mai bună idee. Pentru backup-ul bazelor de date PostgreSQL, este mai bine să folosești comanda pg_basebackup, care creează o copie binară a jurnalelor WAL. Dar atunci când începi să studiezi întregul proces de creare a unei copii și de restaurare, vei înțelege că trebuie să scrii măcar câteva biciclete cu trei roți pentru a face ca totul să funcționeze fără a-ți provoca dureri atât în partea de sus, cât și în partea de jos. Pentru a ușura suferința, a fost dezvoltat WAL-G.
– este un instrument scris în Go pentru backup și restaurare a bazelor de date PostgreSQL (dar, din recent, și pentru MySQL/MariaDB, MongoDB și FoundationDB). Acesta suportă lucrul cu stocările Amazon S3 (și similare, cum ar fi Yandex Object Storage), precum și Google Cloud Storage, Azure Storage, Swift Object Storage și pur și simplu cu sistemul de fișiere. Întreaga configurare se reduce la câțiva pași simpli, dar din cauza că articolele despre el sunt dispersate pe internet – nu există un manual complet care să includă toți pașii de la început și până la sfârșit (pe Habr sunt câteva postări, dar multe momente sunt omise).

Acest articol este scris în primul rând pentru a-mi sistematiza cunoștințele. Nu sunt DBA și, uneori, pot folosi un limbaj mai accesibil dezvoltatorilor, așa că sunt binevenite orice corecturi!
Subliniez că tot ce urmează este actual și verificat pentru PostgreSQL 12.3 pe Ubuntu 18.04, toate comenzile trebuie să fie executate de un utilizator privilegiat.
Instalare
La momentul scrierii acestui articol, versiunea stabilă a WAL-G este . Aceasta o vom folosi (dar dacă dorești să o compilezi singur din ramura master, în repository-ul de pe github există toate instrucțiunile necesare). Pentru descărcare și instalare, este necesar să executăm:
#!/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/
După aceasta, trebuie să configurăm mai întâi WAL-G și apoi PostgreSQL.
Configurarea WAL-G
Pentru exemplul de stocare a backup-urilor, va fi folosit Amazon S3 (deoarece este mai aproape de serverele mele și utilizarea sa este foarte ieftină). Pentru a lucra cu el, ai nevoie de un "bucket s3" și de chei de acces.
În toate articolele anterioare despre WAL-G, configurarea s-a realizat prin variabile de mediu, dar de la acest release, setările pot fi plasate în din directorul de acasă al utilizatorului postgres. Pentru a-l crea, să executăm următorul script bash:
#!/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
Voi explica puțin toate parametrii:
- WALG_S3_PREFIX – calea către bucketul dvs. S3 unde vor fi încărcate backup-urile (poate fi atât în rădăcină, cât și într-un folder);
- AWS_ACCESS_KEY_ID – cheia de acces în S3 (în cazul restaurării pe un server de test – aceste chei trebuie să aibă ReadOnly Policy! Detalii suplimentare sunt scrise în secțiunea despre restaurare);
- AWS_SECRET_ACCESS_KEY – cheia secretă în magazinul S3;
- WALG_COMPRESSION_METHOD – metoda de comprimare, se recomandă utilizarea Brotli (deoarece este un compromis optim între dimensiunea finală și viteza de compresie/decompresie);
- WALG_DELTA_MAX_STEPS – numărul de „delta” înainte de crearea unui backup complet (permite economisirea timpului și a dimensiunii datelor de încărcat, dar poate încetini puțin procesul de restaurare, prin urmare nu este recomandat să se folosească valori mari);
- PGDATA – calea către directorul cu datele bazei dvs. (puteți afla executând comanda pg_lsclusters);
- PGHOST – conexiunea la bază, pentru un backup local este mai bine să se facă prin unix-socket ca în acest exemplu.
Parametrii rămași pot fi vizualizați în documentație: .
Configurarea PostgreSQL
Pentru ca arhivatorul din interiorul bazei să încarce singur jurnalurile WAL în cloud și să se restaureze din ele (dacă este necesar) – trebuie să stabiliți câțiva parametri în fișierul de configurare /etc/postgresql/12/main/postgresql.conf. Doar că, la început trebuie să vă asigurați, că niciuna dintre setările de mai jos nu este setată pe alte valori, astfel încât la reaplicarea configurației – SGBD-ul să nu se oprească. Aceste parametri pot fi adăugați folosind:
#!/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
Descrierea parametrilor setați:
- wal_level – cât de multe informații să scriem în jurnalele WAL, „replica” – a scrie tot;
- archive_mode – activarea încărcării jurnalelor WAL folosind comanda din parametrul archive_command;
- archive_command – comanda pentru arhivarea jurnalului WAL finalizat;
- archive_timeout – arhivarea jurnalelor are loc doar atunci când este finalizată, dar dacă serverul dvs. modifică/adaugă puțină informație în Baza de Date, ar fi bine să setați o limită în secunde, după care comanda de arhivare va fi apelată forțat (eu am scriere intensivă în bază la fiecare secundă, așa că am renunțat la setarea acestui parametru în producție);
- restore_command – comanda de restaurare a jurnalului WAL din backup, va fi folosită în cazul în care în „backup-ul complet” (base backup) vor lipsi ultimele modificări din baza de date.
Pentru mai multe detalii despre fiecare dintre acești parametri, puteți citi traducerea documentației oficiale: .
Configurarea programului de backup
Indiferent cum o privești, cea mai convenabilă metodă de a rula este cron. Asta vom configura pentru a crea copii de rezervă. Să începem cu comanda de creare a unui backup complet: în wal-g, acesta este argumentul de pornire backup-push. Dar, mai întâi, este mai bine să execuți această comandă manual de la utilizatorul postgres, pentru a te asigura că totul este în regulă (și nu există erori de acces):
#!/bin/bash
su - postgres -c '/usr/local/bin/wal-g backup-push /var/lib/postgresql/12/main'
În argumentele de pornire este specificat calea către directorul cu date - reamintesc că acest lucru poate fi aflat efectuând pg_lsclusters.
Dacă totul a decurs fără erori și datele au fost încărcate în stocarea S3, atunci poți configura execuția periodică în 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
În acest exemplu, procesul de backup se lansează în fiecare zi la 4:15 dimineața.
Ștergerea copiilor de rezervă vechi
Probabil că nu ai nevoie să păstrezi toate copiile de rezervă din era mezozoică, așa că va fi util să curăți periodic stocarea ta (atât copiile de rezervă complete, cât și jurnalele WAL). Vom face asta tot printr-o sarcină cron:
#!/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 va executa această sarcină în fiecare zi la 6:30 dimineața, ștergând tot (copiile de rezervă complete, deltele și WAL-urile) cu excepția copiilor de rezervă mai vechi de 10 zile, dar va lăsa cel puțin o copie de rezervă la de la data specificată, astfel încât orice dată după să cadă în PITR.
Restaurarea din copia de rezervă
Nu este un secret pentru nimeni că secretul unei baze de date sănătoase este în restaurarea și verificarea periodică a integrității datelor. Cum să te restaurezi cu ajutorul WAL-G - voi explica în această secțiune, iar despre verificări vom discuta mai târziu.
Merită menționat în mod special că pentru restaurarea în medii de testare (tot ceea ce nu este production) - trebuie să folosești un cont Read Only în S3, pentru a nu suprascrie accidental copiile de rezervă. În cazul WAL-G, trebuie să atribui utilizatorului S3 următoarele permisiuni în Group Policy (Effect: Allow): s3:GetObject, s3:ListBucket, s3:GetBucketLocation. Și, bineînțeles, nu uita să setezi în prealabil archive_mode=off în fișierul de configurare postgresql.conf, pentru a nu permite ca baza ta de test să se salveze în liniște.
Restaurarea se face cu o simplă mișcare a mâinii cu ștergerea tuturor datelor PostgreSQL (inclusiv utilizatorii), așa că, te rog, fii extrem de atent când vei rula următoarele comenzi.
#!/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
Pentru cei care doresc să verifice procesul de recuperare — mai jos este pregătit un mic fragment de magică bash, astfel încât, în caz de probleme la recuperare, scriptul să se oprească cu un cod de ieșire non-zero. În acest exemplu se fac 120 de verificări cu un timeout de 5 secunde (în total 10 minute pentru recuperare), pentru a afla dacă fișierul semnal a fost șters (acest lucru va însemna că recuperarea a fost un succes):
#!/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
După recuperarea cu succes, nu uitați să reporniți toate procesele (pgbouncer/monit etc.).
Verificarea datelor după recuperare
Este absolut necesar să verificați integritatea bazei de date după recuperare, pentru a nu se ajunge la situații cu copii de rezervă corupte. Și este mai bine să faceți acest lucru cu fiecare arhivă creată, dar unde și cum — depinde doar de imaginația dumneavoastră (puteți să ridicați servere separate cu plată pe oră sau să rulați verificarea în CI). Cel puțin — trebuie să verificați datele și indecșii din baza de date.
Pentru a verifica datele, este suficient să le rulați printr-un dump, dar este mai bine ca atunci când creați baza să aveți activate sumele de control ():
#!/bin/bash
if ! su - postgres -c 'pg_dumpall > /dev/null'
then
echo 'pg_dumpall failed'
exit 125
fi
Pentru verificarea indecșilor — există , interogarea sql pe care o vom folosi este din și vom construi în jurul acesteia o mică logică:
#!/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
În concluzie
Mulțumesc lui Andrei Borodin pentru ajutorul oferit la pregătirea publicației și un mulțumesc special pentru contribuția sa la dezvoltarea WAL-G!
Această notă a ajuns la final. Sper că am reușit să transmit ușurința de configurare și potențialul enorm de aplicare al acestui instrument în compania dumneavoastră. Am auzit multe despre WAL-G, dar nu am avut timp să mă așez și să mă documentez. Iar după ce l-am implementat, a ieșit acest articol din mine.
De asemenea, merită menționat că WAL-G poate funcționa și cu următoarele SGBD-uri:
- ;
- ;
- ;
- Și, judecând după commituri – se așteaptă încă câteva!
Sursa: habr.com
