WAL-G: Backups und Wiederherstellung von PostgreSQL-Datenbanken

Es ist schon lange bekannt, dass es keine gute Idee ist, Backups in SQL-Dumps zu machen (unter Verwendung von pg_dump oder pg_dumpall). Für die Sicherung von PostgreSQL-Datenbanken sollte man besser den Befehl verwenden pg_basebackup, der eine binäre Kopie der WAL-Protokolle erstellt. Aber wenn Sie den gesamten Prozess der Erstellung von Kopien und der Wiederherstellung studieren, werden Sie verstehen, dass man mindestens ein paar dreirädrige Fahrräder schreiben muss, damit alles funktioniert und Ihnen sowohl oben als auch unten keine Schmerzen bereitet. Um das Leiden zu erleichtern, wurde WAL-G entwickelt.

WAL-G – ein in Go geschriebenes Tool zum Backup und zur Wiederherstellung von PostgreSQL-Datenbanken (und seit kurzem auch MySQL/MariaDB, MongoDB und FoundationDB). Es unterstützt die Arbeit mit Speicherlösungen wie Amazon S3 (und ähnlichen, z.B. Yandex Object Storage) sowie Google Cloud Storage, Azure Storage, Swift Object Storage und einfach mit dem Dateisystem. Die gesamte Konfiguration besteht aus einfachen Schritten, aber da die Artikel darüber im Internet verstreut sind – gibt es kein vollständiges How-To-Handbuch, das alle Schritte von Anfang bis Ende umfasst (auf Habré gibt es mehrere Beiträge, aber viele Punkte werden dort ausgelassen).

WAL-G: Backups und Wiederherstellung von PostgreSQL-Datenbanken

Dieser Artikel ist in erster Linie geschrieben worden, um mein Wissen zu systematisieren. Ich bin kein DBA und kann manchmal in einer umgangssprachlichen Entwickler-Sprache sprechen, daher sind alle Korrekturen willkommen!

Ich möchte besonders darauf hinweisen, dass alles, was im Folgenden aufgeführt wird, relevant und überprüft für PostgreSQL 12.3 auf Ubuntu 18.04 ist, alle Befehle sollten als privilegierter Benutzer ausgeführt werden.

Installation

Zum Zeitpunkt der Erstellung dieses Artikels ist die stabile Version von WAL-G – v0.2.15 (März 2020). Diese werden wir verwenden (aber wenn Sie es selbst aus dem Master-Branch kompilieren möchten, gibt es im Repository auf GitHub alle Anweisungen dafür). Um herunterzuladen und zu installieren, muss man Folgendes ausführen:

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

Danach muss zuerst WAL-G konfiguriert und dann PostgreSQL selbst eingerichtet werden.

WAL-G Konfiguration

Für das Beispiel der Speicherung von Backups wird Amazon S3 verwendet (weil es näher an meinen Servern ist und dessen Nutzung sehr günstig ist). Für die Arbeit damit benötigt man einen "s3-Bucket" und Zugangsschlüssel.

In allen vorherigen Artikeln über WAL-G wurde die Konfiguration mithilfe von Umgebungsvariablen vorgenommen, aber ab diesem Release können die Einstellungen in .walg.json Datei im Home-Verzeichnis des Benutzers postgres abgelegt werden. Um sie zu erstellen, führen wir folgendes Bash-Skript aus:

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

Ich werde ein wenig zu allen Parametern erklären:

  • WALG_S3_PREFIX – der Pfad zu Ihrem S3-Bucket, in dem die Backups gespeichert werden (sowohl im Stammverzeichnis als auch in einem Ordner möglich);
  • AWS_ACCESS_KEY_ID – Zugriffsschlüssel für S3 (im Falle der Wiederherstellung auf einem Testserver – diese Schlüssel sollten eine ReadOnly-Policy haben! Dazu steht mehr im Abschnitt über die Wiederherstellung.);
  • AWS_SECRET_ACCESS_KEY – geheimer Schlüssel im S3-Speicher;
  • WALG_COMPRESSION_METHOD – Kompressionsmethode, am besten Brotli verwenden (da dies das beste Verhältnis zwischen Endgröße und Kompressions-/Dekompressionsgeschwindigkeit bietet);
  • WALG_DELTA_MAX_STEPS – Anzahl der "Deltas" vor der Erstellung eines vollständigen Backups (sie sparen Zeit und die Größe der hochgeladenen Daten, können jedoch den Wiederherstellungsprozess etwas verlangsamen, weshalb große Werte nicht empfehlenswert sind);
  • PGDATA – Pfad zum Verzeichnis mit den Daten Ihrer Datenbank (kann ermittelt werden, indem Sie den Befehl pg_lsclusters);
  • PGHOST – Verbindung zur Datenbank, bei lokalen Backups sollte dies besser über einen Unix-Socket erfolgen, wie in diesem Beispiel.

