guest@6admin.io:~$

Un volume persistant n'est pas une donnée durable

Quelques jours après la cascade DNS du driver de log , le même nœud du prototype, le même défaut d’alimentation, mais une panne d’une autre nature, plus sournoise, parce qu’elle ne s’est pas manifestée tout de suite. Après un arrêt brutal de l’hyperviseur, la base MySQL de PowerDNS est repartie vide. Les enregistrements DNS des hôtes provisionnés, les A et les PTR créés par Foreman, avaient disparu. Et personne ne l’a remarqué, jusqu’à ce qu’un playbook Ansible échoue des heures plus tard en ne pouvant plus résoudre un nom d’hôte.

Une perte silencieuse

C’est le pire type de panne, celui qui ne lève aucune alerte et se découvre au plus mauvais moment. Les enregistrements de service de base avaient réapparu seuls, parce qu’ils sont recréés à chaque déploiement de façon idempotente par le script de setup; j’ai donc d’abord cru la zone intacte. Mais les A et PTR des hôtes, eux, sont créés dynamiquement par Foreman et ne sont jamais re-semés au déploiement: ils n’existaient plus et rien ne le disait.

dig @127.0.0.1 unhote.devops.lab +short   # NXDOMAIN

La zone ne contenait que les enregistrements de service, zéro hôte. Foreman, lui, avait tout: la base PostgreSQL avait survécu, les hôtes étaient toujours dans l’inventaire. Cette asymétrie, PostgreSQL de Foreman intact et MySQL de PowerDNS vidé, est le coeur de l’histoire.

Le mécanisme et ce que je peux prouver

L’arrêt brutal a rendu le datadir MySQL inexploitable et au redémarrage le conteneur est reparti sur un répertoire de données vide, s’est réinitialisé, PowerDNS a recréé son schéma et le setup a reposé les enregistrements de base. Le datadir vivait dans un volume Docker nommé persistant, donc la perte ne vient pas d’une suppression de volume et j’ai vérifié ce point avant d’accuser quoi que ce soit: le down du projet ne fait qu’un stack rm, qui ne touche pas aux volumes et seule une commande de nettoyage explicite, interactive, les aurait purgés.

Le chemin exact de la corruption, je ne peux pas le démontrer avec les éléments dont je dispose: dommage sur le tablespace InnoDB, corruption du système de fichiers invité sur le disque virtuel, cache d’écriture de l’hyperviseur ne garantissant pas la durabilité des fsync, les trois sont plausibles. Le fait établi, lui, est net: les données n’ont pas survécu à l’arrêt non propre et la honnêteté consiste à s’arrêter là plutôt qu’à broder une cause racine que les traces ne soutiennent pas. L’asymétrie entre MySQL et PostgreSQL est d’ailleurs une piste en soi, une différence de garanties de flush entre les deux moteurs sous un cache disque qui ment sur la durabilité, mais je la laisse au rang d’hypothèse à instruire, pas de conclusion.

La vraie cause n’est pas la coupure

Le déclencheur environnemental, je l’avais déjà traité dans l’incident précédent. Ce qui rend celui-ci grave, c’est que la perte ait été totale, silencieuse et non récupérable par construction et cela tient à trois manques qui n’ont rien à voir avec l’électricité.

Les enregistrements d’hôtes ne vivaient que dans MySQL et n’étaient jamais regénérés au déploiement, donc toute perte du volume équivalait à une perte de tout le DNS des hôtes. Il n’existait aucune sauvegarde de cette base: le volume était absent du script de backup, qui ne dumpait que deux autres services et le script de restauration référençait d’ailleurs un nom de volume qui n’existait pas, si bien qu’une restauration aurait échoué même si une sauvegarde avait existé. Et il n’y avait aucune détection: pas d’alerte, des healthchecks commentés dans le compose, une perte découverte par le hasard d’un playbook qui tombe.

Ce qui a sauvé l’affaire

La récupération a tenu à une seule chose, que Foreman soit la source de vérité et qu’il ait survécu. Un Rebuild Config sur la partie DNS, en action groupée sur les hôtes managés, a reprojeté les A et les PTR une fois la zone inverse recréée.

Au passage, l’incident a mis à nu un bug qui dormait: la variable qui définit la zone inverse n’était pas renseignée, donc la zone inverse n’avait jamais été créée et les PTR n’avaient jamais eu d’endroit où atterrir. Il a fallu la définir, l’ajouter à l’environnement du setup, redéployer et supprimer au passage une zone parasite créée par la variable vide. C’est le genre de détail qu’on ne voit que le jour où l’on doit tout reconstruire.

Le volume a survécu, pas les données

Un volume persistant n’est pas une donnée durable: sans flush honoré de bout en bout et sans sauvegarde, un arrêt brutal peut tout effacer et le mot “persistant” ne protège que contre la suppression, pas contre la corruption. Une donnée qui n’est ni sauvegardée ni regénérable depuis sa source de vérité est une donnée à risque de perte définitive et ici la chance a simplement tenu à ce que la source de vérité ait survécu, ce qui n’est pas une stratégie. Une perte silencieuse est pire qu’une panne visible, parce qu’une panne visible, au moins, se présente d’elle-même.

Ce que j’en ai tiré, concrètement: un dump logique de la base DNS ajouté au backup et une restauration testée, les healthchecks réactivés, une alerte sur un enregistrement canari qui cesse de résoudre ou sur le nombre d’enregistrements de la zone qui s’effondre et surtout la formalisation de Foreman comme source de vérité et de PowerDNS comme simple projection, avec une reprojection documentée en un geste après toute perte. La durabilité côté hyperviseur, le cache disque qui doit honorer les fsync, reste le point de fond, couplé à l’arrêt ordonné déjà réclamé la fois précédente.