kubectl rollout undo a tout du bouton « annuler » : mauvais déploiement, une commande, retour au calme. C’est à peu près le cas. Mais ce qu’il restaure, jusqu’où il peut remonter et ce qui se passe quand un contrôleur GitOps surveille le même Deployment obéissent à des mécaniques qu’il vaut mieux comprendre avant le mauvais déploiement. Voici comment les rollbacks de Deployment Kubernetes fonctionnent réellement : où vivent les révisions, ce que rollout undo restaure ou non, et pourquoi dans un pipeline GitOps le vrai rollback est un revert Git.
Les révisions sont des ReplicaSets, pas des snapshots
Un Deployment ne conserve pas l’historique de ses specs passées. À chaque modification du template de pod (spec.template : image, variables d’environnement, sondes, labels des pods), le contrôleur de Deployment calcule un hash du nouveau template et crée un ReplicaSet nommé d’après ce hash, puis scale le nouveau ReplicaSet vers le haut et l’ancien vers le bas selon la stratégie de rollout. L’ancien ReplicaSet n’est pas supprimé : il reste à 0 réplica, tout en conservant le template de pod exact avec lequel il a été créé.
Ce ReplicaSet mis à la retraite est la révision. La comptabilité tient dans une seule annotation :
$ kubectl get rs -l app=my-app
NAME DESIRED CURRENT READY AGE
my-app-5d8c7b9f6d 0 0 0 2d # revision 1
my-app-7f9b4c8d5e 0 0 0 1d # revision 2
my-app-6c5d9e8f7a 3 3 3 2h # revision 3
$ kubectl get rs my-app-7f9b4c8d5e \
-o jsonpath='{.metadata.annotations.deployment\.kubernetes\.io/revision}'
2
kubectl rollout history deployment/my-app n’est en substance qu’un formateur d’affichage au-dessus de ces annotations. Deux conséquences en découlent directement :
- Une révision n’enregistre que le template de pod. Le nombre de réplicas, les réglages du HPA, les labels et annotations propres au Deployment, et tout ce qui vit en dehors de
spec.templaten’en font pas partie. - Si le ReplicaSet a disparu, la révision aussi. Il n’en existe aucune autre copie.
Une subtilité : si vous déployez un template identique à celui que détient déjà un ancien ReplicaSet, le contrôleur réutilise ce ReplicaSet au lieu d’en créer un doublon, et son annotation de révision est incrémentée au numéro le plus récent (les numéros précédents sont conservés dans une annotation deployment.kubernetes.io/revision-history). Les révisions se dédupliquent par contenu.
Ce que rollout undo fait réellement
# retour à la révision précédente
kubectl rollout undo deployment/my-app
# retour à une révision précise
kubectl rollout history deployment/my-app
kubectl rollout history deployment/my-app --revision=2 # inspecter avant de sauter
kubectl rollout undo deployment/my-app --to-revision=2
rollout undo retrouve le ReplicaSet de la révision cible, prend son template de pod et le réinjecte dans le spec.template du Deployment. C’est tout. Il n’existe pas de marche arrière spéciale : le patch déclenche un rolling update ordinaire vers l’avant jusqu’à l’ancien template, en utilisant la strategy, les sondes, le maxSurge et le maxUnavailable habituels du Deployment. Un rollback est exactement aussi rapide, et exactement aussi sûr, que vos rollouts habituels. Si vos déploiements perdent des requêtes en cours de route, vos rollbacks aussi, une raison de plus de bien régler votre rolling deployment avant d’en avoir besoin sous pression.
La numérotation des révisions après un undo surprend. Revenir à la révision 2 ne « ramène pas à la révision 2 » : le template de la révision 2 devient la nouvelle révision la plus récente (disons la révision 4), et l’entrée d’historique du 2 disparaît, son contenu vit désormais tout en haut. Les numéros de révision ne font que croître ; rollout history est un journal de templates, pas une chronologie que l’on rembobine.
$ kubectl rollout undo deployment/my-app --to-revision=2
deployment.apps/my-app rolled back
$ kubectl rollout history deployment/my-app
REVISION CHANGE-CAUSE
1 <none>
3 <none>
4 <none> # c'est l'ancienne révision 2, renumérotée
Ce qu’un rollback ne restaure pas
Puisqu’une révision n’est qu’un template de pod, plusieurs choses que l’on attend d’un « annuler » sortent de son périmètre.
Le nombre de réplicas
Le scaling ne crée pas de révision : replicas vit en dehors de spec.template, donc kubectl scale ne laisse aucune trace dans rollout history. De façon symétrique, rollout undo ne touche jamais au nombre de réplicas : si vous êtes passé de 3 à 10 après le mauvais déploiement, vous êtes toujours à 10 après le undo. C’est généralement ce que vous voulez ; la surprise vient quand on s’attend à ce que le undo annule aussi un scale-down accidentel. Il ne le fera pas. Le scaling et le template sont deux axes indépendants.
ConfigMaps, Secrets, et tout ce qui entoure le pod
Le template de pod référence une ConfigMap ou un Secret par son nom. Si le mauvais déploiement a aussi modifié le contenu de la ConfigMap, revenir en arrière sur le Deployment remonte la même ConfigMap, toujours modifiée. Idem pour les migrations de base de données, les CRDs, les changements de RBAC, ou tout ce que votre release a touché en dehors de spec.template. rollout undo revient en arrière sur un champ d’un seul objet.
(La parade standard consiste à utiliser des noms immuables, hashés sur le contenu, comme app-config-7d9f2b, pour qu’un rollback de template ramène mécaniquement l’ancienne config. Le configMapGenerator de Kustomize et les annotations de checksum de Helm existent tous les deux pour cette raison.)
Tout ce qu’a fait un Deployment en pause
Vous ne pouvez pas revenir en arrière sur un Deployment en pause ; kubectl rollout undo refuse jusqu’à ce que vous fassiez kubectl rollout resume. Si vous utilisez pause/resume pour regrouper des changements, gardez en tête que la période de pause produit une seule révision, pas une par changement : la granularité du undo est le resume, pas l’édition.
revisionHistoryLimit : jusqu’où vous pouvez remonter
Le contrôleur de Deployment fait le ménage des anciens ReplicaSets au-delà de spec.revisionHistoryLimit, qui vaut 10 par défaut. Les révisions plus anciennes sont supprimées, et avec elles la possibilité d’y revenir :
$ kubectl rollout undo deployment/my-app --to-revision=1
error: unable to find specified revision 1 in history
Les deux extrémités du curseur :
revisionHistoryLimit: 0supprime immédiatement tout ReplicaSet inactif. Cela rend le rollback impossible, point final. Parfois proposé au nom du « nettoyage », c’est en réalité la suppression de votre filet de sécurité.- Une limite élevée ne coûte presque rien (les ReplicaSets retirés sont de petits objets d’API sans aucun pod), mais notez que les pipelines bavards consomment l’historique vite. Dix déploiements dans une journée chargée, et la révision known-good d’hier peut déjà être hors de portée le temps que quelqu’un remarque une régression lente.
Dix est une valeur par défaut raisonnable pour un pipeline piloté par le repo. Si votre stratégie de rollback dépend réellement de rollout undo (voir plus bas pourquoi ce ne devrait peut-être pas être le cas), dimensionnez la limite en fonction de votre fréquence de déploiement, pas de la valeur par défaut.
Le change-cause manquant, et savoir vers quoi on revient
rollout history a une colonne CHANGE-CAUSE qui affiche <none> sur la plupart des clusters, parce qu’elle est renseignée depuis l’annotation kubernetes.io/change-cause, que rien ne pose automatiquement. L’ancien flag kubectl --record qui la remplissait est déprécié depuis des années. Vous pouvez la poser à la main :
kubectl annotate deployment/my-app \
kubernetes.io/change-cause="release 2026-07-24, image v1.42.1"
L’annotation est copiée sur le ReplicaSet au moment du rollout, donc chaque révision garde sa propre cause. En pratique, les annotations maintenues à la main pourrissent. La manière fiable d’identifier une révision consiste à regarder son template réel :
kubectl rollout history deployment/my-app --revision=3
# ...affiche le template de pod : tag d'image, env, ce qui a réellement changé
Un undo à l’aveugle, c’est-à-dire rollout undo sans vérifier ce que contient la révision précédente, est un pari : celui que le déploiement précédent était le seul changement récent. Trente secondes d’inspection avec --revision=N sont une assurance bon marché ; cela vaut double quand l’historique a déjà été renuméroté par un undo antérieur.
rollout undo face à un revert Git
Si vos Deployments sont appliqués par un pipeline ou un contrôleur GitOps depuis des manifests dans Git, rollout undo pose un problème structurel : il modifie le cluster, pas le repo. Le Deployment dans le cluster et le manifest dans Git sont désormais en désaccord, et tout le mécanisme que vous avez construit existe précisément pour éliminer ce désaccord.
- Avec un pipeline CI (
kubectl applyau merge) : le undo tient jusqu’au prochain merge, moment où le pipeline réapplique les manifests, y compris le mauvais tag d’image que vous aviez fui. Le rollback se dé-fait silencieusement, peut-être des jours plus tard, déployé par un changement sans rapport. - Avec un contrôleur GitOps (Argo CD, Flux) : le contrôleur détecte le drift à sa prochaine synchronisation. Avec le self-heal activé, il annule votre rollback en quelques minutes. Le contrôleur fait son travail, qui est de rendre le cluster conforme à Git. Le combattre à coups de undo répétés, c’est combattre votre propre outillage ; le seul contournement est de désactiver l’auto-sync, ce qui sacrifie la garantie même pour laquelle GitOps existe.
Le rollback qui colle au modèle est un revert Git : reverter le commit qui a changé le tag d’image (ou le bump de release), laisser le pipeline ordinaire l’appliquer. Vous conservez la piste d’audit, la revue de diff, les checks CI qui gardent vos manifests, et surtout la garantie que Git décrit toujours ce qui tourne. Le compromis est honnête : un revert passe par le pipeline et prend quelques minutes, là où rollout undo agit en quelques secondes. Un motif d’urgence défendable consiste à utiliser rollout undo comme mesure d’arrêt qui stoppe l’hémorragie, puis à pousser immédiatement le revert Git pour que le repo s’accorde avec ce que vous venez de faire, et à traiter tout undo non suivi d’un commit comme une action de post-mortem, pas comme un correctif. Si votre pipeline est assez lent pour que cet écart paraisse dangereux, c’est le pipeline qu’il faut réparer ; rendre les déploiements et les rollbacks ennuyeux est exactement l’objectif de l’industrialisation de votre CI/CD.
Aide-mémoire
kubectl rollout history deployment/my-app # lister les révisions
kubectl rollout history deployment/my-app --revision=3 # en inspecter une
kubectl rollout undo deployment/my-app # reculer d'une révision
kubectl rollout undo deployment/my-app --to-revision=3 # revenir à une révision précise
kubectl rollout status deployment/my-app # regarder converger
Et les pièges en une passe : les révisions sont des ReplicaSets retirés et n’enregistrent que le template de pod ; le undo est un rolling update ordinaire vers l’avant et renumérote l’historique ; il ne restaure ni le nombre de réplicas ni les ConfigMaps ; revisionHistoryLimit (10 par défaut, jamais 0) borne la distance à laquelle vous pouvez remonter ; un Deployment en pause refuse de revenir en arrière ; et sous GitOps, le rollback durable est le revert Git, rollout undo n’en étant au mieux que la première moitié. D’autres ressources d’automatisation dans la catégorie automatisation.