WAL-G : sauvegardes et restauration de bases de données PostgreSQL

Il est depuis longtemps connu que faire des sauvegardes en SQL-dumps (en utilisant pg_dump ou pg_dumpall) n'est pas la meilleure idée. Pour sauvegarder la base de données PostgreSQL, il vaut mieux utiliser la commande pg_basebackup, qui crée une copie binaire des journaux WAL. Mais lorsque vous commencerez à étudier tout le processus de création de copies et de restauration, vous comprendrez qu'il faut écrire au moins quelques tricycles pour que tout cela fonctionne sans causer de douleur à la fois en haut et en bas. Pour alléger les souffrances, WAL-G a été développé.

WAL-G – c'est un outil écrit en Go pour la sauvegarde et la restauration des bases de données PostgreSQL (et depuis peu de temps pour MySQL/MariaDB, MongoDB et FoundationDB). Il prend en charge le travail avec les stockages Amazon S3 (et similaires, par exemple, Yandex Object Storage), ainsi que Google Cloud Storage, Azure Storage, Swift Object Storage et simplement avec le système de fichiers. Toute la configuration se résume à quelques étapes simples, mais en raison du fait que les articles à son sujet sont épars sur Internet - il n'y a pas de manuel how-to complet qui inclurait toutes les étapes de A à Z (il y a plusieurs posts sur Habr, mais de nombreux points y sont omis).

WAL-G : sauvegardes et restauration de bases de données PostgreSQL

Cet article a été écrit principalement pour systématiser mes connaissances. Je ne suis pas DBA et je peux parfois m'exprimer d'une manière plus familière ou de développeur, donc toutes les corrections sont les bienvenues !

Je souligne en particulier que tout ce qui suit est pertinent et vérifié pour PostgreSQL 12.3 sur Ubuntu 18.04, toutes les commandes doivent être exécutées par un utilisateur privilégié.

Installation

Au moment de la rédaction de cet article, la version stable de WAL-G est v0.2.15 (mars 2020). C'est celle que nous allons utiliser (mais si vous souhaitez le compiler vous-même à partir de la branche master, le dépôt sur GitHub contient toutes les instructions pour cela). Pour le téléchargement et l'installation, vous devez exécuter :

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

Après cela, il faut configurer d'abord WAL-G, puis PostgreSQL lui-même.

Configuration de WAL-G

Pour l'exemple de stockage des sauvegardes, Amazon S3 sera utilisé (parce qu'il est plus proche de mes serveurs et son utilisation est très peu coûteuse). Pour cela, un "bucket S3" et des clés d'accès sont nécessaires.

Dans tous les articles précédents sur WAL-G, la configuration se faisait à l'aide de variables d'environnement, mais depuis cette version, les paramètres peuvent être placés dans .walg.json fichier dans le répertoire personnel de l'utilisateur postgres. Pour le créer, nous exécuterons le script bash suivant :

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

Je vais expliquer brièvement tous les paramètres :

  • WALG_S3_PREFIX – chemin vers votre bucket S3 où les sauvegardes seront enregistrées (cela peut être à la racine ou dans un dossier);
  • AWS_ACCESS_KEY_ID – clé d'accès S3 (en cas de restauration sur un serveur de test – ces clés doivent avoir une politique ReadOnly! Cela est expliqué plus en détail dans la section sur la restauration.);
  • AWS_SECRET_ACCESS_KEY – clé secrète dans le stockage S3;
  • WALG_COMPRESSION_METHOD – méthode de compression, il est préférable d'utiliser Brotli (car c'est le juste milieu entre la taille finale et la vitesse de compression/décompression);
  • WALG_DELTA_MAX_STEPS – nombre de « deltas » avant la création d'une sauvegarde complète (ils permettent d'économiser du temps et l'espace des données à charger, mais peuvent légèrement ralentir le processus de restauration, donc il n'est pas recommandé d'utiliser de grandes valeurs);
  • PGDATA – chemin vers le répertoire des données de votre base (vous pouvez le savoir en exécutant la commande pg_lsclusters);
  • PGHOST – connexion à la base, pour une sauvegarde locale il est préférable de passer par un socket unix comme dans cet exemple.

Les autres paramètres peuvent être consultés dans la documentation: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Configuration de PostgreSQL

Pour que l'archivage au sein de la base charge automatiquement les journaux WAL dans le cloud et se restaure à partir de ceux-ci (si nécessaire) – vous devez définir plusieurs paramètres dans le fichier de configuration /etc/postgresql/12/main/postgresql.conf. Mais d'abord vous devez vous assurer, qu'aucune des configurations ci-dessous n'est définie sur d'autres valeurs, afin que lors du redémarrage de la configuration – la SGBD ne tombe pas. Ces paramètres peuvent être ajoutés à l'aide de:

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

