Débutant Chapitre 3-4 / 2

kubectl apply et les manifestes YAML & etcd, le magasin de l'état désiré

Comment l'état désiré entre dans le cluster (manifestes YAML, kubectl apply) et où il est conservé (etcd) — sans prérequis, avec YAML et kubectl.

kubectl apply et les manifestes YAML

L’idée en une phrase

Un manifeste est un fichier YAML qui décrit un objet Kubernetes — donc un fragment d’état désiré ; kubectl apply le transmet à l’API server, qui le valide et l’enregistre. L’apply ne déclenche aucune action sur les machines : il met à jour la déclaration, et la boucle de réconciliation (chapitre 1-2) se charge ensuite, en continu, de faire converger l’état réel.

Analogie : Considérons le dépôt d’un formulaire au guichet d’une administration. Le guichet contrôle que le formulaire est complet et conforme, l’enregistre au registre officiel, puis délivre un récépissé. Le traitement effectif est réalisé plus tard, par d’autres services, sur la seule base du registre. Le formulaire est le manifeste, le guichet l’API server, et le récépissé la réponse created.

Points clés

  • Tout manifeste comporte quatre champs racine : apiVersion (le groupe et la version de l’API qui définissent l’objet), kind (le type d’objet), metadata (l’identité : nom, namespace, labels) et spec (l’état désiré proprement dit).
  • kubectl apply -f envoie le manifeste à l’API server par une requête HTTPS. kubectl ne dialogue jamais directement avec les nœuds ni avec la base de données : l’API server est le guichet unique du cluster.
  • L’apply est idempotent, comme vu au chapitre 1-2 : la réponse vaut created (l’objet n’existait pas), configured (la déclaration enregistrée différait) ou unchanged (aucun écart, aucune écriture).
  • L’apply effectue une fusion à trois sources (three-way merge) : la dernière configuration appliquée, l’état enregistré dans le cluster et le nouveau fichier. Il ne touche qu’aux champs que le fichier gère — les champs écrits par d’autres acteurs sont préservés.
  • kubectl diff -f calcule l’écart entre le fichier et la déclaration enregistrée sans rien modifier ; --dry-run=server soumet le manifeste à toutes les validations de l’API server sans l’enregistrer.

Exemple concret

Un fichier deployment.yaml déclare 3 replicas de nginx:1.27. À l’exécution de kubectl apply -f deployment.yaml, l’API server valide le manifeste (schéma, droits d’accès), l’enregistre, puis répond created en quelques dizaines de millisecondes. À cet instant précis, aucun pod n’existe encore : seul l’état désiré a changé. Les boucles de contrôle constatent l’écart (désiré 3, réel 0) et créent les pods dans les secondes qui suivent. Une semaine plus tard, l’image est mise à jour vers nginx:1.28 dans le fichier ; le même apply répond configured, et la convergence reprend — même commande, même mécanique.

Les verbes kubectl face à l’état désiré

CommandeStyleRejouable sans effet de bordUsage typique
kubectl create -fDéclaratif ponctuelNon — erreur AlreadyExistsCréation initiale, scripts simples
kubectl apply -fDéclaratifOui — répond unchangedGestion continue de l’état désiré
kubectl replace -fDéclaratif destructifNon — remplace l’objet entierCas particuliers
kubectl edit, kubectl scaleImpératifNon — modifie l’objet en placeExpérimentation, urgence

Code YAML — l’anatomie d’un manifeste

# deployment.yaml — un manifeste : la déclaration complète d'un objet
apiVersion: apps/v1        # le groupe/version d'API qui définit l'objet
kind: Deployment           # le type d'objet déclaré
metadata:
  name: hello              # l'identité : nom, namespace, labels…
spec:                      # l'état désiré — le contrat (chapitre 1-2)
  replicas: 3
  selector:
    matchLabels:
      app: hello
  template:                # le modèle des pods à créer
    metadata:
      labels:
        app: hello
    spec:
      containers:
        - name: web
          image: nginx:1.27

Code kubectl — le cycle de vie d’un apply

# Enregistrer l'état désiré pour la première fois
kubectl apply -f deployment.yaml
# deployment.apps/hello created

# Après modification du fichier (replicas: 3 → 4) :
# mesurer l'écart AVANT d'enregistrer — rien n'est modifié
kubectl diff -f deployment.yaml
# -  replicas: 3
# +  replicas: 4

# Valider sans enregistrer : toutes les vérifications, aucune écriture
kubectl apply -f deployment.yaml --dry-run=server
# deployment.apps/hello configured (server dry run)

