Pods et déploiements : l'état désiré

Première leçon du cours Kubernetes, en accès libre : environ 18 minutes, 3 exercices et 3 questions de quiz. C'est la leçon de base du catalogue ; dans l'espace étudiant, elle est réécrite pour ton profil.

Objectifs

  • Comprendre l'architecture d'un cluster Kubernetes (plan de contrôle et nœuds)
  • Décrire une application avec un manifeste Deployment et l'appliquer de façon déclarative
  • Mettre à jour une application par rolling update et revenir en arrière

Plan de contrôle et nœudsPodsDeployments et ReplicaSetsLabels et sélecteursRolling update et retour arrière

Avec Docker et Compose, vous savez lancer des conteneurs sur une machine. Kubernetes répond à la question suivante : comment faire tourner des dizaines de conteneurs sur plusieurs machines, les redémarrer quand ils tombent, les mettre à jour sans coupure et les répartir selon les ressources disponibles ? Dans ce cours, on part d’images déjà construites et publiées dans un registre (voir le cours Docker) : on ne réécrit pas de Dockerfile.

L’architecture d’un cluster

Un cluster Kubernetes se compose de deux parties.

  • Le plan de contrôle (control plane) prend les décisions : l’API server reçoit toutes les demandes (y compris celles de kubectl), etcd stocke l’état du cluster, le scheduler choisit sur quel nœud placer chaque pod, le controller manager fait tourner les contrôleurs qui corrigent les écarts.
  • Les nœuds (nodes) exécutent les conteneurs : sur chacun, le kubelet lance les pods demandés via un moteur de conteneurs (containerd le plus souvent) et kube-proxy gère le réseau des services.

Sur un service managé (GKE, EKS, AKS…), le fournisseur opère le plan de contrôle pour vous. En local, minikube ou kind créent un cluster complet sur votre poste :

minikube start            # ou : kind create cluster --name atelier
kubectl get nodes         # un nœud doit être Ready
kubectl cluster-info

L’état désiré et la réconciliation

On ne dit pas à Kubernetes « lance trois conteneurs ». On lui décrit l’état désiré : « je veux trois répliques de cette image ». Des contrôleurs comparent sans cesse cet état à la réalité et corrigent l’écart. Si un nœud tombe, les pods manquants sont recréés ailleurs ; si vous supprimez un pod, il revient. C’est la boucle de réconciliation.

Le pod, plus petite unité

Un pod regroupe un ou plusieurs conteneurs qui partagent une adresse IP et peuvent partager des volumes. Dans la grande majorité des cas, un pod contient un seul conteneur applicatif. Un pod est éphémère : son IP change à chaque recréation, et un pod créé seul n’est pas recréé s’il disparaît. On ne crée donc pas de pods directement : on passe par un Deployment.

Un manifeste Deployment

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: notes-api
  labels:
    app: notes-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: notes-api          # doit correspondre aux labels du template
  template:
    metadata:
      labels:
        app: notes-api
    spec:
      containers:
        - name: notes-api
          image: hashicorp/http-echo:0.2.3
          args: ["-text=notes-api v1"]
          ports:
            - containerPort: 5678

Chaque objet a la même structure : apiVersion et kind (le type d’objet), metadata (nom, labels), spec (l’état désiré). Les labels sont des étiquettes clé-valeur ; le sélecteur est la requête qui retrouve les objets portant ces étiquettes. Le Deployment crée un ReplicaSet qui, lui, maintient le bon nombre de pods.

kubectl apply -f deployment.yaml
kubectl get deploy notes-api          # READY 3/3
kubectl get rs,pods -l app=notes-api  # le ReplicaSet et ses trois pods
kubectl delete pod <un-des-pods>      # il est aussitôt remplacé

L’approche déclarative (kubectl apply -f sur des fichiers versionnés dans Git) est la norme : on modifie le fichier, on le réapplique, et l’historique Git documente chaque changement. Les commandes impératives (kubectl create, kubectl edit) dépannent mais créent un écart entre le dépôt et le cluster.

