Maintenant que nous avons installé [1] et configuré [2] un serveur PostgreSQL, la prochaine étape revient à s’assurer de sa sauvegarde. Il existe deux types de sauvegardes avec PostgreSQL : la sauvegarde (ou export) logique et la sauvegarde physique.
Pour un export logique, il est habituel de faire appel à l’outil pg_dump. Cependant, ce dernier ne gère pas la rétention, ce qui est pourtant une fonctionnalité critique. Nous allons donc mettre en place l’outil pg_back qui utilise en sous-main pg_dump, et propose de gérer, entre autres, la rétention.
Pour ce qui est des sauvegardes physiques (aussi appelées sauvegardes PITR), les développeurs de PostgreSQL proposent pg_basebackup, mais là aussi, pas de gestion de la rétention. Deux outils sont généralement sélectionnés : pgbackrest et barman. Ma préférence va à pgbackrest, bien plus complet, et c’est donc ce dernier que nous installerons.
1. Gérer les exports logiques avec pg_back
Les exports logiques dans PostgreSQL sont gérés avec les outils pg_dump et pg_dumpall. Ce dernier peut sembler suffisant vu qu’il sauvegarde toute l’instance, mais il souffre d’un inconvénient majeur : le résultat de l’export est un script SQL. De ce fait, il devient très compliqué de faire une restauration partielle (par exemple, une seule base ou quelques tables). Le cas où nous avons besoin de restaurer l’instance complète est assez rare, ce qui diminue fortement l’intérêt de pg_dumpall.
Donc les utilisateurs ont plutôt tendance à utiliser pg_dump. Lors de son exécution, cet outil ne s’occupera que d’une seule base. Si nous souhaitons sauvegarder toutes les bases, il faut alors l’exécuter une fois par base à sauvegarder. Cet inconvénient est contrebalancé par tous les avantages de cet outil : sauvegarde complète ou partielle, format de sauvegarde au choix, compression, etc. Pour en revenir au souci de la sauvegarde par base, elle amène un autre problème : il faut connaître la liste des bases à sauvegarder. Nous pouvons très bien écrire un script avec en dur la liste des bases et une exécution de pg_dump pour chacune des bases, mais cela voudrait dire qu’il faut modifier le script à chaque ajout et suppression de base. Ça n’est pas pratique, c’est source d’erreurs. Il faut donc un moyen automatisé pour récupérer la liste des bases à sauvegarder. L’outil psql nous permet de le faire en exécutant une requête sur le catalogue système pg_database.
Il existe un autre inconvénient à pg_dump. Le script de création du schéma permet de cloner les objets de cette base. Cela veut dire qu’il contient la définition des objets, avec leur propriétaire, leur tablespace, et les droits des utilisateurs sur ces objets. Cependant, la définition des rôles (propriétaires et utilisateurs) et des tablespaces n’est pas incluse dans cette sauvegarde. L’explication généralement donnée (mais peu convaincante) est que ce ne sont pas des objets de la base et que pg_dump ne sauvegarde que les objets contenus dans la base. Bref, si nous voulons importer sans erreur le résultat de pg_dump, il faut que les rôles et les tablespaces soient déjà définis. Et c’est là que pg_dumpall devient intéressant. Cet outil permet de sauvegarder uniquement la définition de tous les rôles et de tous les tablespaces grâce à l’option --globals-only.
Donc si nous devions résumer en un petit script les différentes opérations à réaliser, cela ressemblerait à cela :
Évidemment, c’est très simpliste. Il y a plein d’autres points à prendre en compte, comme exclure certaines bases, gérer le niveau de compression, gérer la rétention, gérer la sauvegarde sur un serveur secondaire, etc.
Pour éviter d’avoir à coder un script gérant tout ça (sans même parler de la maintenance d’un tel script personnalisé), il est nettement préférable de s’en remettre à un outil déjà conçu pour ça. Cet outil s’appelle pg_back. Il a été écrit par Nicolas Thauvin et profite de toute l’expérience de ce dernier comme administrateur PostgreSQL. Il est disponible sur son site GitHub (https://github.com/orgrim/pg_back). Des packages Debian et RPM sont téléchargeables directement depuis le site (https://github.com/orgrim/pg_back/releases). Le script fait appel à pg_dumpall pour récupérer la définition des rôles et des tablespaces. Il récupère la liste des bases à sauvegarder, tout en excluant celles indiquées dans son fichier de configuration. Il utilise par défaut le format binaire custom pour la sauvegarde. La sauvegarde des différentes bases peut être parallélisée. Une somme de contrôle peut être calculée et enregistrée une fois la sauvegarde terminée. Il sait mettre en pause le rejeu de la réplication quand il sauvegarde à partir d’un secondaire. Et surtout, il gère la rétention des sauvegardes. C’est un outil très complet, et très facilement configurable.
Voici comment l’installer sur le système que nous avons mis en place depuis deux articles maintenant :
La version 2.2.1 correspond à la dernière version de ce projet au moment de l’écriture de cet article. Je vous conseille fortement de vérifier si une version plus récente est disponible.
Le fichier de configuration se trouve dans le répertoire /etc/pg_back. Nous allons simplement modifier le répertoire des binaires de PostgreSQL, vu qu’il ne se trouve pas forcément dans le PATH de chaque utilisateur :
Le répertoire de sauvegarde, indiqué par le paramètre backup_directory, est par défaut /var/backups/postgresql. Il peut être intéressant de le modifier pour qu’il pointe vers un autre disque, plus volumineux. Dans tous les cas, il doit exister, nous allons donc le créer :
La rétention se configure en nombre de jours (paramètre purge_older_than) et en nombre de sauvegardes (paramètre purge_min_keep). Par défaut, la rétention est de 30 jours. Il sera nécessaire de la modifier suivant la taille de la sauvegarde et la place disponible sur disque.
Pour tester la sauvegarde, nous allons créer deux bases et les peupler à l’aide de l’outil pgbench :
Maintenant, lançons une sauvegarde :
Voyons ce que pg_back a ajouté dans le répertoire de sauvegarde :
Nous avons les sauvegardes des bases b1, b2 et postgres, la sauvegarde des objets globaux, et même la sauvegarde des fichiers de configuration du serveur PostgreSQL. Configurons maintenant une rétention de deux sauvegardes :
Faisons maintenant plusieurs sauvegardes, puis regardons le nombre de sauvegardes de la base b1 :
Après la deuxième sauvegarde, nous avons deux fichiers pour b1. Après la troisième sauvegarde, nous n’en avons toujours que 2. En regardant le nom des fichiers, nous voyons bien la troisième sauvegarde (à 15:31:37), et la première sauvegarde (à 15:27:06) n’existe plus, elle a été supprimée.
Il ne reste plus qu’à exécuter pg_back grâce à la crontab, avec une fréquence dépendant de votre RPO (Recovery Point Objective), et à configurer une rétention plus en phase avec vos besoins.
Quant à la restauration, pg_back ne propose rien. Ça n’aurait pas vraiment de sens de toute façon. L’export des objets globaux se restaure avec l’outil psql et l’export des bases avec l’outil pg_restore.
2. Gérer les sauvegardes PITR avec pgbackrest
Un export logique a deux inconvénients forts. La fenêtre de perte de données peut être très importante. Tout dépend de la fréquence des sauvegardes, mais il est rare de voir des exports logiques réalisés fréquemment. Donc le RPO se mesurera généralement en jours, plus rarement en heures. L’autre inconvénient concerne la rapidité de remise en place du service. Je devrais d’ailleurs plutôt parler ici de lenteur. Importer une sauvegarde logique prend du temps. Plus il y aura d’index et de contraintes dans la sauvegarde logique, plus l’import sera long. Et donc, le RTO (Recovery Time Objective) est lui aussi important.
Si vous avez besoin d’un RPO et d’un RTO minimal, il est pratiquement obligatoire de se tourner vers les sauvegardes PITR. Le RPO est minimal, car, après la sauvegarde, toute modification dans la base est archivée pour pouvoir être rejouée le cas échéant. Le RTO est minimal, car il n’est pas nécessaire de reconstruire les index et de vérifier les contraintes.
L’outil proposé par les développeurs de PostgreSQL pour une sauvegarde PITR simplifiée est pg_basebackup. Cependant, là aussi, la rétention n’est pas gérée et la restauration nécessite de réaliser plusieurs actions.
D’autres outils sont apparus, les plus connus étant barman et pgbackrest. Ce dernier proposant plus de fonctionnalités, nous allons nous pencher sur lui.
Son installation nécessite l’activation du dépôt EPEL. Cela se fait ainsi :
Il ne reste plus qu’à installer le paquet pgbackrest :
La configuration se trouve dans le fichier /etc/pgbackrest.conf. Ce dernier ressemble à un fichier INI avec des sections de configuration (dont le nom est entre crochets) et une série de paramètres par section, avec le format standard « paramètre = valeur ».
Dans la section global, un paramètre essentiel est repo1-path. Il indique où seront stockées les sauvegardes. Nous allons conserver la valeur par défaut.
Chaque instance à sauvegarder doit avoir sa propre section, appelée par la suite stanza. Cette section indiquera comment se connecter à l’instance à sauvegarder et permettra de personnaliser les options de sauvegarde de l’instance. Ajoutons-en une dans le fichier de configuration par l’intermédiaire de ces deux lignes :
Ceci fait, nous devons initialiser la stanza. Cela se fait par l’intermédiaire de l’action stanza-create :
Cette commande a créé quelques sous-répertoires dans le répertoire indiqué par le paramètre repo1-path.
L’action check nous permet de vérifier notre configuration :
La stanza est bien décrite, pgbackrest a pu se connecter à l’instance, mais il s’est rendu compte que le paramètre archive_mode de l’instance PostgreSQL n’était pas configuré. Il faut en effet configurer l’archivage dans PostgreSQL. Pour cela, nous allons réaliser ces deux ajouts dans le fichier linuxpratique.conf :
Pour que ces modifications soient prises en compte, il faut redémarrer le serveur PostgreSQL :
Relançons maintenant la vérification de notre configuration :
Pas de message, tout va bien. Demandons plus d’informations :
Parfait. L’erreur sur le fait qu’il n’y ait pas de sauvegardes valides est logique, vu que nous n’avons pas encore effectué de sauvegarde. De ce fait, nous allons maintenant pouvoir lancer une sauvegarde. Cela se fait avec l’action backup :
Nous avons un premier message d’avertissement nous indiquant que nous n’avons pas configuré de rétention, et que cela peut s’avérer dangereux pour l’espace disque disponible. Le deuxième message d’avertissement indique que pgbackrest fait une sauvegarde complète vu qu’il n’y en a pas encore. En effet, par défaut, pgbackrest fait des sauvegardes incrémentales. Si nous voulons un autre type de sauvegarde (complète ou différentielle), il faut le spécifier avec l’option --type qui prend en argument le type de sauvegarde souhaité.
Ceci étant dit, la sauvegarde s’est bien terminée :
La rétention se configure avec le paramètre repo1-retention-full. Par exemple :
Si nous lançons une deuxième sauvegarde avec cette nouvelle configuration, nous nous trouvons cette fois-ci avec une sauvegarde incrémentale :
Nous pouvons faire autant de sauvegardes que nous le souhaitons, la rétention n’entre pas en ligne de compte, car elle ne concerne que les sauvegardes complètes. Par contre, si je fais deux nouvelles sauvegardes complètes, seules les deux dernières sauvegardes complètes seront conservées :
Nous remarquons d’ailleurs que cela a forcé la suppression des sauvegardes incrémentales entre la première sauvegarde (réalisée à 17h31, et qui a été supprimée pour respecter la configuration de la rétention) et la deuxième sauvegarde (lancée à 17h41).
Reste à tester la restauration. Disons qu’à 18h02, nous avons ajouté une troisième base :
Et qu’à 18h05, j’ai supprimé par erreur la base b2 :
Je souhaite restaurer ma sauvegarde et rejouer l’activité jusqu’à avant la suppression de b2, donc un peu avant 18h05. Pour cela, nous allons utiliser l’action restore en lui précisant la cible de restauration. Il faut tout d’abord arrêter PostgreSQL :
Puis supprimer le contenu du répertoire de données et du répertoire des journaux de transactions :
Nous pouvons enfin lancer la restauration en précisant la cible datée :
Comme l’indique le message d’avertissement, le répertoire pg_wal n’est plus le lien symbolique renvoyant vers la bonne partition. Nous allons donc changer cela :
L’action restore de pgbackrest a rempli le fichier postgresql.auto.conf avec le paramétrage de restauration :
et il a créé le fichier recovery.signal :
Il ne reste plus qu’à démarrer PostgreSQL :
Au bout d’un petit moment, nous pouvons nous connecter et constater la réapparition de la base supprimée :
Nous avons donc bien un système qui nous permet de restaurer jusqu’à un moment spécifique situé plus loin que la fin de la sauvegarde.
Il existe énormément plus d’options pour pgbackrest, à tel point qu’il est difficile de les connaître toutes. Heureusement, la documentation (https://pgbackrest.org/command.html) est bien écrite et exhaustive. Il est aussi à noter que Stefan Fercot, un contributeur majeur de pgbackrest, a fait de nombreuses conférences sur le sujet, et certaines sont disponibles sur YouTube. Son blog contient aussi plusieurs articles sur cet outil (https://pgstef.github.io/).
3. Exports logiques ou sauvegardes PITR ?
Maintenant que nous avons discuté des deux outils, lequel doit-on vraiment installer ? Mon avis personnel serait les deux. Même si ce sont tous les deux des outils de sauvegarde, ils ne font pas la même chose, ils ne remplissent pas le même besoin.
Un export logique pourra être restauré sur n’importe quel serveur, quelle que soit son architecture côté matériel et côté logiciel. Il pourra être restauré sur une version majeure de PostgreSQL différente de celle de la sauvegarde. Il pourra être restauré partiellement.
Cependant, sa relative lenteur en sauvegarde comme en restauration fait qu’une sauvegarde PITR est très intéressante, car bien plus rapide, dans le cas d’un serveur de production problématique où seul un rechargement de la sauvegarde permettrait de reprendre la production. La sauvegarde PITR est aussi intéressante pour restaurer jusqu’à un certain point dans le temps, permettant à la fois de minimiser le RPO tout en évitant de rejouer des écritures erronées.
Bref, c’est à vous de faire ce choix. Le mien est fait : il faut faire les deux sauvegardes, mais pas avec la même fréquence. L’export logique se fait moins fréquemment, voire manuellement, pour régler des besoins spécifiques. La sauvegarde PITR est mise en place en permanence et la sauvegarde des fichiers se fait à une fréquence allant de tous les jours à toutes les semaines. Il est possible d’avoir une sauvegarde de fichiers encore moins fréquente, mais cela rendra la restauration plus lente et plus risquée.
4. Oups
L’article précédent [2] mentionnait d’activer la récupération de statistiques sur les routines en configurant le paramètre track_functions à la valeur on. C’est une erreur, ce paramètre n’est pas un booléen. Il peut avoir les valeurs none, pl ou all. La valeur pl permet de tracer l’activité sur les fonctions écrites dans un langage de procédure, la valeur all permet de tracer aussi les fonctions C et SQL. La valeur all est intéressante, mais la valeur pl peut s’avérer suffisante.
Références
[1] « Installation de PostgreSQL », Linux Pratique n°139 :
https://connect.ed-diamond.com/linux-pratique/lp-139/installation-de-postgresql
[2] « Configuration de base d’une instance PostgreSQL », Linux Pratique n°140 :
https://connect.ed-diamond.com/linux-pratique/lp-140/configuration-de-base-d-une-instance-postgresql