# Enregistrer la nouvelle déclaration
kubectl apply -f deployment.yaml
# deployment.apps/hello configured   ← l'état désiré a changé, la boucle converge

Piège courant : « kubectl apply remplace l’objet entier par le contenu du fichier » est inexact — l’apply fusionne. Si un autre acteur (un autoscaler, par exemple) gère le champ replicas, un fichier qui ne mentionne pas ce champ ne l’écrasera pas. C’est kubectl replace qui substitue l’objet entier ; confondre les deux conduit à perdre des réglages gérés par d’autres boucles.


etcd, le magasin de l’état désiré

L’idée en une phrase

etcd est la base de données clé-valeur distribuée où l’API server persiste tous les objets du cluster : c’est la source de vérité de l’état désiré. Toute la réconciliation s’y rapporte — les boucles comparent l’état réel à ce qui est écrit dans etcd, jamais l’inverse — mais aucun composant n’y accède directement : seul l’API server lit et écrit dans etcd.

Analogie : Considérons un cadastre. Sur le terrain, les clôtures peuvent être déplacées ou tomber ; en cas de litige, c’est le registre qui fait foi, pas la clôture. Toute mutation passe par un acte enregistré auprès d’un office unique, et les géomètres travaillent à partir du registre. etcd est le cadastre du cluster, l’API server l’office d’enregistrement, et l’état réel le terrain.

Points clés

  • etcd est un magasin clé-valeur : chaque objet du cluster est sérialisé sous une clé de la forme /registry/deployments/default/hello. Pas de tables ni de requêtes relationnelles — des clés, des valeurs, et des observations de changements.
  • etcd est distribué : un cluster de 3 ou 5 membres s’accorde par le protocole de consensus Raft. Une écriture n’est validée qu’une fois répliquée sur la majorité (le quorum) ; un cluster de 3 membres tolère donc la panne d’1 membre.
  • Seul l’API server accède à etcd. Les contrôleurs, le scheduler et les kubelets passent tous par l’API : ce point de passage unique applique l’authentification, la validation et le contrôle d’admission.
  • Chaque objet persisté porte un resourceVersion, compteur monotone issu d’etcd. Il permet de détecter les modifications concurrentes et alimente le mécanisme de watch (chapitre 13-14), par lequel les boucles observent l’état sans interroger en permanence.
  • Perdre etcd, c’est perdre l’accès à l’état désiré : les conteneurs en cours d’exécution continuent de tourner (et le kubelet les redémarre localement si besoin), mais plus aucune création ni réconciliation n’est possible. D’où la sauvegarde régulière par instantané (etcdctl snapshot save).

Exemple concret

Un cluster etcd compte 3 membres. À 14 h 02, le membre 3 tombe : le quorum (2 sur 3) est conservé, les lectures et écritures continuent. À 14 h 03, un kubectl apply est validé après réplication de l’écriture sur les membres 1 et 2. À 14 h 10, le membre 3 revient et rattrape l’historique Raft manquant.

Variante : les membres 2 et 3 tombent. Le quorum est perdu : l’API server ne peut plus écrire — les apply sont refusés, les pods supprimés ne sont plus remplacés. L’état réel, lui, persiste : les conteneurs déjà lancés continuent de servir le trafic. Le cluster ne s’effondre pas ; il cesse de converger.

Dimensionner un cluster etcd

MembresQuorumPannes toléréesUsage
110Poste de travail, lab (minikube, kind)
321Production standard
532Production critique

Code kubectl et etcdctl — observer le magasin

# Chaque objet persisté porte un resourceVersion issu d'etcd
kubectl get deployment hello -o yaml | grep resourceVersion
#   resourceVersion: "4821"   ← compteur monotone, base des watches (chapitre 13-14)

# Vue interne, depuis un membre etcd : les objets vivent sous /registry
etcdctl get /registry/deployments/default/hello --keys-only
# /registry/deployments/default/hello

# Sauvegarder l'état désiré du cluster entier — un seul fichier
etcdctl snapshot save /backup/etcd-2026-06-07.db
# Snapshot saved at /backup/etcd-2026-06-07.db

Piège courant : « etcd ne stocke que l’état désiré » est inexact — etcd persiste les objets entiers, spec et status compris. La distinction entre contrat et constat est logique, pas physique : la spec fait foi parce que les boucles convergent vers elle, tandis que le status n’est qu’un constat consigné, réécrit au fil des observations. Qualifier etcd de « magasin de l’état désiré » décrit son rôle dans la réconciliation, pas le découpage de son contenu.


Quiz — validation des acquis
Débutant 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.