Description des paramètres à définir:

  • wal_level – combien d'informations écrire dans les journaux WAL, «replica» – écrire tout;
  • archive_mode – activation du téléchargement des journaux WAL en utilisant la commande du paramètre archive_command;
  • archive_command – commande pour archiver le journal WAL terminé;
  • archive_timeout – l'archivage des journaux ne se produit que lorsqu'il est terminé, mais si votre serveur modifie/ajoute peu de données dans la base de données, il est judicieux de fixer ici une limite en secondes, après laquelle la commande d'archivage sera appelée de manière forcée (j'ai une écriture intensive dans la base chaque seconde, donc j'ai renoncé à définir ce paramètre en production.);
  • restore_command – commande de restauration du journal WAL à partir de la sauvegarde, qui sera utilisée si le « backup complet » (base backup) manque des dernières modifications dans la base de données.

Vous pouvez en savoir plus sur tous ces paramètres dans la traduction de la documentation officielle : https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Configuration du planning de sauvegarde

Quoi qu'il en soit, le moyen le plus pratique de lancer est cron. C'est ce que nous allons configurer pour créer des sauvegardes. Commençons par la commande pour créer une sauvegarde complète : dans wal-g, c'est un argument de lancement. backup-push. Mais pour commencer, il est préférable d'exécuter cette commande manuellement en tant qu'utilisateur postgres, pour s'assurer que tout fonctionne correctement (et qu'il n'y a pas d'erreurs d'accès) :

#!/bin/bash

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

Le chemin à directory des données est spécifié dans les arguments de lancement – je rappelle qu'il peut être obtenu en exécutant pg_lsclusters.

Si tout s'est bien passé et que les données ont été téléchargées dans le stockage S3, vous pouvez configurer un lancement périodique dans 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

Dans cet exemple, le processus de sauvegarde est lancé tous les jours à 4h15 du matin.

Suppression des anciennes sauvegardes

Il est probable que vous n'ayez pas besoin de conserver toutes les sauvegardes depuis l'ère mésozoïque, il sera donc utile de « nettoyer » périodiquement votre stockage (à la fois les « sauvegardes complètes » et les journaux WAL). Nous ferons cela également via une tâche 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 exécutera cette tâche tous les jours à 6h30 du matin, supprimant tout (sauvegardes complètes, deltas et WAL) sauf les copies des 10 derniers jours, mais laissera au moins une sauvegarde à de la date donnée, de sorte que chaque point après de date soit inclus dans le PITR.

Récupération à partir de la sauvegarde

Il n'est un secret pour personne que la clé d'une base de données saine réside dans la restauration périodique et la vérification de l'intégrité des données. Comment récupérer avec WAL-G – je vais en parler dans cette section, et nous discuterons des vérifications par la suite.

Il convient de mentionner séparément que pour la restauration dans un environnement de test (tout ce qui n'est pas en production) – il faut utiliser un compte en lecture seule dans S3, afin de ne pas écraser accidentellement les sauvegardes. Dans le cas de WAL-G, il faut attribuer à l'utilisateur S3 les droits suivants dans la politique de groupe (Effet : Autoriser): s3:GetObject, s3:ListBucket, s3:GetBucketLocation. Et, bien sûr, n'oubliez pas de définir auparavant archive_mode=off dans le fichier de configuration postgresql.conf, afin que votre base de test ne souhaite pas se sauvegarder discrètement.

La restauration s'effectue d'un simple mouvement de main avec la suppression de toutes les données PostgreSQL (y compris les utilisateurs), donc, s'il vous plaît, soyez extrêmement prudent lorsque vous lancerez les commandes suivantes.

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

Pour ceux qui souhaitent vérifier le processus de restauration, ci-dessous un petit morceau de magie bash est préparé, de sorte qu'en cas de problème de restauration, le script échoue avec un code de sortie non nul. Dans cet exemple, 120 vérifications sont effectuées avec un délai de 5 secondes (soit un total de 10 minutes pour la restauration) pour savoir si le fichier de signal a été supprimé (ce qui signifiera que la restauration a réussi) :

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

Après une restauration réussie, n'oubliez pas de relancer tous les processus (pgbouncer/monit, etc.).

Vérification des données après la restauration

Il est impératif de vérifier l'intégrité de la base après la restauration pour éviter des situations de sauvegarde corrompue. Il est préférable de le faire avec chaque archive créée, mais où et comment cela se fait dépend uniquement de votre imagination (vous pouvez lever des serveurs séparés à tarif horaire ou exécuter la vérification dans CI). Mais au minimum, il est nécessaire de vérifier les données et les index dans la base.

Pour vérifier les données, il suffit de les faire passer par un dump, mais il est préférable d'avoir activé les sommes de contrôle lors de la création de la base (somme de contrôle des données):

#!/bin/bash

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

Pour vérifier les index, il existe le module amcheck, la requête sql que nous prendrons de tests WAL-G et autour nous construirons une petite logique :

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

En résumé

Je remercie Андрей Бородин pour son aide dans la préparation de cette publication et un merci particulier pour sa contribution au développement de WAL-G !

Cet article touche maintenant à sa fin. J'espère avoir réussi à transmettre la simplicité de la configuration et l'énorme potentiel d'application de cet outil dans votre entreprise. J'avais beaucoup entendu parler de WAL-G, mais je n'avais jamais eu le temps de m'asseoir et de comprendre. Et après l'avoir intégré chez moi, cet article est sorti de moi.

Il convient de noter que WAL-G peut également fonctionner avec les SGBD suivants :

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster