WAL-G: varnostne kopije in obnovitev DBMS PostgreSQL

Že dolgo je znano, da izdelava varnostnih kopij v izpise SQL (z uporabo pg_dump ali pg_dumpall) ni dobra ideja. Za varnostno kopiranje DBMS PostgreSQL je bolje uporabiti ukaz pg_basebackup, ki naredi binarno kopijo dnevnikov WAL. Toda ko začnete preučevati celoten postopek ustvarjanja kopije in obnavljanja, boste razumeli, da morate napisati vsaj nekaj triciklov, da bo to delovalo in vam ne bo povzročalo bolečin zgoraj in spodaj. Za lajšanje trpljenja je bil razvit WAL-G.

WAL-G je orodje, napisano v Go za varnostno kopiranje in obnavljanje podatkovnih baz PostgreSQL (in nedavno MySQL/MariaDB, MongoDB in FoundationDB). Podpira delo s shrambo Amazon S3 (in analogi, na primer Yandex Object Storage), kot tudi Google Cloud Storage, Azure Storage, Swift Object Storage in preprosto z datotečnim sistemom. Celotna nastavitev se skrči na preproste korake, vendar zaradi dejstva, da so članki o tem raztreseni po internetu, ni popolnega priročnika, ki bi vseboval vse korake od začetka do konca (na Habréju je več objav, vendar je veliko točk tam zgrešenih).

WAL-G: varnostne kopije in obnovitev DBMS PostgreSQL

Ta članek je bil napisan predvsem zaradi sistematizacije mojega znanja. Nisem DBA in se nekje lahko izrazim v laičnem jeziku, zato so kakršnikoli popravki dobrodošli!

Ločeno ugotavljam, da je vse spodaj ustrezno in preizkušeno za PostgreSQL 12.3 na Ubuntu 18.04, vse ukaze je treba izvesti kot privilegiran uporabnik.

Namestitev

V času pisanja tega članka je stabilna različica WAL-G v0.2.15 (marec 2020). To bomo uporabili (če pa ga želite zgraditi sami iz glavne veje, potem ima repozitorij github vsa navodila za to). Za prenos in namestitev morate narediti:

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

Po tem morate najprej konfigurirati WAL-G in nato sam PostgreSQL.

Nastavitev WAL-G

Za primer shranjevanja varnostnih kopij bo uporabljen Amazon S3 (ker je bližje mojim strežnikom in je njegova uporaba zelo poceni). Za delo z njim potrebujete »s3 bucket« in ključe za dostop.

Vsi prejšnji članki o WAL-G so uporabljali konfiguracijo z uporabo spremenljivk okolja, toda s to izdajo lahko nastavitve najdete v .walg.json v domačem imeniku uporabnika postgres. Če ga želite ustvariti, zaženite naslednji 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

Naj na kratko razložim vse parametre:

  • WALG_S3_PREFIX – pot do vedra S3, kamor bodo naložene varnostne kopije (lahko v koren ali v mapo);
  • AWS_ACCESS_KEY_ID – ključ za dostop v S3 (v primeru obnovitve na testnem strežniku morajo imeti ti ključi pravilnik samo za branje! To je podrobneje opisano v razdelku o obnovitvi.);
  • AWS_SECRET_ACCESS_KEY – tajni ključ v shrambi S3;
  • WALG_COMPRESSION_METHOD – način stiskanja, bolje uporabiti Brotli (ker je to zlata sredina med končno velikostjo in hitrostjo stiskanja/dekompresije);
  • WALG_DELTA_MAX_STEPS – število »delt« pred ustvarjanjem popolne varnostne kopije (prihranijo čas in velikost prenesenih podatkov, vendar lahko nekoliko upočasnijo proces obnovitve, zato ni priporočljivo uporabljati velikih vrednosti);
  • PGDATA – pot do imenika s podatki vaše baze (lahko ugotovite tako, da zaženete ukaz pg_lsclusters);
  • PGHOST – povezovanje z bazo podatkov, z lokalno varnostno kopijo je bolje, da to storite prek vtičnice unix, kot je v tem primeru.

Ostale parametre najdete v dokumentaciji: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Konfiguriranje PostgreSQL

Da lahko arhivar znotraj baze podatkov naloži dnevnike WAL v oblak in jih iz njih obnovi (če je potrebno), morate v konfiguracijski datoteki nastaviti več parametrov /etc/postgresql/12/main/postgresql.conf. Samo za začetek morate se prepričatida nobena od spodnjih nastavitev ni nastavljena na druge vrednosti, tako da se ob ponovnem nalaganju konfiguracije DBMS ne zruši. Te parametre lahko dodate z:

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

Opis parametrov, ki jih je treba nastaviti:

  • wal_level – koliko informacij zapisati v dnevnike WAL, “replika” – zapisati vse;
  • arhivski_način – omogočite prenos dnevnikov WAL z ukazom iz parametra ukaz_arhiv;
  • ukaz_arhiv – ukaz za arhiviranje izpolnjenega dnevnika WAL;
  • časovna omejitev_arhiva – arhiviranje dnevnikov se izvede šele, ko je končano, če pa vaš strežnik spremeni/doda malo podatkov v bazo, potem je smiselno tukaj nastaviti omejitev v sekundah, po kateri se ukaz za arhiviranje kliče prisilno (Vsako sekundo intenzivno pišem v bazo, zato sem se odločil, da tega parametra v produkciji ne bom nastavljal);
  • obnovitveni_ukaz – ukaz za obnovitev dnevnika WAL iz varnostne kopije bo uporabljen, če v »polni varnostni kopiji« (osnovni varnostni kopiji) manjkajo zadnje spremembe v bazi podatkov.