Mettre à jour sans coupure : le rolling update

Modifier le template (par exemple l’image ou ses arguments) crée un nouveau ReplicaSet. Kubernetes remplace alors les pods progressivement : par défaut, il peut créer jusqu’à 25 % de pods en plus (maxSurge) et en rendre indisponibles au plus 25 % (maxUnavailable) pendant la bascule.

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0      # jamais moins de 3 pods prêts
kubectl set image deploy/web nginx=nginx:1.27   # ou modifier le fichier puis apply
kubectl rollout status deploy/web               # suit la progression
kubectl rollout history deploy/web              # révisions connues
kubectl rollout undo deploy/web                 # retour à la révision précédente

L’ancien ReplicaSet est conservé (mis à zéro réplique) : c’est lui qui rend le retour arrière immédiat. Comme avec Docker, utilisez des tags immuables : avec latest, impossible de savoir quelle version tourne ni de revenir en arrière de façon fiable.

Exercices

Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.

  1. TâcheDifficulté 11 indice

    Premier Deployment à trois répliques

    Travaillez sur un cluster local : minikube start (ou kind create cluster). Vérifiez avec kubectl get nodes qu’un nœud est Ready avant de commencer.

    Créez le fichier deployment.yaml de la leçon (Deployment notes-api, 3 répliques, image hashicorp/http-echo:0.2.3, argument -text=notes-api v1, label app: notes-api), appliquez-le, puis supprimez un des pods et observez le résultat.

    • `kubectl apply -f deployment.yaml` affiche `deployment.apps/notes-api created`
    • `kubectl get deploy notes-api` affiche READY `3/3`
    • `kubectl get pods -l app=notes-api` liste exactement 3 pods en statut `Running`
    • Après `kubectl delete pod <nom>`, une nouvelle commande `kubectl get pods -l app=notes-api` liste de nouveau 3 pods, dont un avec un AGE de quelques secondes
  2. TâcheDifficulté 22 indices

    Mise à jour puis retour arrière

    Créez un Deployment web à 2 répliques avec l’image nginx:1.26 (nom du conteneur : nginx). Mettez-le à jour vers nginx:1.27, suivez le déploiement, puis revenez à la version précédente.

    • `kubectl rollout status deploy/web` affiche `deployment "web" successfully rolled out` après la mise à jour
    • `kubectl rollout history deploy/web` affiche au moins 2 révisions
    • Après `kubectl rollout undo deploy/web`, `kubectl get deploy web -o jsonpath='{.spec.template.spec.containers[0].image}'` affiche `nginx:1.26`
    • `kubectl get rs -l app=web` montre deux ReplicaSets, dont un à 0 réplique
  3. QCMDifficulté 21 indice

    Le rôle du sélecteur

    Dans un Deployment, à quoi sert spec.selector.matchLabels ?

    • À choisir le nœud sur lequel placer les pods
    • À indiquer quels pods appartiennent à ce Deployment, en les retrouvant par leurs labels
    • À nommer l'image à télécharger
    • À définir le nombre de répliques

Quiz de fin de leçon

  1. Que se passe-t-il si vous supprimez un pod géré par un Deployment ?

    • Le Deployment est supprimé aussi
    • Le ReplicaSet recrée un pod pour revenir au nombre de répliques désiré
    • Rien, il faut le recréer à la main
  2. Quel composant choisit le nœud sur lequel un pod va s’exécuter ?

    • Le kubelet
    • Le scheduler
    • etcd
  3. Quelle commande revient à la version précédente d’un Deployment ?

    • kubectl rollout undo deploy/web
    • kubectl delete deploy/web
    • kubectl rollback web

La suite du cours

  1. Services et Ingress : exposer une application18 min
  2. Configuration, secrets et namespaces18 min
  3. Mise à l'échelle et résilience20 min
  4. Observabilité et dépannage20 min

Kubernetes · Avancé · 1 h 34