WAL-G: kopie zapasowe i odzyskiwanie systemu PostgreSQL DBMS

Od dawna wiadomo, że tworzenie kopii zapasowych w zrzutach SQL (przy użyciu pg_dump lub pg_dumpall) to nie jest dobry pomysł. Aby wykonać kopię zapasową systemu DBMS PostgreSQL, lepiej użyć polecenia pg_basebackup, który tworzy binarną kopię dzienników WAL. Ale kiedy zaczniesz studiować cały proces tworzenia kopii i przywracania, zrozumiesz, że musisz napisać co najmniej kilka trójkołowców, aby to zadziałało i nie powodowało bólu zarówno u góry, jak i u dołu. Aby złagodzić cierpienie, opracowano WAL-G.

WAL-G to narzędzie napisane w Go do tworzenia kopii zapasowych i przywracania baz danych PostgreSQL (a ostatnio MySQL/MariaDB, MongoDB i FoundationDB). Obsługuje pracę z pamięcią masową Amazon S3 (i analogami, na przykład Yandex Object Storage), a także Google Cloud Storage, Azure Storage, Swift Object Storage i po prostu z systemem plików. Cała konfiguracja sprowadza się do prostych kroków, jednak w związku z tym, że artykuły na ten temat krążą po Internecie, nie ma kompletnej instrukcji, która obejmowałaby wszystkie kroki od początku do końca (na Habré jest kilka postów, m.in. ale brakuje tam wielu punktów).

WAL-G: kopie zapasowe i odzyskiwanie systemu PostgreSQL DBMS

Artykuł ten powstał przede wszystkim w celu usystematyzowania mojej wiedzy. Nie jestem administratorem baz danych i mogę gdzieś wyrazić się laickim językiem, więc wszelkie poprawki są mile widziane!

Osobno zauważam, że wszystko poniżej jest istotne i przetestowane dla PostgreSQL 12.3 na Ubuntu 18.04, wszystkie polecenia muszą zostać wykonane jako użytkownik uprzywilejowany.

Instalacja

W chwili pisania tego artykułu stabilną wersją WAL-G jest wersja 0.2.15 (marzec 2020). Tego właśnie użyjemy (ale jeśli chcesz zbudować go samodzielnie z gałęzi master, repozytorium Github zawiera wszystkie instrukcje do tego). Aby pobrać i zainstalować, musisz wykonać:

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

Następnie musisz najpierw skonfigurować WAL-G, a następnie sam PostgreSQL.

Konfigurowanie WAL-G

Jako przykład przechowywania kopii zapasowych zostanie użyty Amazon S3 (ponieważ jest bliżej moich serwerów i jego użytkowanie jest bardzo tanie). Aby z nim pracować, potrzebujesz „wiadra s3” i kluczy dostępu.

Wszystkie poprzednie artykuły na temat WAL-G wykorzystywały konfigurację przy użyciu zmiennych środowiskowych, ale w tej wersji ustawienia można zlokalizować plik .walg.json w katalogu domowym użytkownika Postgres. Aby go utworzyć, uruchom następujący skrypt 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

Pozwólcie, że wyjaśnię trochę o wszystkich parametrach:

  • WALG_S3_PREFIX – ścieżkę do Twojego segmentu S3, do którego będą przesyłane kopie zapasowe (możesz albo do katalogu głównego, albo do folderu);
  • AWS_ACCESS_KEY_ID – klucz dostępu w S3 (w przypadku odzyskiwania na serwerze testowym klucze te muszą mieć politykę ReadOnly! Zostało to opisane bardziej szczegółowo w części dotyczącej odzyskiwania.);
  • AWS_SECRET_ACCESS_KEY – tajny klucz do magazynu S3;
  • WALG_COMPRESSION_METHOD – metoda kompresji, lepiej zastosować Brotli (ponieważ jest to złoty środek pomiędzy ostatecznym rozmiarem a szybkością kompresji/dekompresji);
  • WALG_DELTA_MAX_STEPS – ilość „delt” przed utworzeniem pełnej kopii zapasowej (oszczędzają czas i rozmiar pobieranych danych, ale mogą nieco spowolnić proces odzyskiwania, dlatego nie zaleca się stosowania dużych wartości);
  • PGDANE – ścieżka do katalogu z danymi Twojej bazy danych (możesz się tego dowiedzieć, uruchamiając polecenie pg_lsclusters);
  • PDUCH – łączenie się z bazą danych, przy kopii zapasowej lokalnej lepiej to zrobić poprzez gniazdo unixowe jak w tym przykładzie.

