Spec et status : l'anatomie d'un objet Kubernetes & La boucle de contrôle, l'idée fondatrice
L'anatomie d'un objet Kubernetes (apiVersion, metadata, spec, status) et la boucle de contrôle qui fait converger l'état réel vers l'état désiré — sans prérequis, avec YAML et kubectl.
Spec et status : l’anatomie d’un objet Kubernetes
L’idée en une phrase
Tout objet Kubernetes se divise en deux sections complémentaires : la spec, qui décrit l’état désiré (ce qui est voulu), et le status, qui consigne l’état réel observé (ce qui est constaté). La réconciliation est précisément le travail qui rapproche le second du premier : un contrôleur lit la spec, observe le status, et agit pour combler l’écart.
Analogie : Considérons un bon de livraison et son suivi de colis. L’expéditeur remplit le bon — « livrer 3 colis à telle adresse » : c’est la commande, fixée, l’équivalent de la spec. Le transporteur, lui, ne modifie jamais la commande ; il met à jour la page de suivi — « 2 colis livrés, 1 en transit » : c’est le constat, l’équivalent du status. Deux sections, deux rédacteurs distincts ; le client n’inscrit jamais « livré » à la place du transporteur.
Points clés
- Tout objet se compose de quatre parties : un en-tête (
apiVersion,kind, vus au chapitre 3-4), metadata (l’identité : nom, namespace, labels), spec (l’état désiré) et status (l’état réel observé). - La spec est rédigée par l’auteur de l’objet — un humain via un manifeste, ou un contrôleur de niveau supérieur. Elle exprime une intention, jamais un ordre immédiat.
- Le status est rédigé exclusivement par le contrôleur responsable de l’objet. On ne renseigne jamais le status dans un manifeste :
kubectl applyn’écrit que la spec. - L’écart entre spec et status est le moteur de la réconciliation : tant que le status diffère de la spec, une boucle a du travail. Quand les deux coïncident, l’objet est dit convergé.
- Le status n’est pas la spec recopiée : il porte des champs propres au constat —
availableReplicas(nombre de pods réellement disponibles),conditions(état de santé synthétique) ouobservedGeneration(la version de la spec déjà traitée) — sans équivalent dans la spec.
Exemple concret
Un Deployment déclare spec.replicas: 3. Juste après l’enregistrement, son status indique availableReplicas: 0 : l’écart est maximal. Le contrôleur de Deployment crée les pods ; à mesure qu’ils démarrent, il réécrit le status — 1, puis 2, puis 3. À availableReplicas: 3, status et spec coïncident : la boucle n’a plus rien à faire. Si un pod est supprimé à 14 h 02, le status retombe à 2 ; l’écart réapparaît, le contrôleur recrée un pod, et le status remonte à 3. La spec, elle, n’a pas changé une seule fois.
spec et status — deux sections, deux rôles
| Critère | spec | status |
|---|---|---|
| Décrit | L’état désiré (le « quoi ») | L’état réel observé (le constat) |
| Rédigé par | L’auteur de l’objet (humain ou contrôleur) | Le contrôleur responsable, exclusivement |
Écrit par kubectl apply | Oui | Non |
| Rôle dans la boucle | La cible vers laquelle converger | Le point de départ mesuré |
Code YAML — on ne rédige que la spec
# deployment.yaml — le manifeste ne contient QUE la spec (l'état désiré)
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello # metadata : l'identité de l'objet
spec: # spec : l'état désiré, le contrat
replicas: 3 # l'intention : "je veux 3 pods disponibles"
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: web
image: nginx:1.27
# status : ABSENT du manifeste — c'est le contrôleur qui le remplira
Code kubectl — lire le status renseigné par le contrôleur
# Après apply, l'objet enregistré porte un status, écrit par le contrôleur
kubectl get deployment hello -o yaml
# spec:
# replicas: 3 ← l'état désiré, tel que déclaré
# status:
# observedGeneration: 1 ← quelle version de la spec le contrôleur a traitée
# replicas: 3
# readyReplicas: 3
# availableReplicas: 3 ← l'état réel : 3 pods disponibles → convergé
# Vue condensée du même écart (ici : nul)
kubectl get deployment hello
# NAME READY UP-TO-DATE AVAILABLE AGE
# hello 3/3 3 3 20s ← READY 3/3 : status == spec
Piège courant : « Modifier le status corrige l’état du cluster » est inexact — le status n’est qu’un constat. Le forcer à la main (via la sous-ressource status, chapitre 21-22) ne crée ni ne supprime aucun pod : au tick suivant, le contrôleur ré-observe l’état réel et réécrit la valeur exacte, effaçant la retouche. Pour changer ce que fait le cluster, on modifie la spec, jamais le status.
La boucle de contrôle, l’idée fondatrice
L’idée en une phrase
Une boucle de contrôle (control loop) est un processus qui répète sans fin trois actions — observer l’état réel, calculer l’écart (diff) avec l’état désiré, agir pour le réduire. C’est l’idée fondatrice de Kubernetes : le cluster n’exécute pas une suite d’ordres, il fait tourner en permanence des boucles qui convergent vers la spec.
Analogie : Considérons un GPS de navigation. La destination saisie est l’état désiré ; la position mesurée par le satellite est l’état réel ; à chaque instant, l’appareil compare les deux et recalcule l’itinéraire. Un virage manqué ne provoque pas d’erreur : la boucle observe la nouvelle position, recalcule l’écart, propose une correction. Le GPS ne suppose jamais que ses instructions ont été suivies — il vérifie, encore et encore.
Points clés
- Une boucle de contrôle est continue, pas ponctuelle : elle ne s’exécute pas une fois après l’apply, elle tourne indéfiniment. C’est ce qui permet l’auto-réparation (self-healing) sans intervention.
- Le composant qui héberge une boucle est un contrôleur (controller). Le kube-controller-manager en regroupe plusieurs — Deployment, ReplicaSet, Node… — chacun responsable d’un type d’objet (anatomie détaillée au chapitre 11-12).
- Une boucle ne pilote jamais directement les machines : elle lit et écrit des objets via l’API server (chapitre 3-4). Agir, pour un contrôleur, c’est le plus souvent créer ou modifier d’autres objets, non lancer un conteneur soi-même.
- La boucle est idempotente : si l’état réel correspond déjà à l’état désiré, le tick ne fait rien. Réexécuter une boucle sur un état convergé est sans effet — propriété centrale, approfondie au chapitre 23-24.
- Plusieurs boucles coopèrent sans se coordonner explicitement : chacune observe l’état et réagit pour son objet. Cette indépendance est ce qui rend le système robuste (architecture vue au chapitre 7-8).
Exemple concret
Un ReplicaSet déclare 3 pods. Sa boucle s’exécute à chaque changement observé, et périodiquement par sécurité. À t₀, état réel 3, désiré 3 : l’écart est nul, le tick ne fait rien. À 14 h 02, un nœud tombe et emporte un pod. Au tick suivant, la boucle observe 2 pods, calcule l’écart (3 − 2 = 1) et crée un pod. Ce pod met quelques secondes à devenir prêt ; pendant ce délai, un nouveau tick peut observer encore 2 pods disponibles — mais comme un pod est déjà en création, une boucle correctement écrite n’en crée pas un second. Une fois le pod prêt, l’état réel revient à 3 : les ticks suivants ne font plus rien.
Une exécution unique vs une boucle de contrôle
| Critère | Script lancé une fois | Boucle de contrôle |
|---|---|---|
| Déclenchement | Une fois, à la demande | En continu, sans fin |
| Après une panne | Aucune correction automatique | Écart ré-observé, puis comblé |
| Hypothèse sur le résultat | Suppose le succès | Ne suppose rien — vérifie |
| Si l’état est déjà correct | Réapplique aveuglément | Ne fait rien (idempotent) |
Code — la boucle, en pseudo-code
boucle infinie:
désiré = lire la spec de l'objet # observer (1/2)
réel = observer l'état réel du cluster # observer (2/2)
écart = comparer(désiré, réel) # diff
si écart non nul:
agir pour réduire l'écart # agir : créer / modifier / supprimer
attendre (un événement, ou la prochaine échéance)
Code kubectl — observer l’auto-réparation
# État convergé : 3 pods désirés, 3 réels
kubectl get pods -l app=hello
# NAME READY STATUS RESTARTS AGE
# hello-7d4b9c-aaa11 1/1 Running 0 5m
# hello-7d4b9c-bbb22 1/1 Running 0 5m
# hello-7d4b9c-ccc33 1/1 Running 0 5m
# Supprimer un pod À LA MAIN : on crée volontairement un écart
kubectl delete pod hello-7d4b9c-aaa11
# pod "hello-7d4b9c-aaa11" deleted
# Sans aucune autre commande, la boucle a déjà comblé l'écart
kubectl get pods -l app=hello
# NAME READY STATUS RESTARTS AGE
# hello-7d4b9c-bbb22 1/1 Running 0 5m
# hello-7d4b9c-ccc33 1/1 Running 0 5m
# hello-7d4b9c-ddd44 1/1 Running 0 3s ← recréé par la boucle
Piège courant : « La boucle ne se déclenche que lorsqu’on lance une commande » est inexact — elle tourne en permanence, indépendamment de toute action de l’opérateur. Supprimer un pod géré par un contrôleur ne le retire donc pas durablement : la boucle observe l’écart et recrée un pod équivalent (au nom différent). Pour retirer réellement un pod, on modifie l’état désiré — réduire
replicas, supprimer le Deployment —, jamais l’état réel directement.