Sauvegarde physique des fichiers PostgreSQL et Recovery
posté en Jan 2022
Pour effectuer une sauvegarde physique des fichiers de base de donnés, PostgreSQL fournit un programme, pg_basebackup, pouvant fonctionner en local ou à distance.
Combiné avec les fichiers WAL (write ahead log), on pourra effectuer une restauration dans le passé à un point particulier (point in time recovery).
Le programme pg_basebackup permet d'effectuer une sauvegarde à chaud des fichiers du cluster vers un format binaire.
Dans un premier temps, il est important de déterminer où seront copiés les fichiers (en local ou sur un filesystem NFS, etc). Pour cela on effectue une création d'un répertoire ayant les droits pour postgres (ici on choisi un répertoire local /backup/baseXYZ ):
root:#> mkdir /backup/baseXYZ
root:#> chmod 750 /backup/baseXYZ
root:#> chown postgres:postgres /backup/baseXYZ
On exécute pg_basebackup, sous le compte postgres, en indiquant ici un format de sortie tar vers le répertoire préparé :
postgres:#> pg_basebackup -z -Ft -D /backup/baseXYZ
Le résultat génère deux fichier tar.gz, un contenant les fichiers base de données, l'autre les fichiers WAL (contenu du répertoire pg_wal)
postgtres:#> ls -l /backup/baseXYZ/
total 3932
-rw------- 1 postgres postgres 4003031 janv. 11 21:33 base.tar.gz
-rw------- 1 postgres postgres 17066 janv. 11 21:33 pg_wal.tar.gz
Attention ces deux fichiers, dans un environnement de production, doivent être archivé. Le répertoire doit être vidé avant d'effectuer une nouvelle sauvegarde.
Dans le cas ou l'on souhaite restaurer les fichiers (problème de base ou suppression de données involontaire), le service du cluster doit être absolument arrêté :
root:#> systemctl stop postgres@12-main
puis le contenu du répertoire des fichiers du cluster supprimé :
root:#> rm -Rf /var/lib/postgresql/12/main/*
root:#> ls -l /var/lib/postgresql/12/main/
total 0
enfin, on restaure les fichiers tar sauvegardés précédemment dans l'ordre suivant, et on redémarre le service du cluster :
root;#> tar xzf /backup/baseXYZ/base.tar.gz -C /var/lib/postgresql/12/main/
root;#> tar xzf /backup/baseXYZ/pg_wal.tar.gz -C /var/lib/postgresql/12/main/pg_wal/
root:#> systemctl start postgresql@12-main
Le cluster est de nouveau fonctionnel, mais les données ont été restaurées à la date de la sauvegarde utilisée.
Lorsque que l'on souhaite perdre le moins de données, on doit sauvegarder les fichiers WAL archivés au fil de l'eau.
On prépare un espace d'archivage des fichiers WAL
root:># mkdir /backup/walXYZ
root:># chmod 750 /backup/walXYZ
root:># chown postgresl:postgresl /backup/walXYZ
Dans le fichier postgresql.conf, on modifie les trois paramètres suivants :
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /backup/walXYZ/%f && cp %p /backup/walXYZ/%f'
La commande utilisée avec le paramètre archive_command est une commande basique qui vérifie la non existence de l'archive dans la destination, et si ok, copie ce fichier dans le répertoire cible, ici /backup/walXYZ/. Bien sur la commande peut être plus sophistiqué, mais elle doit retourner la valeur ok (0) pour valider la bonne exécution de la commande.
Après les modifications effectuées, on redémarre le service cluster
root:#> systemctl stop postgres@12-main
postgres:#> psql -c "select pg_switch_wal()"
pg_switch_wal
---------------
0/D0001D8
(1 row)
postgres:#> ls /backup/walXYZ/
00000001000000000000000D
postgres:#> ls /var/lib/postgresql/12/main/pg_wal/
00000001000000000000000D 00000001000000000000000E archive_status
postgres:#> ls /var/lib/postgresql/12/main/pg_wal/archive_status/
00000001000000000000000D.done
Le répertoire cible contient bien le nouveau WAL, (...OD), fichier bien sur toujours présent dans le répertoirte pg_wal, et on à un fichier de nom identique avec une extension .done dans le répertoire archive_status pour confirmer la fin correcte de l'archivage. Dans un environnement de production il sera necessaire de purger les fichier WAL bine archivés.
Comme le backup précédemment effectué avec pg_basebackup, on effectue un backup de façon régulière (par exemple 1 fois par jour), mais sans les fichiers WAL (-X none) (voir l'aide avec pg_basebackup --help ) :
postgres:#> pg_basebackup -X none -z -Ft -D /backup/baseXYZ
NOTICE: all required WAL segments have been archived
postgres:#> ls -l /backup/baseXYZ
total 3912
-rw------- 1 postgres postgres 4003265 janv. 11 22:54 base.tar.gz
Tout d'abord,on arrête le service du cluster.
Si la place nécessaire est disponible, copier le répertoire complet de données de l'instance et tous les tablespaces dans un emplacement temporaire en prévision d'un éventuel besoin ultérieur. S'il n'y a pas assez de place disponible, il faut au minimum copier le contenu du sous-répertoire pg_wal du répertoire des données de l'instance car il peut contenir des journaux qui n'ont pas été archivés avant l'arrêt du serveur.
On peut alors supprimer les données avant restauration
root:#> systemctl stop postgres@12-main
root:#>
root:#> rm -Rf /var/lib/postgresql/12/main/*
root:#> ls -l /var/lib/postgresql/12/main/
total 0
Puis on restaure les fichiers sauvegardés précédemment par pg_basebackup :
root:#> tar xzf /backup/baseXYZ/base.tar.gz -C /var/lib/postgresql/12/main/
Puis pour exécuter la restauration, en totalité ou au point d'arrêt spécifié dans le paramètre de cible (target), on modifie le fichier postgresql.conf en vérifiant la commande de restauration des archives nécessaires et en indiquant, éventuellement, une cible d'arrêt de récupération. Pour passer en mode recovery, il est nécessaire de créer un fichier recovery.signal dans le répertoire des données du cluster avant le démarrage du cluster de données.
Il peut être judicieux de modifier temporairement le fichier pg_hba.conf pour empêcher les utilisateurs ordinaires de se connecter tant qu'il n'est pas certain que la récupération a réussi.
Par exemple, une commande de restauration des archives, et une cible spécifiant une heure précise :
restore_command = 'cp /backup/walXYZ/%f %p'
recovery_target_time = '2021-12-10 12:00:00 UTC'
On démarre de service.
root:#> systemctl start postgresql@12-main
Le serveur se trouve alors en mode récupération et commence la lecture des fichiers WAL archivés dont il a besoin. Si la récupération se termine sur une erreur externe (commande erronée, fichier manquant), le serveur peut tout simplement être relancé après correction du problème. Il continue alors la récupération.
À la fin du processus de récupération, le serveur supprime le fichier recovery.signal (pour éviter de retourner accidentellement en mode de récupération), puis passe en mode de fonctionnement normal.
Par défaut, la restauration continuera jusqu'à la fin des WAL. Les paramètres suivants peuvent être utilisés pour indiquer un point d'arrêt. Au moins un des paramètres parmi recovery_target, recovery_target_name et recovery_target_time peut être utilisé. Si plus d'un paramètre est indiqué dans le fichier de configuration, une erreur sera renvoyée.
- recovery_target = 'immediate'
- Ce paramètre indique que la restauration doit s'arrêter dès qu'un point de cohérence est atteint, autrement dit le plus tôt possible. Lors de la restauration d'une sauvegarde, cela signifie le moment où la sauvegarde s'est terminée.
- recovery_target_name (string)
- Ce paramètre indique le point de restauration nommé, créé précédemment avec la fonction pg_create_restore_point(<UNIQUE_NAME>) où la restauration doit s'arrêter.
- recovery_target_time (timestamp)
- Ce paramètre indique jusqu'à quel date et heure la restauration doit s'arrêter. Le point d'arrêt précis est aussi influencé par recovery_target_inclusive.
Les options suivantes peuvent être utilisées :
- recovery_target_inclusive (boolean)
- Indique s'il faut arrêter juste après la cible de restauration indiquée (on) ou juste avant (off). S'applique quand recovery_target_time spécifié. La valeur par défaut est on.
- recovery_target_action (enum)
- Indique l'action que le serveur devra prendre une fois la cible de restauration atteinte. La valeur par défaut est pause, ce qui signifie que la restauration sera mise en pause. promote signifie que le processus de restauration finira et que le serveur démarrera pour accepter toute connexion. Enfin, shutdown arrêtera le serveur après avoir atteint la cible de restauration.
Pour tout problème, n'oubliez pas de consulter le fichier de log du cluster.
Bon backup ^^