Pozostałe parametry można znaleźć w dokumentacji: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Konfigurowanie PostgreSQL

Aby archiwizator znajdujący się w bazie danych mógł wgrać logi WAL do chmury i przywrócić je z nich (jeśli zajdzie taka potrzeba), należy ustawić kilka parametrów w pliku konfiguracyjnym /etc/postgresql/12/main/postgresql.conf. Tylko na początek musisz się upewnićaby żadne z poniższych ustawień nie zostało ustawione na inną wartość, tak aby po ponownym załadowaniu konfiguracji system DBMS nie uległ awarii. Możesz dodać te parametry za pomocą:

#!/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 parametrów do ustawienia:

  • poziom_wal – ile informacji zapisać w logach WAL, „replika” – napisz wszystko;
  • tryb_archiwum – włączyć pobieranie logów WAL za pomocą polecenia z parametru archiwum_polecenie;
  • archiwum_polecenie – polecenie archiwizacji wypełnionego logu WAL;
  • limit czasu_archiwum – archiwizacja logów odbywa się dopiero po jej zakończeniu, jednak jeśli Twój serwer zmieni/doda niewielką ilość danych do bazy, to warto ustawić tutaj limit w sekundach, po którym zostanie wymuszona komenda archiwizacji (Co sekundę intensywnie piszę do bazy, dlatego zdecydowałem się nie ustawiać tego parametru na produkcji);
  • przywracanie_komendy – polecenie odtworzenia logu WAL z kopii zapasowej zostanie użyte w przypadku, gdy w „pełnej kopii zapasowej” (bazowej kopii zapasowej) nie ma najnowszych zmian w bazie danych.

Więcej o wszystkich tych parametrach możesz przeczytać w tłumaczeniu oficjalnej dokumentacji: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Konfigurowanie harmonogramu tworzenia kopii zapasowych

Cokolwiek by nie powiedzieć, najwygodniejszym sposobem uruchomienia tego jest cron. To właśnie skonfigurujemy do tworzenia kopii zapasowych. Zacznijmy od polecenia utworzenia pełnej kopii zapasowej: w wal-g jest to argument uruchomienia kopia zapasowa. Ale najpierw lepiej uruchomić to polecenie ręcznie od użytkownika Postgres, aby upewnić się, że wszystko jest w porządku (i nie ma błędów dostępu):

#!/bin/bash

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

Argumenty uruchomienia wskazują ścieżkę do katalogu danych - przypominam, że możesz go znaleźć uruchamiając pg_lsclusters.

Jeśli wszystko przebiegło bez błędów i dane zostały załadowane do pamięci S3, możesz następnie skonfigurować okresowe uruchamianie w crontabie:

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

W tym przykładzie proces tworzenia kopii zapasowej rozpoczyna się codziennie o 4:15.

Usuwanie starych kopii zapasowych

Najprawdopodobniej nie musisz przechowywać absolutnie wszystkich kopii zapasowych z ery mezozoicznej, dlatego przydatne będzie okresowe „czyszczenie” pamięci (zarówno „pełne kopie zapasowe”, jak i logi WAL). Zrobimy to wszystko poprzez zadanie 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 będzie uruchamiał to zadanie codziennie o 6:30, usuwając wszystko (pełne kopie zapasowe, delty i WAL) z wyjątkiem kopii z ostatnich 10 dni, ale pozostawiając co najmniej jedną kopię zapasową do określoną datę, tak aby dowolny punkt później daty zostały uwzględnione w PITR.

