Sauvegardes physiques avec pg_basebackup
Nous allons dérouler le processus de sauvegarde avec pg_basebackup. Cette étape doit être bien maitrisée pour aborder la réplication.
Configuration de l'instance
Avant de manipuler pg_basebackup, il faut configurer notre instance :
- Activation de l'archivage
- Configuration des WAL
- Configuration des checkpoints
Configurer l'instance avec les paramètres suivants :
- Mode archive : activé
- Niveau WAL : replica
- Durée max entre les checkpoints : 1 minute
- Commande d'archivage à définir pour stocker les WAL dans
/opt/wal_archives
La commande d'archivage sera exécutée par PostgreSQL à chaque fois qu'un journal doit être archivé. PostgreSQL fournit deux paramètres qui peuvent être utilisés dans cette commande : %f
pour le nom du fichier et %p
pour le chemin complet vers le fichier.
Après avoir configuré ces paramètres, il faudrait redémarrer l'instance. Nous la redémarrerons au dernier moment pour éviter de générer trop de journaux.
Générer des données avec pgbench
Nous allons simuler une activité sur la base en utilisant l'outil pgbench. Cet outil exécute des transactions aléatoires en boucle pendant une durée donnée.
Il s'utilise en deux phases, une phase d'initialisation et une phase d'exécution.
Initialisation
- Créer une base
bench
dédiée à pgbench - Exécuter la commande d'initialisation
pgbench -i -s 100 bench
Exécution
Attention : ne pas exécuter tout de suite
La commande à lancer pour générer des données sera :
pgbench bench -n -P 5 -T 360
Cette commande provoque une insertion de données en continu pendant 360 secondes.
Sauvegarde de l'instance avec pg_basebackup
pg_basebackup simplifie grandement la sauvegarde physique d'une instance en automatisant les appels à pg_start_backup
et pg_stop_backup
, la copie des fichiers etc.
Les options les plus importantes de pg_basebackup sont les suivantes :
-D répertoire de réception de la sauvegarde
-F p|t format de la sauvegarde (p pour plain, t pour tar)
-r taux de transfert maximum des fichiers (important pour éviter de saturer les I/O du serveur)
--checkpoint vitesse du checkpoint. 'fast' pour le réaliser le plus vite possible, 'spread' pour étaler la consommation d'I/O dans le temps
--progress afficher la progression
Avant de lancer la sauvegarde, nous devons créer le répertoire cible, faire le ménage et démarrer la génération de données :
- Créer le répertoire
/opt/base_backup
et définirpostgres
comme son propriétaire - Lancer un vacuum
- Faire un checkpoint
- Lancer la génération de données avec pgbench
Une fois que la génération de données est bien en cours, nous pouvons faire la sauvegarde :
pg_basebackup -D /opt/base_backup -Fp --checkpoint=fast --progress -r 16M
Pendant la sauvegarde, on peut suivre la progression de la génération de données en regardant le contenu de la table pgbench_history
dans la base bench
, à intervalle régulier.
SELECT max(mtime) FROM pgbench_history;
Quand la sauvegarde est terminée :
- Vérifier que les fichiers sont bien présents dans
/opt/base_backup
- Noter la dernière date (
max(mtime)
) de transaction danspgbench_history
- Remarquer la création du fichier
backup_label
dans le répertoire de sauvegarde, observer son contenu
Nous disposons maintenant :
- D'une sauvegarde de l'instance dans
/opt/base_backup
- Les archives des WAL dans
/opt/wal_archives
Restauration dans une nouvelle instance
Nous allons créer une nouvelle instance de base de données pour restaurer notre backup.
Ceci peut être fait facilement avec l'outil pg_createcluster :
mkdir /opt/postgres_restore
chown postgres:postgres /opt/postgres_restore
pg_createcluster -d /opt/postgres_restore -p 5433 13 postgres_restore
Le répertoire de données de cette nouvelle instance sera dans /opt/postgres-restore
, sa configuration se trouve dans /etc/postgresql
Nous allons vider le répertoire de données de cette nouvelle instance pour le remplacer par notre backup.
rm -rf /opt/postgres_restore/*
cp -R /opt/base_backup /opt/postgres_restore
Il faut ensuite remplacer le fichier de configuration de la nouvelle instance, par celui de l'ancienne :
cp /opt/base_backup/postgresql.conf
/etc/postgresql/13/postgres_restore/postgresql.conf
De l'importance des WAL
Avant de restaurer l'instance complètement, nous allons tenter de la démarrer sans aucun fichier WAL.
Il faut avant cela modifier la configuration de l'instance :
- Modifier
data_directory
pour pointer vers le répertoire de la nouvelle instance - Désactiver l'archivage
Ensuite, supprimons tous les logs présents dans notre instance restaurée
rm -rf /opt/postgres_restore/pg_wal/*
Placer le marquer de restauration et démarrer l'instance :
pg_ctlcluster 13 postgres_restore start
Consulter les logs dans /var/log/postgresql/
. Que s'est-il passé ?
Arrêter l'instance :
pg_ctlcluster 13 postgres_restore stop
Restauration avec les journaux
Pour restaurer correctement notre instance, nous avons besoin des journaux. Pour les obtenir, nous allons à nouveau modifier la configuration de l'instance. Ouvrir le fichier de configuration (dans /etc/postgresql/
) :
- Définir une
restore_command
La commande spécifiée dans restore_command
est exécutée par PostgreSQL pour récupérer les archives nécessaires à la restauration. Elle s'écrit avec les mêmes paramètres que archive_command
Nous pouvons maintenant placer le marqueur de restauration et démarrer l'instance :
touch /opt/postgres_restore/recovery.signal
pg_ctlcluster 13 postgres_restore start
Consulter les logs. L'instance devrait dérouler les journaux WAL et terminer par un message indiquant qu'elle est disponible pour les connexions.
2021-06-29 10:49:33.346 UTC [1251] LOG: starting PostgreSQL 13.1 (Debian 13.1-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
2021-06-29 10:49:33.347 UTC [1251] LOG: listening on IPv4 address "0.0.0.0", port 5433
2021-06-29 10:49:33.347 UTC [1251] LOG: listening on IPv6 address "::", port 5433
2021-06-29 10:49:33.351 UTC [1251] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5433"
2021-06-29 10:49:33.360 UTC [1252] LOG: database system was interrupted; last known up at 2021-06-29 09:27:53 UTC
2021-06-29 10:49:34.094 UTC [1252] LOG: restored log file "00000002.history" from archive
cp: cannot stat '/opt/wal_archives/00000003.history': No such file or directory
2021-06-29 10:49:34.100 UTC [1252] LOG: starting archive recovery
2021-06-29 10:49:34.106 UTC [1252] LOG: restored log file "00000002.history" from archive
2021-06-29 10:49:34.149 UTC [1252] LOG: restored log file "00000001000000010000003A" from archive
2021-06-29 10:49:34.188 UTC [1252] LOG: redo starts at 1/3A000028
2021-06-29 10:49:34.190 UTC [1252] LOG: consistent recovery state reached at 1/3A000138
2021-06-29 10:49:34.191 UTC [1251] LOG: database system is ready to accept read only connections
2021-06-29 10:49:34.231 UTC [1252] LOG: restored log file "00000002000000010000003B" from archive
2021-06-29 10:49:34.365 UTC [1252] LOG: restored log file "00000002000000010000003C" from archive
2021-06-29 10:49:34.479 UTC [1252] LOG: restored log file "00000002000000010000003D" from archive
2021-06-29 10:49:34.594 UTC [1252] LOG: restored log file "00000002000000010000003E" from archive
2021-06-29 10:49:34.703 UTC [1252] LOG: restored log file "00000002000000010000003F" from archive
2021-06-29 10:49:34.803 UTC [1252] LOG: restored log file "000000020000000100000040" from archive
2021-06-29 10:49:34.895 UTC [1252] LOG: restored log file "000000020000000100000041" from archive
2021-06-29 10:49:35.014 UTC [1252] LOG: restored log file "000000020000000100000042" from archive
2021-06-29 10:49:35.121 UTC [1252] LOG: restored log file "000000020000000100000043" from archive
cp: cannot stat '/opt/wal_archives/000000020000000100000044': No such file or directory
2021-06-29 10:49:35.191 UTC [1252] LOG: redo done at 1/436FC6B8
2021-06-29 10:49:35.191 UTC [1252] LOG: last completed transaction was at log time 2021-06-29 10:46:34.30952+00
2021-06-29 10:49:35.224 UTC [1252] LOG: restored log file "000000020000000100000043" from archive
cp: cannot stat '/opt/wal_archives/00000003.history': No such file or directory
2021-06-29 10:49:35.264 UTC [1252] LOG: selected new timeline ID: 3
2021-06-29 10:49:35.311 UTC [1252] LOG: archive recovery complete
2021-06-29 10:49:35.315 UTC [1252] LOG: restored log file "00000002.history" from archive
2021-06-29 10:49:40.755 UTC [1251] LOG: database system is ready to accept connections
Restauration PITR
Pour terminer, nous allons simuler une opération malencontreuse en base de données et effectuer une restauration à une date donnée avec le mécanisme PITR.
- Réaliser un checkpoint et noter l'heure (avec
NOW()
) - Supprimer une table importante :
DROP TABLE customer CASCADE;
- Repartir de la sauvegarde précédente, et restaurer l'instance juste avant la suppression de la table
Pour ce faire, il faut modifier le paramètre recovery_target_time
et indiquer la date souhaitée de restauration (format Y-M-d H:m:s.t
)
Bonus :
Nous pouvons consulter le contenu d'un fichier WAL avec la commande /usr/lib/postgresql/13/bin/pg_waldump /opt/wal_archives/[nom d'un fichier WAL]
Grâce à cela, il est possible de récupérer l'identifiant de la transaction qui a provoqué la suppression de la table, et ainsi utiliser le paramètre recovery_target_xid
pour spécifier jusqu'où nous souhaitons restaurer.