Es ist schon lange bekannt, dass Backups in SQL-Dumps (unter Verwendung von pg_dump oder pg_dumpall) – keine besonders gute Idee sind. Für die Sicherung von PostgreSQL-Datenbanken sollten Sie besser den Befehl pg_basebackup, verwenden, der eine binäre Kopie der WAL-Protokolle erstellt. Doch wenn Sie den gesamten Prozess der Erstellung und Wiederherstellung von Kopien betrachten, werden Sie feststellen, dass es mindestens ein paar technische Hürden zu überwinden gibt, damit alles reibungslos funktioniert und Ihnen sowohl oben als auch unten keine Schmerzen bereitet. Um die Mühen zu erleichtern, wurde WAL-G entwickelt.
ist ein in Go geschriebenes Tool für die Sicherung und Wiederherstellung von PostgreSQL-Datenbanken (und seit kurzem auch für MySQL/MariaDB, MongoDB und FoundationDB). Es unterstützt die Arbeit mit Speichern wie Amazon S3 (und Alternativen, wie Yandex Object Storage), sowie Google Cloud Storage, Azure Storage, Swift Object Storage und einfach mit dem Dateisystem. Die gesamte Konfiguration lässt sich auf einfache Schritte reduzieren, aber da die Artikel darüber verteilt im Internet stehen – gibt es kein umfassendes How-to-Handbuch, das alle Schritte von Anfang bis Ende umfasst (obwohl es auf Habr einige Beiträge dazu gibt, fehlen dort viele Punkte).