Die anderen Parameter können in der Dokumentation nachgeschaut werden: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Vorher haben wir uns ein System von PostgreSQL mit

Damit der Archivierungsdienst innerhalb der Datenbank selbst WAL-Protokolle in die Cloud hochlädt und bei Bedarf aus ihnen wiederhergestellt werden kann – müssen einige Parameter in der Konfigurationsdatei festgelegt werden. /etc/postgresql/12/main/postgresql.confZunächst müssen Sie sicherstellen, dass keine der folgenden Einstellungen auf andere Werte gesetzt sind, damit die Datenbank bei einer Konfigurationserneuerung nicht abstürzt. Diese Parameter können mit folgender Methode hinzugefügt werden:

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

Beschreibung der festgelegten Parameter:

  • wal_level – wie viele Informationen in die WAL-Protokolle geschrieben werden, "replica" – alles schreiben;
  • archive_mode – Aktivierung des Uploads von WAL-Protokollen unter Verwendung des Kommandos aus dem Parameter archive_command;
  • archive_command – Befehl zum Archivieren des abgeschlossenen WAL-Protokolls;
  • archive_timeout – Die Protokolle werden nur archiviert, wenn sie abgeschlossen sind, aber wenn Ihr Server wenig Daten in die DB ändert/hinzufügt, macht es Sinn, hier ein Zeitlimit in Sekunden festzulegen, nach dessen Ablauf der Archivierungsbefehl gewaltsam ausgeführt wird (ich habe eine intensive Schreiblast in die Datenbank jede Sekunde, daher habe ich auf die Festlegung dieses Parameters in der Produktion verzichtet.);
  • restore_command – Befehl zur Wiederherstellung des WAL-Protokolls aus dem Backup, wird verwendet, falls in dem "vollständigen Backup" (Basis-Backup) die letzten Änderungen in der DB fehlen.

Mehr Informationen zu all diesen Parametern finden Sie in der Übersetzung der offiziellen Dokumentation: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Konfiguration des Backup-Zeitplans

Wie auch immer man es dreht, der bequemste Weg zum Starten ist cron. Genau das werden wir für die Erstellung von Backups einrichten. Beginnen wir mit dem Befehl zur Erstellung eines vollständigen Backups: in wal-g ist dies ein Startargument. backup-push. Zunächst sollten wir diesen Befehl jedoch manuell als Benutzer postgres ausführen, um sicherzustellen, dass alles gut läuft (und es keine Zugriffsfehler gibt):

#!/bin/bash

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

Im Startargument ist der Pfad zum Verzeichnis mit den Daten angegeben – ich erinnere daran, dass man dies durch Ausführen von pg_lsclusters.

Wenn alles ohne Fehler verlief und die Daten im S3-Speicher geladen wurden, kann man als Nächstes eine periodische Ausführung im crontab einrichten:

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

In diesem Beispiel wird der Backup-Prozess jeden Tag um 4:15 Uhr morgens gestartet.

Löschen alter Backups

Wahrscheinlich müssen Sie nicht alle Backups aus der mesozoischen Ära aufbewahren, daher wird es nützlich sein, Ihr Speicher periodically zu säubern (sowohl "volle Backups" als auch WAL-Protokolle). Auch dies werden wir über einen cron-Job erledigen:

#!/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 wird diese Aufgabe jeden Tag um 6:30 Uhr morgens ausführen und alles löschen (volle Backups, Deltas und WALs), außer Kopien der letzten 10 Tage, jedoch wird mindestens ein Backup des angegebenen Datums behalten, damit jeder Punkt bis im PITR erfasst wird. nach Es ist kein Geheimnis, dass die Grundlage für eine gesunde Datenbank in der regelmäßigen Wiederherstellung und Überprüfung der Integrität der Daten liegt. Wie man sich mit WAL-G wiederherstellt, werde ich in diesem Abschnitt erläutern, während wir die Überprüfungen nachher besprechen.

Wiederherstellung aus dem Backup

Es sollte gesondert erwähnt werden,

dass für die Wiederherstellung in einer Testumgebung (alles, was nicht Produktion ist) ein Read-Only-Konto in S3 verwendet werden sollte, um versehentliches Überschreiben von Backups zu vermeiden. Im Falle von WAL-G müssen dem S3-Benutzer die folgenden Berechtigungen in der Gruppenzuweisung zugewiesen werden ( Effect: Allows3:GetObject): s3:ListBucket, s3:GetBucketLocation, . Und selbstverständlich sollte man nicht vergessen,archive_mode=off in der Konfigurationsdatei , damit Ihre Testdatenbank nicht unauffällig ein Backup erstellen möchte. postgresql.confDie Wiederherstellung erfolgt mit einem einfachen Handgriff durch

das Löschen aller PostgreSQL-Daten einschließlich der Benutzer, daher bitte seien Sie äußerst vorsichtig, wenn Sie die folgenden Befehle ausführen. (einschließlich der Benutzer), seien Sie daher bitte äußerst vorsichtig, wenn Sie die folgenden Befehle ausführen.

#!/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 diejenigen, die den Wiederherstellungsprozess überprüfen möchten – unten ist ein kleiner Abschnitt Bash-Magie vorbereitet, damit im Falle von Problemen bei der Wiederherstellung das Skript mit einem nicht-null exit code abstürzt. In diesem Beispiel werden 120 Prüfungen mit einem Timeout von 5 Sekunden durchgeführt (insgesamt 10 Minuten für die Wiederherstellung), um zu erfahren, ob die Signaldatei gelöscht wurde (das würde bedeuten, dass die Wiederherstellung erfolgreich war):

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

Nach einer erfolgreichen Wiederherstellung vergessen Sie nicht, alle Prozesse (pgbouncer/monit usw.) zurückzusetzen.

Datenprüfung nach der Wiederherstellung

Es ist unbedingt notwendig, die Integrität der Datenbank nach der Wiederherstellung zu überprüfen, um nicht in eine Situation mit einer beschädigten/fehlerhaften Sicherung zu geraten. Und es ist besser, dies mit jedem erstellten Archiv zu tun, aber wo und wie – hängt nur von Ihrer Fantasie ab (man kann separate Server stundenweise mieten oder die Überprüfung in CI ausführen). Aber mindestens – es ist notwendig, Daten und Indizes in der Datenbank zu überprüfen.

Zur Datenprüfung reicht es aus, diese durch ein Dump zu laufen, aber besser wäre es, wenn beim Erstellen der Datenbank Prüfziffern aktiviert sind (Daten-Prüfziffern):

#!/bin/bash

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

Zur Überprüfung von Indizes – gibt es das Modul amcheck, den SQL-Befehl dafür nehmen wir aus den WAL-G-Tests und darum herum bauen wir eine kleine 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

Zusammenfassend

Ich danke Andrey Borodin für die Hilfe bei der Vorbereitung der Veröffentlichung und ein besonderes Dankeschön für seinen Beitrag zur Entwicklung von WAL-G!

Damit endet dieser Beitrag. Ich hoffe, ich konnte Ihnen die Leichtigkeit der Einrichtung und das enorme Potenzial dieses Werkzeugs in Ihrem Unternehmen näherbringen. Ich habe viel von WAL-G gehört, aber ich hatte nie Zeit, mich damit zu befassen. Und nachdem ich es bei mir eingeführt habe – ist dieser Artikel aus mir herausgekommen.

Es ist ebenfalls erwähnenswert, dass WAL-G auch mit folgenden DBMS arbeiten kann:

Quelle: habr.com

60GB SSD 8Gb DDR4