Intermédiaire Chapitre 9-10 / 6

La cascade Deployment → ReplicaSet → Pod & Rolling updates : la réconciliation progressive

La cascade de trois réconciliateurs imbriqués Deployment → ReplicaSet → Pod, et le rolling update comme réconciliation progressive entre deux ReplicaSets — sans prérequis, avec YAML et kubectl.

La cascade Deployment → ReplicaSet → Pod

L’idée en une phrase

Un Deployment ne crée pas de pods : il pilote un ReplicaSet, qui pilote les pods. Trois réconciliateurs imbriqués forment une cascade où l’état désiré descend (Deployment.specReplicaSet.specPod.spec) et l’état réel remonte (status du pod → du ReplicaSet → du Deployment) ; chaque échelon exécute sa propre boucle observe → diff → agit sur l’échelon immédiatement inférieur, sans court-circuiter les autres.

Analogie : Considérons une chaîne de délégation dans une organisation. La direction fixe un objectif — « maintenir trois agences ouvertes » — sans recruter personne elle-même : elle mandate un responsable régional. Celui-ci crée et surveille les agences, qui embauchent à leur tour le personnel. Chaque échelon ne pilote que celui immédiatement en dessous et ignore les détails des autres. La direction est le Deployment, le responsable régional le ReplicaSet, le personnel les pods.

Points clés

  • Un Deployment décrit l’état désiré d’une application : l’image, le nombre de replicas et la stratégie de mise à jour. Son contrôleur n’observe pas les pods ; il maintient un ReplicaSet conforme à son spec.template.
  • Un ReplicaSet a une responsabilité unique : maintenir N pods correspondant à son selector (vu au chapitre 7-8). Il ignore les notions d’image ou de version — il compte et ajuste, rien de plus.
  • L’état désiré descend, l’état réel remonte : le Deployment transmet replicas et template au ReplicaSet, qui les transmet aux pods ; en retour, le status des pods agrège celui du ReplicaSet, puis celui du Deployment (AVAILABLE, UP-TO-DATE).
  • Chaque révision du template engendre un ReplicaSet distinct, identifié par un hash (pod-template-hash) ajouté au selector et aux labels des pods. Les anciens ReplicaSets sont conservés à 0 replica (paramètre revisionHistoryLimit) pour autoriser un retour arrière.
  • Les ownerReferences relient chaque pod à son ReplicaSet, et chaque ReplicaSet à son Deployment ; la suppression se propage de haut en bas (garbage collection, chapitre 25-26).

Exemple concret

Un Deployment hello déclare replicas: 3 et l’image nginx:1.27. À t₀, son contrôleur observe qu’aucun ReplicaSet ne correspond au template courant et crée hello-7d4b9c (désirant 3 pods). Le contrôleur de ce ReplicaSet observe 0 pod pour 3 désirés et crée hello-7d4b9c-aaa11, -bbb22, -ccc33. Trois objets, deux boucles : le Deployment n’a jamais « vu » un pod, il a seulement garanti l’existence d’un ReplicaSet conforme ; ce dernier a garanti l’existence des pods. Si l’un des trois pods est supprimé, c’est le contrôleur du ReplicaSet — non celui du Deployment — qui observe 2/3 et en recrée un. Le Deployment ne réagit, lui, qu’à un écart portant sur les ReplicaSets.

Trois échelons, trois réconciliateurs

ÉchelonÉtat désiré (sa spec)État réel observéAction pour combler l’écart
Deployment controllertemplate + replicas + stratégieLes ReplicaSets qu’il possèdeCrée le ReplicaSet courant, pilote la transition entre révisions
ReplicaSet controllerreplicas + selectorPods correspondant au selectorCrée ou supprime des pods
kubelet (niveau pod)Pod.spec (conteneurs)Conteneurs en exécution (chapitre 7-8)Démarre ou arrête les conteneurs

Code YAML — l’état désiré au sommet de la cascade

# deployment.yaml — un seul objet déclaré, trois échelons réconciliés
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 3            # transmis tel quel au ReplicaSet créé
  selector:
    matchLabels:
      app: hello
  template:              # toute modification ici engendre un NOUVEAU ReplicaSet
    metadata:
      labels:
        app: hello
    spec:
      containers:
        - name: web
          image: nginx:1.27

Code kubectl — observer la cascade et la filiation

# Un seul apply, trois niveaux d'objets se déploient
kubectl apply -f deployment.yaml
# deployment.apps/hello created

kubectl get deploy,rs,pod -l app=hello
# NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
# deployment.apps/hello         3/3     3            3           8s
# NAME                                     DESIRED   CURRENT   READY   AGE
# replicaset.apps/hello-7d4b9c             3         3         3       8s   ← créé par le Deployment
# NAME                          READY   STATUS    RESTARTS   AGE
# pod/hello-7d4b9c-aaa11        1/1     Running   0          8s   ← créés par le ReplicaSet
# pod/hello-7d4b9c-bbb22        1/1     Running   0          8s
# pod/hello-7d4b9c-ccc33        1/1     Running   0          8s

# La filiation est inscrite dans les ownerReferences (chap. 25-26)
kubectl get pod hello-7d4b9c-aaa11 -o jsonpath='{.metadata.ownerReferences[0].kind}'
# ReplicaSet   ← le pod appartient au ReplicaSet, jamais directement au Deployment

Piège courant : « Le Deployment crée et surveille directement les pods » est inexact — il maintient un ReplicaSet, qui maintient les pods. C’est pourquoi kubectl scale deployment modifie en réalité le replicas du ReplicaSet, et pourquoi un pod supprimé est recréé par le ReplicaSet, jamais par le Deployment. Confondre les deux échelons conduit à diagnostiquer un comportement au mauvais niveau de la cascade.