Przywracanie z kopii zapasowej

Nie jest tajemnicą, że kluczem do zdrowej bazy danych jest okresowe przywracanie i weryfikacja integralności znajdujących się w niej danych. W tej sekcji powiem ci, jak odzyskać siły za pomocą WAL-G, a o kontrolach porozmawiamy później.

Należy to odnotować osobno że aby przywrócić w środowisku testowym (wszystko co nie jest produkcyjne) trzeba skorzystać z konta Read Only w S3, aby przypadkowo nie nadpisać kopii zapasowych. W przypadku WAL-G należy ustawić w Polityce Grupy następujące uprawnienia dla użytkownika S3 (Efekt: Zezwól): s3:Pobierz obiekt, s3:Wiadro listy, s3: GetBucketLocation. I oczywiście nie zapomnij ustawić tryb_archiwum=wyłączony w pliku ustawień postgresql.conf, aby Twoja testowa baza danych nie chciała być tworzona w trybie dyskretnym.

Przywrócenie odbywa się za pomocą lekkiego ruchu ręki usunięcie wszystkich danych PostgreSQL (łącznie z użytkownikami), dlatego należy zachować szczególną ostrożność podczas uruchamiania poniższych poleceń.

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

Dla tych, którzy chcą sprawdzić proces odzyskiwania, poniżej przygotowano mały kawałek magii basha, dzięki któremu w przypadku problemów z odzyskiwaniem skrypt ulegnie awarii z niezerowym kodem wyjścia. W tym przykładzie przeprowadzanych jest 120 kontroli z limitem czasu wynoszącym 5 sekund (w sumie 10 minut na odzyskanie), aby dowiedzieć się, czy plik sygnałowy został usunięty (oznacza to, że odzyskiwanie zakończyło się pomyślnie):

#!/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 pomyślnym odzyskaniu nie zapomnij ponownie uruchomić wszystkich procesów (pgbouncer/monit itp.).

Sprawdzanie danych po odzyskaniu

Po przywróceniu koniecznie sprawdź integralność bazy danych, aby nie doszło do sytuacji z uszkodzoną/krzywą kopią zapasową. I lepiej to robić z każdym utworzonym archiwum, ale gdzie i w jaki sposób zależy tylko od Twojej wyobraźni (możesz podnosić poszczególne serwery co godzinę lub sprawdzać CI). Jednak konieczne jest przynajmniej sprawdzenie danych i indeksów w bazie danych.

Aby sprawdzić dane, wystarczy przepuścić je przez zrzut, ale lepiej, jeśli podczas tworzenia bazy danych masz włączone sumy kontrolne (sumy kontrolne danych):

#!/bin/bash

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

Aby sprawdzić indeksy - istnieje moduł amcheck, weźmy dla niego zapytanie sql Testy WAL-G i zbuduj wokół tego małą logikę:

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

Podsumowanie

Pragnę wyrazić wdzięczność Andriejowi Borodinowi za pomoc w przygotowaniu publikacji oraz szczególne podziękowania za jego wkład w rozwój WAL-G!

Na tym kończę tę notatkę. Mam nadzieję, że udało mi się przekazać łatwość konfiguracji i ogromny potencjał wykorzystania tego narzędzia w Twojej firmie. Wiele słyszałem o WAL-G, ale nigdy nie miałem wystarczająco dużo czasu, aby usiąść i się nad tym zastanowić. A kiedy zaimplementowałem to w domu, ten artykuł wyszedł ode mnie.

Osobno warto zauważyć, że WAL-G może również współpracować z następującymi systemami DBMS:

Źródło: www.habr.com

Dodaj komentarz