Strasbourg, le 29 mars 2024

Le 31 mars prochain tombera une fois de plus le World Backup Day. Nous en avions déjà parlé ici (ici, et ). Mais les articles de ce genre noient le lecteur sous tant d’informations et d’impératifs que le plus important est oublié, tout à la fin :

« Testez vos sauvegardes (régulièrement) ! ».

Certains de nos clients procèdent religieusement à des restaurations logiques ou physiques en environnement de test, avec une procédure bien documentée, et même automatisée. D’autres ont écrit une procédure… quelques années auparavant, ou en ont hérité d’un collègue parti ailleurs. Beaucoup sont conscients qu’il faudra la relire ou la faire, cette procédure, un jour, quand on aura un peu de temps.

L’expérience montre qu’il y a toujours plus important, urgent ou valorisant. Et un jour, ou une nuit, il faut la découvrir ou l’inventer, dans l’urgence, en production, avec des utilisateurs furieux au téléphone.

Voyons quelques affirmations, étonnements et mensonges de divers DBAs et non-DBAs qui ont voulu restaurer une sauvegarde.

« Nous avons des sauvegardes »

Déjà, pour restaurer une sauvegarde, il faut l’avoir, et la retrouver.

Tout bêtement, savez-vous où partent les sauvegardes des bases, au sein de votre gigantesque infrastructure aux droits très restrictifs ? Le serveur sauvegardé ne sera peut-être plus là pour vous indiquer où il écrit. Et y avez-vous accès sans passer par lui ?

« Nous faisons chaque nuit/semaine une sauvegarde »

Et vous l’avez planifiée toutes les nuits, que ce soit avec pg_dump, pg_back, pg_basebackup, pgBackRest, Barman, votre outil propriétaire favori, ou un snapshot de baie.

Mais avez-vous bien vérifié le lendemain que la sauvegarde était là ? Avez-vous vu cette faute de frappe dans /etc/crontab ou dans la ligne de commande ? (Ça arrive aux meilleurs.)

Les mois d’après, avez-vous détecté la saturation de /mnt/pgbackups ? Cette rétention de 6 mois était peut-être excessive, surtout sans aucune purge. Ou la base a simplement grossi beaucoup plus que prévu. Ou le montage NFS ne remonte peut-être pas tout seul au reboot du serveur, et n’a plus que des sauvegardes… de janvier à juin 2021.

Échaudé par cette histoire de saturation, vous pensez écrire un script, qui commence par effacer les sauvegardes précédentes. Problème : si la nouvelle sauvegarde échoue ou est corrompue, vous n’aurez plus rien.

N’allez pas plus loin : par expérience, tout script de sauvegarde « maison » teste insuffisamment les retours d’erreur des commandes ; ou bien il sera trop complexe pour être maintenu de manière fiable pendant des années par les prestataires qui se succéderont. Il vaut mieux laisser la gestion des rétentions à des outils prévus pour cela, par exemple pg_back (sauvegardes logiques) ou pgBackRest (sauvegardes physiques).

Il existe quelques outils pour vérifier au moins l’existence des dernières sauvegardes, et leur âge minimal ou maximal, par exemple la sonde pg_dump_backup de check_pgactivity, ou check_pgbackrest, à intégrer à votre supervision.

Mais si personne ne réagit quand la sonde passe au rouge, ni ne lit les mails d’échec d’une sauvegarde (si même ces mails arrivent), ou si votre structure a besoin de six mois pour obtenir un espace de 8 To, votre problème n’est pas simplement technique.

« pg_dump n’a pas sauvegardé ! »

pg_dump est fiable, mais pas infaillible. Il peut tomber en erreur si la base contient des bytea de 600 Mo ou plus (une mauvaise idée), ou des centaines de milliers de Large objects (une idée encore plus mauvaise).

« pg_dump n’a pas sauvegardé à cause d’un problème de version ! »

pg_dump: error: aborting because of server version mismatch

Cause : vous n’avez pas supprimé tous les paquets de l’ancienne version de PostgreSQL lors de la dernière migration majeure, ni modifié votre ancienne ligne de commande. pg_dump version 9.6 tente donc de sauvegarder votre instance version 16, en est incapable, et envoie un message d’erreur. L’aviez-vous reçu ?

Pour vous consoler, sachez que vous êtes en bonne compagnie.

« pg_dump a sauvegardé mais la sauvegarde est corrompue ! »

Nous arrivons dans la zone tragique et traumatisante des sauvegardes présentes, mais inutilisables.

Si votre script se marche sur les pieds et efface chaque nuit la sauvegarde de la veille qui n’est même pas terminée (car elle dure à présent 26 h…), vous aurez peut-être ceci à la restauration :

pg_restore: error: could not open input file "erp20240317.dump/": No such file or directory

Gênant en test. Très très gênant à la restauration d’une production.

« pg_restore a un problème de version ! »

Ce problème est moins grave qu’il n’en a l’air :

pg_restore: error: unsupported version (1.15) in file header

Vous n’étiez peut-être pas conscient que quelqu’un avait installé les paquets de la version 16 sur cette vieille instance 9.6. Depuis, toutes les sauvegardes sont au format 16.

La bonne nouvelle est qu’il est possible d’utiliser pg_restore version 16 pour restaurer. Il faut juste avoir l’esprit assez clair pour y penser, trouver le bon chemin, et ne pas paniquer quand quelques messages d’erreur inoffensifs apparaissent, ou savoir modifier le DDL. Plus facile lors d’un test que lors d’un appel d’astreinte à 3 h du matin.

« pg_restore me renvoie plein d’erreurs d’intégrité ! »

pg_dump garantit une sauvegarde cohérente des données au moment du début de la sauvegarde. Mais excluez-en des tables trop grosses ou soi-disant inutiles et l’outil ne peut plus rien garantir. Il vous fait confiance.

Plus inquiétant mais bien plus rare : vous avez réellement une erreur d’intégrité dans vos données à cause d’un vieux bug. Des tests de restauration garantissent que cette incohérence ne sera pas découverte en période de crise.

« pg_restore n’a pas restauré les Large Objects ! »

Étaient-ils dans la sauvegarde ?

Si la commande pg_dump exclut une table ou un schéma, il fallait penser à rajouter l’option --blobs pour sauvegarder les Large Objects.

« Nous faisons juste des archives de tous les journaux »

Certes, une sauvegarde physique commence par la mise en place de l’archivage des journaux de transactions, mais ce n’est que le début.

Pour redérouler ces journaux, il faut un point de départ nommé base backup (c’est-à-dire une copie physique, périodique, des fichiers de données). La récupération du base backup est le premier point de toute procédure de restauration physique.

Les journaux ne permettent pas non plus de « rembobiner » magiquement votre instance PostgreSQL.

« J’ai restauré avec succès mon snapshot, mais certaines tables sont inaccessibles ! »

ERROR:  could not open file "pg_tblspc/5016395/PG_16_202307071/5/5025000": No such file or directory

Quel que soit le type de snapshot (LVM, baie…), il est nécessaire d’inclure tous les points de montage dans le snapshot, et de manière parfaitement synchronisée (atomique). Ici, PostgreSQL ne trouve tout simplement pas un tablespace entier. Avec de la chance, c’était celui des fichiers temporaires…

Notons aussi que PostgreSQL avait bien redémarré après la restauration. Un test de restauration doit contrôler qu’il n’y a pas d’erreur dans les traces.

« J’ai restauré avec succès mon snapshot/ma sauvegarde physique, mais PostgreSQL panique ! »

LOG:  creating missing WAL directory "pg_wal/archive_status"
LOG:  invalid checkpoint record
PANIC:  could not locate a valid checkpoint record

Par exemple, PostgreSQL a trouvé un répertoire pg_wal vide. Faute de journaux de transactions, il a préféré ne pas aller plus loin.

Dans ce cas, soit vous découvrez (ou écrivez) la procédure de restauration, et vous avez juste oublié de paramétrer restore_command qui indique à PostgreSQL comment récupérer les journaux archivés, et vous allez vous en sortir…

…soit votre snapshot a oublié la partition des journaux (pg_wal/), alors que vous n’avez pas d’archive des journaux ; ou encore vous avez fait une simple copie physique à chaud (un bête cp -R par exemple) sans les incantations nécessaires.

Si même vous réussissez à redémarrer en utilisant une tronçonneuse, le remède est souvent pire que le mal, avec une base souvent corrompue de manière silencieuse… corruption découverte toujours trop tard.

« La restauration physique n’atteint jamais le point de cohérence ! »

Quand je restaure une sauvegarde PITR, je pousse toujours un soulagement quand apparaît ceci dans les traces :

LOG: restored log file "000000010000000100000032"…
LOG: consistent recovery state reached at 1/32BFDD88

Cela signifie qu’il ne manque pas de journaux pour obtenir une instance utilisable. La restauration n’est pas finie, loin de là, mais tout n’est pas perdu.

Sans point de cohérence, pas d’ouverture de la base. Le problème est courant dans les scripts de sauvegarde PITR mitonnés avec pg_backup_start et oubliant pg_backup_stop. Mais qui fait cela de nos jours, quand pg_basebackup, pgBackRest, Barman ou l’outil de votre baie le font volontiers pour vous ?

« La restauration physique se plaint qu’il manque un journal ! »

Si, huit jours plus tôt, le serveur d’archivage a planté sans enregistrer sur disque un journal pourtant bien reçu (dans son cache…), et sans sauvegarde intermédiaire des fichiers, le flux de restauration ne peut restaurer au-delà d’il y a huit jours. L’instance sera peut-être fonctionnelle, mais cela fait beaucoup de données perdues.

Les outils de sauvegarde courants ont quelques astuces pour éviter ce problème (ne serait-ce qu’un simple sync à l’enregistrement), mais le mieux est de vérifier périodiquement que tous les journaux sont bien présents.

Pour pgBackRest, existe l’outil check_pgbackrest. Les plus prudents activeront même ses fonctionnalités multi-dépôt, et feront fréquemment des sauvegardes incrémentales ou différentielles.

Notez que cette perte de journal est parfois pour votre bien : si vous lui demandez, pgBackRest peut débrayer un archivage trop lent pour éviter la saturation du disque de l’instance primaire.

« La restauration physique a buggé ! »

Les outils de sauvegarde physique sont des outils complexes, car ils tentent d’aller le plus vite possible et de sécuriser au maximum les sauvegardes. Donc ils ont parfois des bugs, et des correctifs réguliers.

De toute manière, vous mettez à jour PostgreSQL tous les trimestres, n’est-ce pas ? Ajoutez donc la mise à jour de pg_back, pgBackRest, Barman, ou des outils de sauvegarde de votre baie.

« Je ne comprends rien à toutes ces options de restauration ! »

Variante : « Je ne comprends rien à toutes les options de pgBackRest ! ».

C’est normal. La pléthore des paramètres et les documentations un peu arides s’appréhendent mieux à tête reposée en rédigeant et rejouant la procédure de restauration, pas à 23 h après une mise en production ratée.

« pg_restore restaure… mais c’est long ! »

Vos procédures de sauvegarde logique et restauration sont au point et complètes, vous supervisez vos sauvegardes, ça ronronne depuis longtemps… et depuis la base est devenue imposante. Savez-vous combien de temps il faut pour la recharger ?

pg_restore doit non seulement charger des données, mais aussi tout réindexer, et recompiler les vues temporaires, et il ne faudra pas oublier un VACUUM ANALYZE. Vos utilisateurs attendront donc longtemps la fin de la restauration, encore plus si vous n’avez pas ressenti le besoin de chercher les quelques astuces pour accélérer.

« pg_restore a restauré une version des données beaucoup trop ancienne ! »

pg_restore restaure les données telles qu’au début de sa sauvegarde, et il est rarement planifié plus d’une fois par jour. Donc si votre RPO (quantité de données perdues acceptables) correspond à 5 minutes d’activité, vous auriez dû utiliser une sauvegarde PITR.

« La restauration physique, c’est quand même long ! »

Une restauration physique démarre par la copie d’un jeu de fichiers pas trop anciens. S’il s’agit de 8 To, ou s’il faut passer par une liaison intercontinentale, ce ne sera pas instantané. Même restaurer un snapshot peut être très long si les performances des disques ne sont pas optimales. N’oubliez pas la facture en volume de transfert dans le cloud.

S’il s’agit de restaurer sur une base existante, rsync ou le mode --delta de pgBackRest accélèrent énormément les choses. Saviez-vous que vous en aurez besoin ?

« La restauration physique, c’est quand même long ! (bis) »

Une restauration physique doit rejouer un par un tous les journaux nécessaires. Même avec l’archive-get asynchrone de pgBackRest, et les améliorations des dernières versions de PostgreSQL, restaurer 90 000 journaux est long (certes moins s’il en manque un au milieu…).

Des sauvegardes différentielles ou incrémentales chaque nuit peuvent prendre de la place, mais c’est du temps d’indisponibilité économisé.

« La restauration physique a fini, mais des tables sont vides ! »

Les tables non journalisées (UNLOGGED) sont plus rapides à écrire car elles ne sont pas journalisées, mais leur contenu est perdu en cas de problème… ou de restauration. Elles ne sont pas destinées à contenir des données pérennes. Certains ont essayé, ils ont eu des problèmes.

« Nous n’avons restauré qu’une seule base ! »

pg_dump travaille par base de données. Vous avez peut-être oublié de rajouter la sauvegarde des autres bases à leur création ?

« Nous avons écrasé les autres bases de données ! »

À l’inverse, une sauvegarde physique travaille sur l’instance entière. La restauration d’une base entraîne celles des autres. En étiez-vous conscient ?

« PostgreSQL ne comprend pas mon recovery.conf ! »

FATAL:  using recovery command file "recovery.conf" is not supported

Ceci est le signe que votre procédure n’a pas été modifiée après votre dernière migration vers PostgreSQL 12 ou supérieur. recovery.conf n’est plus utilisé depuis.

« Je viens d’écraser la mauvaise instance ! »

Devoir gérer un incident de production est stressant, et sous le stress on commet des erreurs stupides. Une procédure claire et régulièrement testée réduit les risques. Prévoyez des sécurités pour que l’utilisateur qui se trompe de serveur ne puisse pas tout effacer sans le vouloir consciemment.

(1 an après) « Pourquoi PostgreSQL réécrit-il toute la base ? »

Un défaut de PostgreSQL est la nécessité de recycler périodiquement les numéros de transaction. Cela peut être lourd et douloureux si des milliards de lignes, importées en même temps, portent le même numéro.

Lors de la restauration d’une grosse base, la procédure doit finir par planifier un ou plusieurs VACUUM FREEZE les mois suivants, aux horaires où il gênera le moins.

« Ça marchait quand nous avons testé la procédure ! »

Les sauvegardes physiques problématiques, qui marchaient par chance sur la petite base de développement de 2 Go sans activité, marchent moins bien sur une production de 3 To deux ans plus tard lourdement chargée.

Testez régulièrement vos sauvegardes. Restaurez en développement, par exemple : vos développeurs doivent avoir une base représentative de la production de toute manière.

« Où est la procédure ? »

Rejouer souvent les procédures permet de se rafraîchir la mémoire, de résoudre les problèmes de droits d’accès aux outils internes, et de transmettre les consignes dans un contexte de turn-over élevé.

« Cette procédure est incompréhensible ! »

Une bonne idée est de la faire tester à tout nouvel arrivant, peu au fait du contexte et de l’historique. Il doit pouvoir facilement trouver toutes les informations nécessaires.

« Il manque les fichiers de configuration ! »

Les données sont l’essentiel. Vous pouvez reconstituer le reste de zéro si vous l’avez perdu/oublié : le postgresql.conf optimisé il y a des années (absent des sauvegardes logiques voire de certaines sauvegardes physiques), le pg_hba.conf de gestion des accès ou la liste des utilisateurs de la base et leurs mots de passe (à sauvegarder séparément de pg_dump).

Ça va juste prendre un certain temps.

« Il manque la VM ! »

Toute restauration suppose une infrastructure en place. Or, le cas d’un sinistre total impose de repartir de zéro.

Si vous avez une vieille installation sur CentOS 6 ou Debian 9, dont les dépôts ont disparu, avec des extensions exotiques compilées à la main, du temps va passer avant de pouvoir même commencer la restauration.

Même sur une installation récente, il faut être sûr d’avoir documenté (ou défini dans un outil comme Ansible) le paramétrage noyau, les noms des points de montage, les scripts utilisés pour la maintenance…

« Je n’ai pas de machine pour tester la restauration »

Faute de mieux, même une vieille machine à disque mécanique (avec assez de disque) suffit à tester la restauration de grosses instances.

« On a des sauvegardes, mais j’aimerais mieux pas tester la restauration »

Si un collègue/client/vous-même sort cela froidement ou cyniquement, vous savez ce qu’il faut faire : passer l’après-midi à restaurer sur des machines de test ; ou harceler des collègues pour vous permettre de le faire ; ou peaufiner votre CV en prévision du jour d’après la destruction de votre production.

« Notre procédure consiste à basculer sur l’instance secondaire »

Un secondaire n’est pas une sauvegarde. Certes, si le disque de votre serveur primaire décède subitement, un secondaire pourra effectivement prendre le relai rapidement.

Mais ce secondaire réplique fidèlement le DROP DATABASE du développeur stagiaire qui s’est connecté sur la mauvaise machine. Impossible de revenir en arrière.

« Quels cas doit prévoir la procédure ? »

Vous devez prévoir tout ou partie des scénarios suivants, ou choisir de les ignorer en toute connaissance de cause :

  • retour en arrière de la production à un point précis dans le temps (une heure ? une semaine ?) suite à une mauvaise manipulation, une malveillance ;
  • restauration sur le serveur de production comme sur un autre ;
  • restauration d’une seule des bases de l’instance ;
  • reconstruction d’une instance après destruction du serveur primaire ;
  • idem, après destruction ou piratage du data center entier ;
  • idem, après destruction des sauvegardes en ligne comprises (ransomware).

Pour chaque cas, fixez les RTO/RPO acceptables. Tenez compte de la règle des 3/2/1 (au moins 3 copies des données sur 2 médias dont 1 site extérieur). Vérifiez l’intégrité.

« Tout le monde prend vraiment le temps de tester tout cela ? »

Hélas non, comme toute tâche fastidieuse qui semble pouvoir être indéfiniment repoussée.

Mais plus elle est connue et pratiquée, moins elle est fastidieuse.

Moralité

Il vaut mieux rater ses restaurations en test seul devant son PC, qu’en production devant le big boss.

Une vraie catastrophe est souvent un enchaînement d’erreurs et d’arrangements avec la rigueur, comme dans le célèbre post-mortem de Gitlab de 2017 qui nous a servi de fil rouge.


DALIBO

DALIBO est le spécialiste français de PostgreSQL®. Nous proposons du support, de la formation et du conseil depuis 2005.