Pour un de nos clients, nous avons eu l'occasion de mettre en place Barman, qui se connecte à un PostgreSQL 9.3 Standby.
Cet article présente les différents problèmes que nous avons rencontrés et comment nous les avons réglés.
Barman
Tout d'abord, présentons Barman.
Il s'agit d'un outil utilisé pour la gestion des sauvegardes et l'archivage des journaux transactionnels de bases de données PostgreSQL.
Il permet aussi de réaliser des restaurations de bases de données à partir de ces sauvegardes, et notamment à un point défini dans le temps (Point in Time Recovery, ou PITR).
Son objectif est de fournir une solution permettant d'avoir une Perte de Données Maximale Admissible (PDMA, en Anglais RPO pour Recovery Point Objective) la plus proche de 0 possible.
Il fonctionne en deux phases.
La première consiste à récupérer et archiver les journaux transactionnels (Write Ahead Logs ou WAL) en continu.
La seconde consiste à réaliser une sauvegarde complète à une fréquence régulière (généralement programmée dans un cron).
Les dernières versions de Barman permettent d'utiliser le protocole de
réplication pour ces deux phases, ce qui est beaucoup moins intrusif qu'avant
où nous devions configurer PostgreSQL pour envoyer lui-même les WAL par le
mécanisme d'archivage (archive_mode = on
et archive_command
), en passant
par SSH pour les transférer, par exemple.
Cela permet aussi de mettre en place une sauvegarde complètement en mode "pull", où c'est le serveur de sauvegarde qui "tire" les sauvegardes vers lui, ce qui évite de créer un accès direct du serveur PostgreSQL vers ses sauvegardes, et réduit la surface lors d'une attaque mal intentionnée depuis ce serveur (destruction des bases et de leurs sauvegardes).
Contraintes avec PostgreSQL 9.3
Comme nous venons de le voir, Barman est capable d'utiliser le protocole de réplication pour effectuer les sauvegardes et la récupération des journaux transactionnels.
Seulement voilà, il utilise ce que l'on appelle les slots de réplication, c'est à dire un emplacement réservé sur le serveur PostgreSQL qui permet d'avoir une gestion plus sécurisée de la récupération des journaux. En effet lorsqu'un slot de réplication est créé, PostgreSQL sait que si il n'est pas utilisé il doit garder les WAL en vue d'une reconnexion ultérieure. Et ce mécanisme n'est disponible qu'à partir de PostgreSQL 9.4.
Autre point, nous pourrions mettre en place une copie des journaux depuis PostgreSQL "à l'ancienne", mais d'une part, nous voulons être le moins intrusif possible au niveau des serveurs PostgreSQL, et d'autre part, ça impliquerait la mise en place d'un accès direct sur le serveur de sauvegardes depuis le serveur PostgreSQL que nous souhaitons évier pour la raison exprimée plus haut. Et de toutes façons ce n'est pas possible de le faire depuis un standby avant la version 9.5.
Architecture
Tous nos serveurs tournent sur une distribution Debian Stretch, toutes les commandes et les configurations sont donc spécifiques à cette distribution.
Nous avons déjà en place une réplication entre :
- un serveur primaire
pg1
dont l'adresse est 192.168.0.41 - un serveur standby
pg2
dont l'adresse est 192.168.0.42
Le serveur de sauvegardes sera appelé bak1
et aura pour adresse IP
192.168.0.50.
Nous aurons donc deux choses à mettre en place sur bak1
.
La première est la récupération des WAL en utilisant le protocole de
réplication. Pour cela, nous utiliserons l'outil fourni par PostgreSQL :
pg_receivexlog
. Nous le ferons tourner dans un service et nous le
superviserons pour éviter toute rupture dans l'archivage des WAL. Cela sera
fait de façon à ce que Barman croie que c'est le serveur PostgreSQL qui lui
envoie les journaux en mode "archive".
La seconde est une sauvegarde complète avec Barman. Il s'agit d'une
configuration classique, Barman utilisera alors pg_basebackup
pour les
réaliser. Il archivera aussi les journaux fournis par le service
pg_receivexlog
.
Préparation du réplicat PostgreSQL
Sur pg1
, nous devrons créer deux utilisateurs PostgreSQL qui serviront à
Barman.
Le premier sera utilisé pour les vérifications effectuées par Barman.
Le second est utilisé pour les sauvegardes complètes et la récupération des journaux utilisant le protocole de réplication.
Voici les commandes pour les réaliser :
CREATE USER barman WITH LOGIN, SUPERUSER;
CREATE USER streaming_barman WITH LOGIN, REPLICATION;
Nous prenons ici le parti de ne pas donner de mot de passe aux utilisateurs car
les accès seront filtrés par le fichier pg_hba.conf
. De plus, autoriser la
connexion sans mot de passe, ou stocker ce mot de passe sur le serveur de
sauvegardes revient strictement au même d'un point de vue sécurité.
La sauvegarde et la récupération des WAL se faisant en se connectant sur pg2
,
nous allons y autoriser nos utilisateurs depuis bak1
en rajoutant dans le
fichier /etc/postgresql/9.3/main/pg_hba.conf
les lignes suivantes :
host all barman 192.168.0.50/32 trust
host replication streaming_barman 192.168.0.50/32 trust
Il faut recharger le service PostgreSQL quand on modifie ce fichier :
sudo service postgresql@9.3-main reload
Dernier point, si ce n'est pas déjà fait, il faut configurer au moins pg2
pour faire croire à Barman qu'il est en mode archive, en activant les
paramètres suivants dans /etc/postgresql/9.3/main/postgresql.conf
:
archive_mode = on
archive_command = 'cd .'
Si l'un de ces paramètres est changé, il faut cette fois complètement redémarrer PostgreSQL :
sudo service postgresql@9.3-main restart
Installation des éléments nécessaires
L'installation de Barman se fait à partir des dépôt PGDG.
Une fois le dépôt mis en place comme dans l'article référencé, il suffit
d'installer barman et la bonne version de PostgreSQL avec apt
sur bak1
:
apt install barman postgresql-9.3
L'installation créera une configuration de base, et deux exemples de configurations de serveurs.
Il créera aussi la tâche planifiée nécessaire, nous en reparlerons plus tard.
Nous allons aussi préparer le répertoire de destination :
sudo install -o barman -g barman -d /srv/barman
Récupération des WAL
Dans un premier temps, nous allons mettre en place la récupération des WAL.
Cela se fera en mettant en place un service Systemd, qui sera garant de
l'exécution permanente de la commande pg_receivexlog
.
Préparation et test du script
Nous allons d'abord écrire un script /usr/local/bin/barman-receivexlog.sh
qui récupèrera les journaux pour un serveur passé en paramètre :
#!/bin/sh
SERVER="$1"
BARMAN_HOME="/srv/barman"
WAL_TARGET_DIR=${BARMAN_HOME}/${SERVER}/receive-xlog
cd /
sudo -u barman mkdir -p ${WAL_TARGET_DIR}
exec /usr/lib/postgresql-9.3/bin/pg_receivexlog \
-h ${SERVER} -U streaming_barman -w \
-v -D ${WAL_TARGET_DIR}
Nous pouvons tester la récupération :
sudo -u barman /usr/local/bin/barman-receivexlog.sh pg2
Le script devrait produire une sortie qui au bout d'un moment ressemble à ça :
pg_receivexlog: starting log streaming at 60F/91000000 (timeline 1)
pg_receivexlog: finished segment at 60F/92000000 (timeline 1)
pg_receivexlog: finished segment at 60F/93000000 (timeline 1)
Une fois que nous avons une seconde ligne avec finished segment
, nous pouvons
arrêter le script (avec CTRL+C), et vérifier la présence de WAL :
# ls /srv/barman/pg2/receive-xlog
000000010000060F00000092
000000010000060F00000093.partial
Le test étant concluant, nous pouvons supprimer les fichiers pour passer à l'écriture du service proprement dit :
rm /srv/barman/pg2/receive-xlog/*
Si le test n'est pas concluant, il faut analyser les traces côté pg2
et
bak1
pour déterminer ce qu'il se passe et corriger le problème.
Écriture et activation du service
Le service de récupération des journaux transactionnels sera dans le fichier
/etc/systemd/system/barman-receivexlog@.service
. Le @
est important car ça
permettra de gérer plusieurs serveurs si c'est nécessaire.
Voici le contenu du fichier :
[Unit]
Description=Service de récupération des journaux transactionnels pour %i
Requires=network.target
After=network.target
[Service]
User=barman
ExecStart=/usr/local/bin/barman-receivexlog.sh %i
Restart=always
[Install]
WantedBy=multi-user.target
Une fois le service écrit, nous allons l'activer et le démarrer :
sudo systemctl daemon-reload
sudo systemctl enable barman-receivexlog@pg2.service
sudo systemctl start barman-receivexlog@pg2.service
Nous pouvons vérifier son état :
# sudo systemctl status barman-receivexlog@pg2.service
● barman-receivexlog@pg2.service - Service de récupération des journaux transactionnels pour pg2.
Loaded: loaded (/etc/systemd/system/barman-receivexlog@.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2018-10-26 14:36:04 CEST; 2s ago
Main PID: 12264 (pg_receivexlog)
Tasks: 1 (limit: 4915)
CGroup: /system.slice/system-barman\x2dreceivexlog.slice/barman-receivexlog@pg2.service
└─12264 /usr/lib/postgresql/9.3/bin/pg_receivexlog -h pg2 -U streaming_barman -w -v -D /srv/barman/pg2/receive-xlog
Oct 26 14:36:04 bak1 systemd[1]: Started Service de récupération des journaux transactionnels pour pg2..
Oct 26 14:36:04 bak1 barman_receive-xlog.sh[12264]: pg_receivexlog: starting log streaming at 60F/CB000000 (timeline 1)
Configuration de Barman
Configuration globale
La configuration globale reste la même à peu de choses près. Nous changerons juste la destination des fichiers de barman :
barman_home = /srv/barman
Configuration du serveur
Voici le plus gros. Nous allons créer une configuration du serveur dans le
fichier /etc/barman.d/pg2.conf
et y mettre le contenu suivant :
; Barman, Backup and Recovery Manager for PostgreSQL
; http://www.pgbarman.org/ - http://www.2ndQuadrant.com/
[pg2]
; Human readable description
description = "PostgreSQL Database on pg2."
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; PostgreSQL connection string (mandatory)
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
conninfo = host=pg2 user=barman dbname=postgres
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; PostgreSQL streaming connection string
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; To be used by pg_basebackup for backup and pg_receivexlog for WAL streaming
; NOTE: streaming_barman is a regular user with REPLICATION privilege
streaming_conninfo = host=pg2 user=streaming_barman
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Backup settings (via pg_basebackup)
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
backup_method = postgres
;streaming_backup_name = barman_streaming_backup
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Continuous WAL archiving (via 'archive_command')
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
archiver = on
;archiver_batch_size = 50
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Custom configuration
; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; PATH setting for this server
path_prefix = "/usr/lib/postgresql/9.3/bin"
; WAL compression
compression = pybzip2
; Retention policy
retention_policy = RECOVERY WINDOW OF 1 DAY
Il y a quelques éléments que l'on peut adapter suivant ses besoins :
compression
: on peut l'enlever si on ne veut pas de compression ou mettre une autre valeur. La documentation se trouve iciretention_policy
: la politique de rétention, ici 1 jour, peut être ajustée tel que c'est décrit ici
Adaptation du cron
Le mécanisme que nous avons mis en place nécessite quelques ajustements. En
effet les WAL sont stockés dans un endroit différent de celui où Barman les
attend. C'est nécessaire car pg_receivexlog
récupère les informations dans un
fichier .partial, qui serait immédiatement archivé par Barman et nous voulons
éviter ça.
Du coup, nous copions d'abord les WAL dans le répertoire attendu, puis nous lançons le cron Barman.
Pour cela, nous allons écrire un nouveau script /usr/local/bin/barman-cron.sh
avec le contenu suivant :
#!/bin/sh
BARMAN_HOME=/srv/barman
BARMAN_SERVERS=$*
for BARMAN_SERVER in ${BARMAN_SERVERS}; do
/usr/bin/rsync \
-av --remove-source-files --exclude '*.partial' \
${BARMAN_HOME}/${BARMAN_SERVER}/receive-xlog/ \
${BARMAN_HOME}/${BARMAN_SERVER}/incoming/
done
/usr/bin/barman -q cron
Ce script est fait pour prendre en paramètre les serveurs pour lesquels les WAL
sont récupérés avec un service receivexlog
dans le cas où il y en aurait
plusieurs.
Nous en profitons pour ajouter la planification des sauvegardes complètes, ici, ça sera toutes les 6 heures.
Au final, le fichier /etc/cron.d/barman
doit être modifié pour gérer ces deux
actions :
# /etc/cron.d/barman: crontab entries for the barman package
* * * * * barman /usr/local/bin/barman-cron.sh pg2
0 */6 * * * barman [ -x /usr/bin/barman ] && /usr/bin/barman -q backup pg2
À partir de ce moment, le serveur est configuré pour réaliser les sauvegardes, avec archivage des WAL.
Nettoyage
Barman s'occupe tout seul du nettoyage des anciennes sauvegardes et des journaux associés en fonction de la politique de rétention, donc rien à faire à ce niveau là.
Vérification
Pour voir la réalisation des sauvegardes, nous pouvons utiliser la commande
list-backup
:
# barman list-backup pg2
pg2 20181029T180001 - STARTED
pg2 20181029T120001 - Mon Oct 29 12:39:00 2018 - Size: 246.3 GiB - WAL Size: 1.7 GiB
pg2 20181029T060002 - Mon Oct 29 06:39:20 2018 - Size: 246.0 GiB - WAL Size: 1.0 GiB
pg2 20181029T000001 - Mon Oct 29 00:38:21 2018 - Size: 246.0 GiB - WAL Size: 229.4 MiB
pg2 20181028T180001 - Sun Oct 28 18:41:01 2018 - Size: 246.0 GiB - WAL Size: 1.7 GiB
pg2 20181028T120001 - Sun Oct 28 12:38:36 2018 - Size: 245.7 GiB - WAL Size: 1.7 GiB
Ici, nous constatons qu'une sauvegarde est en cours.
Pour vérifier l'état du serveur, avec la commande check
:
# barman check pg2
Server pg2:
PostgreSQL: OK
is_superuser: OK
PostgreSQL streaming: OK
wal_level: OK
directories: OK
retention policy settings: OK
backup maximum age: OK (interval provided: 1 day, latest backup age: 5 hours, 52 minutes, 25 seconds)
compression settings: OK
failed backups: OK (there are 0 failed backups)
minimum redundancy requirements: OK (have 5 backups, expected at least 0)
pg_basebackup: OK
pg_basebackup compatible: OK
pg_basebackup supports tablespaces mapping: OK (pg_basebackup 9.4 or higher is required for tablespaces support)
archive_mode: OK
archive_command: OK
archiver errors: OK
Cette commande peut-être compatible Nagios :
barman check --nagios pg2
Il suffit alors de la mettre dans une configuration NRPE, par exemple, et de la configurer au niveau de notre hôte.
Restauration automatique de la dernière sauvegarde
Pour effectuer certains tests, notamment la restauration elle-même, il peut être intéressant de mettre en place une restauration locale automatique.
Pour cela nous avons écrit un script /usr/local/bin/barman-recovery.sh
qui
prépare le cluster PostgreSQL, lance la commande de restauration sur la
dernière sauvegarde du serveur passé en paramètre, et fait le nécessaire pour
lancer PostgreSQL :
#!/bin/sh
PG_VERSION=9.3
PG_CLUSTER_NAME=main
BARMAN_DESTINATION_DIRECTORY=/var/lib/postgresql/${PG_VERSION}/${PG_CLUSTER_NAME}
BARMAN_SERVER="$1"
[ -z "${BARMAN_SERVER}" ] && echo "Missing server name in parameter." >&2 && exit 1
set -e
# Stop PostgreSQL.
/usr/sbin/service postgresql@${PG_VERSION}-${PG_CLUSTER_NAME} stop
# Cleanup destination.
/bin/rm -Rf ${BARMAN_DESTINATION_DIRECTORY}
# Change owner.
/bin/chown barman:barman $(dirname ${BARMAN_DESTINATION_DIRECTORY})
# Recover
LAST_BACKUP=$(/usr/bin/barman list-backup --minimal ${BARMAN_SERVER} | /usr/bin/head -n 1)
[ -z "${LAST_BACKUP}" ] && echo "Could not find backup for ${BARMAN_SERVER}." >&2 && exit 1
/usr/bin/barman -q recover ${BARMAN_SERVER} ${LAST_BACKUP} ${BARMAN_DESTINATION_DIRECTORY}
# Change destination owner.
/bin/chown postgres:postgres -R $(dirname ${BARMAN_DESTINATION_DIRECTORY})
# Start PostgreSQL.
/usr/sbin/service postgresql@${PG_VERSION}-${PG_CLUSTER_NAME} start
exit 0
Il suffit ensuite d'appeler ce script dans un cron pour qu'il s'exécute 1h
après chaque sauvegarde complète, par exemple
/etc/cron.d/cron-barman-recovery
:
MAILTO=""
0 1,7,13,19 * * * root /usr/local/bin/barman-recovery.sh pg2
Pour aller plus loin
Cet article ne rentre pas les détails de la restauration d'une base.
Si vous souhaitez poursuivre, je vous conseille de lire l'article de DBI Services, il date un peu mais est toujours d'actualité, ou encore celui d'Oxalide, un peu plus récent.
Enfin, pour la mise en place de la récupération des WAL en streaming directement par Barman, en Anglais cette fois-ci, je vous conseille aussi l'article de 2ndQuadrant, il est un peu long, et utilise une connexion SSH pour réaliser les sauvegardes complètes, mais il explique clairement comment mettre en place les slots de réplication pour Barman.