Un backup Kubernetes n’est pas une reprise après sinistre
Le 10 septembre 2026, deux ambassadeurs CNCF ont publié trois scénarios de panne reproductibles qui séparent le fait d’avoir des sauvegardes de celui de pouvoir réellement restaurer. Testez la restauration complète dans un cluster qui n’a jamais tourné, validez les données contre un résultat attendu et chronométrez le tout.
10 septembre 2026. Les ambassadeurs CNCF Saiyam Pathak et Saloni Narang publient un guide de reprise après sinistre Kubernetes fondé sur trois scénarios de panne reproductibles sur un portable. Leur thèse tient en une phrase : avoir des sauvegardes n’est pas pouvoir restaurer. Pourquoi c’est important : la plupart des équipes surveillent le statut Completed de leurs backups, et presque aucune ne répète réellement la restauration.
Les quatre couches d’une reprise
Pour qu’une reprise compte, quatre couches doivent revenir : les données, la charge de travail, le cluster et le trafic. Chaque couche a un outillage mature, et chacune se restaure bien isolément. La reprise échoue aux jointures entre les couches : un cluster restauré sans données, des données restaurées sans chemin de trafic, une définition d’application qui provisionne un volume vide. Les trois scénarios du guide cassent chacun une jointure.
Le labo est volontairement modeste : deux clusters (un de production, un de reprise qui existe avant la panne), un stockage d’objets compatible S3 hors des deux clusters pour que perdre l’un n’emporte pas les points de reprise, et Git avec un contrôleur GitOps pointé dessus. La charge de travail est un PostgreSQL au contenu connu — quatre lignes — pour que chaque restauration soit validée contre un résultat attendu, et non contre un tableau de bord vert.
Scénario 1 : vérifier qu’un backup contient des données
Un backup Kubernetes a deux parties distinctes : les définitions de ressources (le YAML) et les octets des volumes persistants. Les outils protègent les volumes par snapshots CSI, sauvegarde de système de fichiers ou déplacement de données vers un stockage externe. Le labo utilise cette dernière voie, avec Velero et son data mover.
La plupart des vérifications s’arrêtent au statut Completed du backup. Le guide propose d’aller une étape plus loin et de confirmer que les octets ont réellement bougé :
kubectl -n velero get datauploads -l velero.io/backup-name=$BACKUP \
-o custom-columns='NAME:.metadata.name,PHASE:.status.phase,BYTES:.status.progress.bytesDone'
# NAME PHASE BYTES
# guestbook-rehearsal-20260727001126-q2j9m Completed 47989888 Le data mover confirme que 47 989 888 octets ont quitté le cluster pour atterrir dans le stockage externe. Un outil qui ne peut pas produire ce chiffre pour un backup donné mérite un examen. La suppression du namespace — PVC compris — puis la restauration a rendu les quatre lignes en deux minutes. C’est le chemin heureux, et il cache trois choses qu’aucun outil ne fait automatiquement : protéger le volume ne rend pas une base cohérente côté application (il faut des hooks de flush ou de quiescence), restaurer sur une infrastructure différente exige des correspondances de storage class, et un statut Completed ne prouve pas que l’application démarrera avec les données attendues.
La frontière est nette : les outils de backup restaurent dans un cluster qui existe déjà. Ils ne créent ni le cluster, ni les nœuds, ni le réseau, ni les équilibreurs de charge, ni le DNS. C’est le rôle de l’infrastructure as code ou de Cluster API. Un plan de reprise qui commence par « restaurer le backup » doit dire dans quoi ce backup est restauré.
Scénario 2 : l’état déclaré n’est pas l’état stocké
Le cluster de production est éteint. Le cluster de reprise, qui existait avant la panne, a un contrôleur GitOps pointé sur Git et un outil de backup pointé sur le stockage partagé. Il n’a jamais exécuté l’application.
La synchronisation depuis Git réussit : le sync est Synced, le StatefulSet se déploie, le pod de base de données est Running et Ready, chaque tableau de bord est vert. La requête sur la base renvoie alors :
ERROR: relation "attendees" does not exist La base tourne, et elle est vide. Rien n’a mal fonctionné. Git ne contenait que les déclarations, alors Kubernetes a fait exactement ce que dit le YAML : créer un StatefulSet, créer un Service, et provisionner un volume neuf et vide pour le PVC. Le GitOps a reconstruit l’état déclaré à la perfection, et n’a restauré aucun des états stockés. Les deux outils sont requis parce qu’il y a deux choses différentes à ramener, et chaque outil n’en porte qu’une : Git stocke l’intention, les backups stockent l’état.
La restauration qui a produit des données validées a suivi trois étapes : supprimer l’application vide que le sync a créée, restaurer l’application volumes compris depuis le stockage de backup, puis valider les données contre le contenu attendu. La mesure est instructive : éteindre la production jusqu’aux données validées a pris quatre minutes en direct, et un peu moins de deux minutes en reprise répétée. Le moment où les tableaux de bord sont passés au vert n’était pas la reprise ; le moment où les données sont revenues et ont été vérifiées, oui.
Scénario 3 : la cohérence multi-volumes
Les applications à état réelles s’étendent sur plusieurs volumes : données de base plus WAL, partitions de broker de messages, jeux de réplicas. Le substitut du labo écrit des paires appariées — une commande d’ordre n sur un PVC, un paiement n sur un autre — cinq fois par seconde, avec un invariant : chaque paiement doit avoir sa commande.
Snapshoteur les deux volumes individuellement, à cinq secondes d’écart, a produit deux snapshots chacun ReadyToUse et individuellement parfaits. La restauration des deux et la comparaison des derniers numéros de séquence commités donnent :
last order committed : 108352 last payment committed : 108377
[FAIL] 25 payments have NO matching order.
[FAIL] Each snapshot succeeded. The restore is still wrong. Vingt-cinq paiements référencent des commandes qui n’existent pas. Aucun composant n’a échoué, chaque opération a rapporté un succès, et le point de reprise combiné décrit un instant qui n’a jamais existé. En production, cet écart de cinq secondes, c’est un outil de backup qui parcourt une liste de cent PVC un par un.
La réponse de l’API s’appelle VolumeGroupSnapshot, passé GA dans Kubernetes 1.36. Un seul objet sélectionne les PVC par label, et le pilote CSI reçoit une requête unique pour un point de reprise coordonné et crash consistent sur tous :
apiVersion: groupsnapshot.storage.k8s.io/v1
kind: VolumeGroupSnapshot
metadata:
name: ledger-group-snap
spec:
volumeGroupSnapshotClassName: csi-hostpath-groupsnapclass
source:
selector:
matchLabels:
group: ledger La restauration des snapshots membres du groupe et la reprise du vérificateur donnent cette fois 109169 de chaque côté, avec un verdict « [OK] Every payment has a matching order. » Les réserves valent pour le réel : le support est spécifique au pilote (la plupart des grands pilotes cloud ne l’implémentaient pas mi-2026), la mise en place est explicite (CRD et feature gates du contrôleur de snapshot), et crash consistent n’est pas application consistent : l’API retire le décalage temporel entre volumes, mais ne flushe pas la base.
Ce qu’est un vrai test de reprise
Un test de reprise n’est pas supprimer un pod et le regarder revenir : cela teste la réconciliation de charge de travail. Un test de reprise restaure une application à état complète dans une cible propre qui ne l’a jamais exécutée, valide les données et le parcours utilisateur contre des contenus attendus (et non contre des statuts de ressources), et mesure le tout avec un chronomètre.
Le guide pointe trois trous béants qu’aucun outil ne comble seul : il n’existe pas de contrat de bascule inter-cluster commun, pas d’unité de reprise standard pour une application (chaque outil trace la frontière différemment), et le succès du backup est traité comme preuve de reprise alors que les résultats de répétition de restauration ne sont presque jamais surveillés. L’initiative Cloud Native Business Continuity du TAG Operational Resilience de la CNCF cherche des contributeurs sur exactement ces sujets.
Verdict
Le guide de Pathak et Narang remet le doigt sur une vérité que la plupart des équipes évitent : le coût d’un backup n’est pas sa création, c’est la répétition de sa restauration. Si votre seul indicateur est le statut Completed de Velero, vous savez que les backups passent, pas qu’ils servent à quelque chose. Si vous gérez des applications à état, instaurez une répétition de reprise qui restaure dans un cluster neuf, valide les données contre un résultat attendu et chronomètre le tout — et traitez VolumeGroupSnapshot comme la réponse à la cohérence multi-volumes, à condition que votre pilote CSI l’implémente.