Rolling updates : la réconciliation progressive

L’idée en une phrase

Un rolling update ne remplace pas les pods d’un seul coup : le contrôleur de Deployment fait coexister deux ReplicaSets — l’ancien décroissant, le nouveau croissant — et transfère les replicas de l’un vers l’autre par paliers. L’état désiré final (« N pods de la nouvelle version ») est atteint par une suite d’états intermédiaires, chaque tick respectant deux bornes (maxUnavailable, maxSurge) : la réconciliation est ici progressive et contrainte, non instantanée.

Analogie : Considérons le renouvellement des rames d’une ligne de métro en service. Pour ne pas interrompre le trafic, on n’immobilise pas toutes les rames à la fois : une rame neuve entre en circulation, on vérifie qu’elle roule, puis une ancienne est retirée, et ainsi de suite. À chaque instant, le nombre de rames en service reste suffisant. La cadence — combien de rames en plus ou en moins sont tolérées — est fixée à l’avance.

Points clés

  • La stratégie par défaut est RollingUpdate ; l’alternative Recreate supprime tous les anciens pods avant de créer les nouveaux, au prix d’une interruption de service assumée.
  • Deux paramètres règlent la cadence : maxUnavailable (combien de pods peuvent manquer sous l’effectif désiré, défaut 25 %) et maxSurge (combien peuvent être créés au-dessus, défaut 25 %). Ils bornent chaque état intermédiaire.
  • Une nouvelle version implique un nouveau ReplicaSet. Le contrôleur n’édite jamais un pod existant — les pods sont immuables — il crée des pods neufs et supprime les anciens. La transition n’est qu’un transfert de replicas entre deux ReplicaSets.
  • La progression est conditionnée par la disponibilité : un nouveau pod n’est compté disponible qu’une fois Ready (readiness probe, renseignée dans le status vu au chapitre 5-6). Tant que le quota de pods disponibles n’est pas tenu, le contrôleur n’abaisse pas davantage l’ancien ReplicaSet.
  • Le retour arrière (kubectl rollout undo) ne redéploie rien depuis zéro : il réconcilie vers l’ancien ReplicaSet, conservé à 0 replica, en le remontant. L’historique des révisions est précisément la liste des ReplicaSets conservés.

Exemple concret

Un Deployment de replicas: 4, avec maxUnavailable: 1 et maxSurge: 1, passe de nginx:1.27 à nginx:1.28. Le contrôleur crée un second ReplicaSet (version 1.28) et procède par paliers : il porte le nouveau de 0 à 1 (total 5 pods, surge respecté), attend que ce pod soit Ready, puis abaisse l’ancien de 4 à 3 (3 disponibles ≥ 4 − 1). Puis nouveau 1 → 2, ancien 3 → 2 ; nouveau 2 → 3, ancien 2 → 1 ; nouveau 3 → 4, ancien 1 → 0. À aucun instant moins de 3 pods ne sont disponibles, ni plus de 5 au total. L’ancien ReplicaSet subsiste à 0 replica, prêt pour un éventuel rollout undo.

RollingUpdate ou Recreate : deux régimes de transition

CritèreRollingUpdate (défaut)Recreate
Interruption de serviceAucune (chevauchement des versions)Oui : arrêt complet puis redémarrage
Versions coexistantesDeux, transitoirementUne seule à la fois
Bornes de cadencemaxUnavailable, maxSurgeSans objet
Usage typiqueServices web sans étatCas interdisant toute coexistence de versions

Code YAML — fixer la cadence de la transition

# deployment.yaml — la stratégie borne chaque état intermédiaire
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1   # au plus 1 pod sous l'effectif → au moins 3 disponibles
      maxSurge: 1         # au plus 1 pod au-dessus → au plus 5 au total
  template:
    spec:
      containers:
        - name: web
          image: nginx:1.28   # 1.27 → 1.28 : déclenche la création d'un nouveau ReplicaSet

Code kubectl — déclencher, suivre et annuler une transition

# Changer l'image = modifier le template = nouveau ReplicaSet
kubectl set image deployment/hello web=nginx:1.28
# deployment.apps/hello image updated

# Suivre la convergence, palier par palier
kubectl rollout status deployment/hello
# Waiting for deployment "hello" rollout to finish: 2 of 4 updated replicas are available...
# deployment "hello" successfully rolled out

# Pendant la transition, DEUX ReplicaSets coexistent
kubectl get rs -l app=hello
# NAME               DESIRED   CURRENT   READY   AGE
# hello-7d4b9c       1         1         1       6m    ← ancien (1.27), en décroissance
# hello-5f8d2c       3         3         3       20s   ← nouveau (1.28), en croissance

# Retour arrière : réconcilier vers l'ancien ReplicaSet, toujours présent
kubectl rollout undo deployment/hello
# deployment.apps/hello rolled back

Piège courant : « kubectl apply d’une nouvelle image modifie les pods existants » est inexact — un pod est immuable : le rolling update crée un nouveau ReplicaSet, fait converger les effectifs des deux, et supprime les anciens pods une fois les nouveaux Ready. De même, un rollout undo n’est pas un redéploiement neuf mais une réconciliation vers un ReplicaSet déjà présent. Croire que les pods sont « mis à jour sur place » fait mal interpréter l’apparition d’un second ReplicaSet et la coexistence temporaire des versions.


Quiz — validation des acquis
Intermédiaire 7 questions Objectif : 5/7 minimum
0/7
bonnes reponses
Objectif non atteint (minimum 5/7 requis).
Relire la fiche memo ci-dessus en pretant attention aux points manques, puis cliquer sur « Recommencer » pour retenter.