Več o vseh teh parametrih si lahko preberete v prevodu uradne dokumentacije: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Nastavitev urnika varnostnega kopiranja

Karkoli lahko rečemo, najprimernejši način za zagon je cron. To bomo konfigurirali za ustvarjanje varnostnih kopij. Začnimo z ukazom za ustvarjanje popolne varnostne kopije: v wal-g je to argument za zagon backup-push. Toda najprej je bolje, da ta ukaz zaženete ročno od uporabnika postgres, da se prepričate, da je vse v redu (in da ni napak pri dostopu):

#!/bin/bash

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

Zagonski argumenti označujejo pot do podatkovnega imenika - spomnim vas, da jo lahko najdete z zagonom pg_lsclusters.

Če je vse potekalo brez napak in so bili podatki naloženi v shrambo S3, potem lahko konfigurirate občasni zagon v crontabu:

#!/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 tem primeru se postopek varnostnega kopiranja začne vsak dan ob 4.

Brisanje starih varnostnih kopij

Najverjetneje vam ni treba hraniti absolutno vseh varnostnih kopij iz mezozojske dobe, zato bo koristno občasno "počistiti" svoj prostor za shranjevanje (tako "polne varnostne kopije" kot dnevnike WAL). Vse to bomo naredili prek naloge 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 bo zagnal to nalogo vsak dan ob 6:30 zjutraj, pri čemer bo izbrisal vse (polne varnostne kopije, delte in WAL-je), razen kopij za zadnjih 10 dni, vendar bo pustil vsaj eno varnostno kopijo za določen datum, tako da katera koli točka po datumi so bili vključeni v PITR.

Obnavljanje iz varnostne kopije

Nobena skrivnost ni, da je ključ do zdrave zbirke podatkov občasno obnavljanje in preverjanje celovitosti podatkov v njej. V tem razdelku vam bom povedal, kako obnoviti z uporabo WAL-G, kasneje pa bomo govorili o pregledih.

Treba je opozoriti ločeno da morate za obnovitev v testnem okolju (vse, kar ni proizvodnja) uporabiti račun samo za branje v S3, da ne bi slučajno prepisali varnostnih kopij. V primeru WAL-G morate v pravilniku skupine nastaviti naslednje pravice za uporabnika S3 (Učinek: Dovoli): s3:GetObject, s3: ListBucket, s3:GetBucketLocation. In seveda ne pozabite nastaviti archive_mode=izklopljeno v datoteki z nastavitvami postgresql.conf, tako da vaša testna baza podatkov ne želi tiho varnostno kopirati.

Obnova se izvede z rahlim gibom roke brisanje vseh podatkov PostgreSQL (vključno z uporabniki), zato bodite zelo previdni, ko izvajate naslednje ukaze.

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

Za tiste, ki želijo preveriti postopek obnovitve, je spodaj pripravljen majhen košček bash magic, tako da se bo v primeru težav pri obnovitvi skript zrušil z izhodno kodo, ki ni ničelna. V tem primeru se izvede 120 preverjanj s časovno omejitvijo 5 sekund (skupaj 10 minut za obnovitev), da se ugotovi, ali je bila signalna datoteka izbrisana (to pomeni, da je bila obnovitev uspešna):

#!/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 uspešni obnovitvi ne pozabite znova zagnati vseh procesov (pgbouncer/monit itd.).

Preverjanje podatkov po obnovitvi

Nujno je treba preveriti celovitost baze podatkov po obnovitvi, da ne pride do situacije z zlomljeno/pokvarjeno varnostno kopijo. In bolje je, da to storite z vsakim ustvarjenim arhivom, kje in kako pa je odvisno samo od vaše domišljije (lahko dvignete posamezne strežnike vsako uro ali zaženete preverjanje v CI). Vendar pa je treba najmanj preveriti podatke in indekse v bazi podatkov.

Za preverjanje podatkov je dovolj, da jih poženete skozi dump, vendar je bolje, da imate pri ustvarjanju baze omogočene kontrolne vsote (kontrolne vsote podatkov):

#!/bin/bash

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

Za preverjanje indeksov - obstaja modul amcheck, vzemimo sql poizvedbo zanj iz WAL-G testi in zgradite malo logike okoli tega:

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

Povzemanje

Rad bi izrazil svojo hvaležnost Andreju Borodinu za njegovo pomoč pri pripravi publikacije in posebno zahvalo za njegov prispevek k razvoju WAL-G!

S tem se ta zapis zaključuje. Upam, da sem lahko predstavil enostavnost nastavitve in ogromen potencial za uporabo tega orodja v vašem podjetju. Veliko sem slišal o WAL-G, vendar nikoli nisem imel dovolj časa, da bi se usedel in ugotovil. In ko sem to izvedel doma, je ta članek prišel iz mene.

Ločeno je treba omeniti, da lahko WAL-G deluje tudi z naslednjimi DBMS:

Vir: www.habr.com

Dodaj komentar