Dieser Artikel ist in erster Linie geschrieben, um mein Wissen zu systematisieren. Ich bin kein DBA und kann manchmal in einem umgangssprachlichen Entwicklerjargon sprechen, daher sind alle Korrekturen willkommen!
Ich möchte betonen, dass alles, was unten steht, aktuell und überprüft für PostgreSQL 12.3 auf Ubuntu 18.04 ist. Alle Befehle sollten von einem privilegierten Benutzer ausgeführt werden.
Installation von
Zum Zeitpunkt des Schreibens dieses Artikels ist die stabile Version von WAL-G – . Diese werden wir verwenden (aber wenn Sie es selbst aus dem Master-Branch compilieren möchten, finden Sie alle Anweisungen im Repository auf GitHub.). Um es herunterzuladen und zu installieren, führen Sie Folgendes aus:
#!/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/
Nach der Installation müssen Sie zunächst WAL-G konfigurieren und anschließend PostgreSQL.
WAL-G Konfiguration
Für die Speicherung der Backups wird Amazon S3 verwendet (weil es näher an meinen Servern ist und die Nutzung sehr kostengünstig ist). Für die Nutzung benötigen Sie einen „S3-Bucket“ und Zugangsschlüssel.
In allen früheren Artikeln über WAL-G wurde die Konfiguration mithilfe von Umgebungsvariablen durchgeführt, aber ab dieser Version können die Einstellungen in im Home-Verzeichnis des Benutzers postgres. Um es zu erstellen, führen wir das folgende 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
Lassen Sie mich die einzelnen Parameter kurz erläutern:
- WALG_S3_PREFIX – der Pfad zu Ihrem S3-Bucket, in den die Backups hochgeladen werden (dies kann entweder im Hauptverzeichnis oder in einem Ordner geschehen);
- AWS_ACCESS_KEY_ID – der Zugriffsschlüssel für S3 (Wenn Sie auf einem Testserver wiederherstellen, müssen diese Schlüssel eine ReadOnly Policy haben! Weitere Informationen hierzu finden Sie im Abschnitt zur Wiederherstellung.);
- AWS_SECRET_ACCESS_KEY – der geheime Schlüssel im S3-Speicher;
- WALG_COMPRESSION_METHOD – das Komprimierungsverfahren, am besten verwenden Sie Brotli (da dies einen optimalen Kompromiss zwischen Größe und Geschwindigkeit der Komprimierung/Dekomprimierung darstellt);
- WALG_DELTA_MAX_STEPS – die Anzahl der „Deltas“, bevor ein vollständiges Backup erstellt wird (diese sparen Zeit und den Umfang der hochgeladenen Daten, könnten jedoch den Wiederherstellungsprozess etwas verlangsamen, daher sollten große Werte vermieden werden);
- PGDATA – der Pfad zum Verzeichnis mit Ihren Datenbanken (dies kann mit dem Befehl abgerufen werden: pg_lsclusters);
- PGHOST – Verbindung zur Datenbank, bei lokalen Backups ist es besser, dies über den Unix-Socket zu machen, wie in diesem Beispiel.
Die weiteren Parameter finden Sie in der Dokumentation: .
PostgreSQL-Konfiguration
Damit der Archivator innerhalb der Datenbank die WAL-Protokolle selbst in die Cloud hochlädt und sich aus ihnen wiederherstellt (falls nötig), müssen einige Parameter in der Konfigurationsdatei festgelegt werden. /etc/postgresql/12/main/postgresql.conf. Zunächst müssen Sie jedoch sicherstellen, dass keine der unten aufgeführten Einstellungen auf andere Werte gesetzt sind, damit die DBMS bei einem Neustart der Konfiguration nicht abstürzt. Diese Parameter können Sie wie folgt hinzufügen:Beschreibung der festgelegten Parameter:
#!/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
wal_level
- – wie viele Informationen in die WAL-Protokolle geschrieben werden sollen, „replica“ – alles schreiben; archive_mode
- – Aktivierung des Hochladens von WAL-Protokollen unter Verwendung des Kommandos aus dem Parameter archive_command – Befehl zum Archivieren des abgeschlossenen WAL-Protokolls;;
- – Befehl zum Archivieren des abgeschlossenen WAL-Protokolls; archive_timeout
- – die Archivierung der Protokolle erfolgt nur, wenn sie abgeschlossen sind. Wenn Ihr Server jedoch nur wenige Änderungen/Hinzufügungen in die DB vornimmt, macht es Sinn, hier eine Sekundenobergrenze festzulegen, nach deren Ablauf der Archivierungsbefehl zwangsweise aufgerufen wird ( ich habe jede Sekunde intensive Schreibvorgänge in die Datenbank, deshalb habe ich auf die Festlegung dieses Parameters in der Produktion verzichtet.Ich habe jede Sekunde eine intensive Datenbankeintragung, deshalb habe ich beschlossen, diese Einstellung in der Produktion nicht zu verwenden.);
- restore_command – Das WAL-Log-Wiederherstellungsteam aus dem Backup wird verwendet, falls im "vollen Backup" (Basis-Backup) die letzten Änderungen in der DB fehlen.
Weitere Informationen zu all diesen Parametern finden Sie in der Übersetzung der offiziellen Dokumentation: .
Konfiguration des Backup-Zeitplans
Wie auch immer man es dreht, die bequemste Methode zum Starten ist cron. Genau dies 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 das Startargument backup-push. Aber zuerst sollte dieser Befehl manuell von dem Benutzer postgres ausgeführt werden, um sicherzustellen, dass alles in Ordnung ist (und keine Zugriffsfehler auftreten):
#!/bin/bash
su - postgres -c '/usr/local/bin/wal-g backup-push /var/lib/postgresql/12/main'
Im Startargument ist der Pfad zum Datenverzeichnis angegeben – ich erinnere daran, dass Sie ihn ermitteln können, indem Sie pg_lsclusters.
Wenn alles ohne Fehler verlief und die Daten im S3-Speicher hochgeladen wurden, können Sie die regelmäßige 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 täglich um 4:15 Uhr gestartet.
Löschen alter Backups
Wahrscheinlich müssen Sie nicht alle Backups aus der mesozoischen Ära speichern, daher ist es sinnvoll, Ihr Speicher regelmäßig "aufzuräumen" (sowohl "volle Backups" als auch WAL-Logdateien). Wir werden dies ebenfalls ü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
Der Cron-Job wird diese Aufgabe jeden Tag um 6:30 Uhr morgens ausführen und alles (volle Backups, Deltas und WALs) außer den Kopien der letzten 10 Tage löschen, dabei jedoch mindestens ein Backup bis zu des angegebenen Datums behalten, damit jeder Zeitpunkt nach im PITR enthalten ist.
Wiederherstellung aus einem Backup
Es ist kein Geheimnis, dass die Grundlage einer gesunden Datenbank im regelmäßigen Wiederherstellen und Überprüfen der Datenintegrität liegt. Wie Sie mit WAL-G wiederherstellen, werde ich in diesem Abschnitt erläutern, und über die Überprüfungen sprechen wir später.
Besonders wichtig ist, dass für die Wiederherstellung in einer Testumgebung (alles, was nicht Produktion ist) ein Read-Only-Konto in S3 verwendet werden muss, um zu vermeiden, dass Backups versehentlich überschrieben werden. Im Fall von WAL-G müssen dem S3-Benutzer folgende Rechte in der Gruppenrichtlinie zugewiesen werden (Effect: Allow): s3:GetObject, s3:ListBucket, s3:GetBucketLocation). Und natürlich sollte nicht vergessen werden, im Voraus archive_mode=off in der Konfigurationsdatei postgresql.conf, damit Ihre Testdatenbank nicht heimlich ein Backup anfertigt.
Die Wiederherstellung erfolgt mit nur wenigen Handgriffen über die Löschung aller Daten von PostgreSQL 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 überwachen möchten, ist hier ein kleines Stück Bash-Magie vorbereitet. Sollte es Probleme bei der Wiederherstellung geben, wird das Skript mit einem nicht-null Exit-Code abbrechen. In diesem Beispiel führen wir 120 Prüfungen mit einem Timeout von 5 Sekunden durch (insgesamt 10 Minuten für die Wiederherstellung), um zu prüfen, ob die Signaldatei gelöscht wurde (was bedeuten würde, 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
Vergessen Sie nicht, nach einer erfolgreichen Wiederherstellung alle Prozesse (pgbouncer/monit usw.) wieder zu starten.
Datenprüfung nach der Wiederherstellung
Es ist unbedingt erforderlich, die Integrität der Datenbank nach der Wiederherstellung zu überprüfen, um eine Situation mit beschädigten oder fehlerhaften Sicherungskopien zu vermeiden. Es wäre ideal, dies für jedes erstellte Archiv zu tun, aber wo und wie – das hängt ganz von Ihrer Fantasie ab (Sie können separate Server auf Stundenbasis hochfahren oder die Prüfung in CI ausführen). Mindestens jedoch sollten die Daten und Indizes in der Datenbank überprüft werden.
Um die Daten zu überprüfen, reicht es aus, sie durch ein Dump zu leiten, aber es ist besser, wenn bei der Erstellung der Datenbank die Prüfziffern aktiviert sind ():
#!/bin/bash
if ! su - postgres -c 'pg_dumpall > /dev/null'
then
echo 'pg_dumpall failed'
exit 125
fi
Um die Indizes zu überprüfen, gibt es , die SQL-Abfrage nehmen wir aus und bauen eine kleine Logik darum herum:
#!/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
Danke an Andrei Borodin für seine Hilfe bei der Vorbereitung der Veröffentlichung und besonders für seinen Beitrag zur Entwicklung von WAL-G!
Damit ist dieser Beitrag zu Ende. Ich hoffe, ich konnte die Benutzerfreundlichkeit der Einrichtung und das enorme Potenzial dieses Instruments für Ihr Unternehmen verdeutlichen. Ich habe viel über WAL-G gehört, aber ich hatte nie die Zeit, mich hinzusetzen und es zu verstehen. Nachdem ich es bei mir implementiert habe, ist dieser Artikel entstanden.
Es ist erwähnenswert, dass WAL-G auch mit den folgenden DBMS arbeiten kann:
- ;
- ;
- ;
- Und judging by the commits, sind noch weitere zu erwarten!
Quelle: habr.com
