Jour 13 Day 13 · vendredi 14 août 2026 Friday 14 August 2026 DevOps Avancé

Kubernetes : l'orchestration expliquée Kubernetes: orchestration explained

Control plane, Pods, Deployments, Services, boucle de réconciliation : comprendre ce que K8s automatise vraiment — et savoir dire en entretien quand il est overkill. Control plane, Pods, Deployments, Services, reconciliation loop: understand what K8s really automates — and know how to say in an interview when it's overkill.

L’essentiel

Kubernetes (K8s) est un orchestrateur de conteneurs : il gère le cycle de vie de conteneurs répartis sur un cluster de machines. Docker Compose démarre une stack sur une machine ; Kubernetes répond aux questions qui arrivent après : que se passe-t-il si un conteneur crashe à 3h du matin ? Si la machine meurt ? S’il faut passer de 3 à 30 réplicas pendant un pic ? Comment déployer une nouvelle version sans coupure ?

Son cœur, c’est le modèle déclaratif : on ne dit pas à K8s quoi faire mais l’état désiré (« je veux 3 réplicas de cette image, joignables sur ce port »), en YAML. Des controllers comparent en permanence l’état réel à l’état désiré et corrigent l’écart : c’est la boucle de réconciliation. Un Pod meurt → un autre est recréé automatiquement (self-healing), sans intervention humaine. Le scaling est une modification d’un champ (replicas: 10), le rolling update un changement de tag d’image.

🎤 En entretien — savoir dire quand K8s est overkill est un signe de maturité : il résout des problèmes de flotte et d’échelle. Un service et une base sur un seul serveur ? Un VPS + Docker Compose (ou un PaaS type Coolify) fait le travail avec 10 % de la complexité. K8s se justifie avec plusieurs services, plusieurs machines, des besoins de scaling/HA réels — ou une équipe plateforme pour l’opérer.

Comment ça marche

Un cluster a deux moitiés :

Le control plane (le cerveau) :

  • kube-apiserver — le point d’entrée unique : kubectl, les controllers et les kubelets passent tous par son API REST. Il valide et persiste les objets.
  • etcd — la base clé-valeur distribuée qui stocke tout l’état du cluster. Perdre etcd sans backup = perdre le cluster.
  • kube-scheduler — décide sur quel node placer chaque nouveau Pod (ressources demandées, affinités, taints/tolerations). Il décide, il n’exécute pas.
  • controller manager — fait tourner les boucles de réconciliation (Deployment, ReplicaSet, Node…) qui rapprochent le réel du désiré.

Les worker nodes (les bras) :

  • kubelet — l’agent sur chaque node : il surveille l’API server, lance les conteneurs des Pods qui lui sont assignés via le container runtime (containerd — pas le démon Docker), et remonte leur état.
  • kube-proxy — programme les règles réseau (iptables/IPVS) pour que les Services routent le trafic vers les bons Pods.
        CONTROL PLANE (le cerveau)
 ┌────────────────────────────────────┐
 │ kube-apiserver ◀───────▶ etcd      │
 │    ▲          ▲                    │
 │ scheduler    controller manager    │
 └─────▲──────────────▲───────────────┘
       │ watch        │ watch
 ┌─────┴───────┐ ┌────┴────────┐
 │ kubelet     │ │ kubelet     │
 │ kube-proxy  │ │ kube-proxy  │
 │ [Pod] [Pod] │ │ [Pod] [Pod] │
 │   node 1    │ │   node 2    │
 └─────────────┘ └─────────────┘
        WORKER NODES (les bras)

Le flux d’un kubectl apply -f deployment.yaml : l’API server valide et écrit dans etcd → le Deployment controller crée un ReplicaSet → le ReplicaSet crée les objets Pod → le scheduler leur assigne un node → le kubelet du node lance les conteneurs. Chaque acteur ne regarde que l’API server : c’est ce découplage qui rend le système résilient.

Le manifest minimal que cette chaîne consomme :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3                     # l'état désiré, pas une commande
  selector:
    matchLabels: { app: api }     # les Pods que ce Deployment gère
  template:                       # modèle des Pods à créer
    metadata:
      labels: { app: api }        # doit matcher le selector
    spec:
      containers:
        - name: api
          image: registry.io/api:1.4.2  # tag immuable, jamais latest
          resources:
            requests: { cpu: 100m, memory: 128Mi }  # pour le scheduler
            limits: { memory: 256Mi }   # dépassé → OOMKilled

Concepts clés à maîtriser

  • Pod — la plus petite unité déployable : un ou plusieurs conteneurs qui partagent réseau (même IP) et volumes. Éphémère par conception : on ne crée quasi jamais un Pod nu, on passe par un Deployment.
  • Deployment → ReplicaSet → Pods — le Deployment gère les versions et les rolling updates (montée progressive du nouveau ReplicaSet, descente de l’ancien, rollback possible) ; le ReplicaSet maintient le nombre de réplicas.
  • Service — une IP virtuelle stable + un nom DNS devant des Pods éphémères, sélectionnés par labels.
  • Ingress — le reverse proxy HTTP du cluster : routage par host/path (api.exemple.com → service api), terminaison TLS, un seul point d’entrée pour N services. Nécessite un Ingress controller (nginx, Traefik).

Les quatre façons d’exposer un service, comparées :

PortéeUsage typique
ClusterIPInterne au cluster (défaut)Trafic service → service
NodePortPort (30000+) ouvert sur chaque nodeTest, on-prem sans load balancer
LoadBalancerIP publique cloud, une par serviceExposition directe (coûteuse)
IngressRoutage HTTP host/path + TLSUn point d’entrée pour N services
  • ConfigMap & Secret — la config hors de l’image, injectée en variables d’environnement ou en fichiers montés (voir le piège Secrets plus bas).
  • Requests & limitsrequests = ressources garanties, utilisées par le scheduler pour placer le Pod ; limits = plafond (CPU throttlé au-delà, RAM dépassée → OOMKilled). Sans requests, le scheduler place à l’aveugle ; sans limits, un Pod peut affamer le node.
  • Probesliveness (« le process est-il vivant ? » : échec → restart du conteneur) et readiness (« peut-il recevoir du trafic ? » : échec → retiré des endpoints du Service, sans restart).

💡 Réflexe probes — la liveness ne doit tester que le process lui-même, jamais une dépendance externe : une liveness qui ping la DB fait redémarrer l’app en boucle dès que la DB tousse. C’est la readiness qui peut attendre une dépendance.

En entretien

« Qu’apporte Kubernetes par rapport à Docker Compose ? » — Compose démarre une stack sur une machine et s’arrête là. K8s ajoute : multi-machines, boucle de réconciliation (self-healing : Pod ou node mort → recréation automatique), scaling déclaratif, rolling updates avec rollback, service discovery et load balancing intégrés. Le prix : une complexité opérationnelle d’un autre ordre.

« Explique le modèle déclaratif. » — On décrit l’état désiré dans des manifests YAML, stockés dans etcd ; des controllers comparent en boucle l’état observé à l’état désiré et agissent pour combler l’écart. Conséquence : idempotence (kubectl apply rejouable), auto-réparation, et le YAML versionné dans Git devient la source de vérité (base du GitOps).

« Que se passe-t-il quand tu appliques un Deployment ? » — API server → etcd → Deployment controller crée un ReplicaSet → qui crée les Pods → le scheduler choisit les nodes → les kubelets lancent les conteneurs → kube-proxy et les Services rendent le tout joignable. Dérouler cette chaîne calmement fait très bonne impression.

« ClusterIP, NodePort, LoadBalancer : quand utiliser quoi ? » — dérouler le tableau plus haut, puis donner le pattern qui fait mouche : une IP LoadBalancer par service coûte cher, donc en pratique un seul LoadBalancer devant un Ingress qui route vers N services ClusterIP.

« Liveness vs readiness probe ? » — Liveness : détecte un process bloqué, échec = restart. Readiness : détecte un Pod pas prêt (démarrage, dépendance lente), échec = retiré du load balancing, pas de restart. Erreur classique à citer : mettre une dépendance externe dans la liveness → cascade de restarts inutiles.

Pièges & idées reçues

⚠️ Les Secrets ne sont pas chiffrés — base64 se décode en une commande. Sans encryption at rest d’etcd et RBAC strict, un Secret n’est qu’un ConfigMap déguisé. En pratique : Vault, External Secrets ou les secrets du cloud provider.

  • « Il nous faut du Kubernetes » — souvent faux : sans multi-services, multi-machines ni besoins de scaling/HA réels, Compose + systemd ou un PaaS suffisent (voir le callout plus haut).
  • latest en prod — combiné à imagePullPolicy, on ne sait plus quelle version tourne où, et un rollback devient impossible. Toujours un tag immuable (SHA du commit, version).
  • Pas de requests/limits — scheduling à l’aveugle, un Pod gourmand affame les autres, et les Pods sans requests sont les premiers évincés sous pression mémoire.
  • Confondre les probes (voir le réflexe plus haut) — la liveness ne teste que le process lui-même.
  • Traiter les Pods comme des serveurs — pas de kubectl exec pour patcher en prod : les Pods sont remplaçables à tout instant. Toute modification passe par l’image ou les manifests.

Pour aller plus loin

The essentials

Kubernetes (K8s) is a container orchestrator: it manages the lifecycle of containers spread across a cluster of machines. Docker Compose starts a stack on one machine; Kubernetes answers the questions that come next: what happens if a container crashes at 3am? If the machine dies? If you need to go from 3 to 30 replicas during a spike? How do you deploy a new version with zero downtime?

Its core is the declarative model: you don’t tell K8s what to do but the desired state (“I want 3 replicas of this image, reachable on this port”), in YAML. Controllers continuously compare the actual state to the desired state and correct the gap: that’s the reconciliation loop. A Pod dies → another is recreated automatically (self-healing), with no human intervention. Scaling is a field change (replicas: 10), a rolling update is an image tag change.

🎤 In an interview — knowing when K8s is overkill is a sign of maturity: it solves fleet and scale problems. One service and one database on a single server? A VPS + Docker Compose (or a PaaS like Coolify) does the job with 10% of the complexity. K8s is justified with multiple services, multiple machines, real scaling/HA needs — or a platform team to operate it.

How it works

A cluster has two halves:

The control plane (the brain):

  • kube-apiserver — the single entry point: kubectl, controllers and kubelets all go through its REST API. It validates and persists objects.
  • etcd — the distributed key-value store holding the entire cluster state. Losing etcd without a backup = losing the cluster.
  • kube-scheduler — decides which node each new Pod lands on (requested resources, affinities, taints/tolerations). It decides; it doesn’t execute.
  • controller manager — runs the reconciliation loops (Deployment, ReplicaSet, Node…) that move actual state toward desired state.

The worker nodes (the arms):

  • kubelet — the agent on each node: it watches the API server, starts the containers of the Pods assigned to it through the container runtime (containerd — not the Docker daemon), and reports their status.
  • kube-proxy — programs the network rules (iptables/IPVS) so Services route traffic to the right Pods.
        CONTROL PLANE (the brain)
 ┌────────────────────────────────────┐
 │ kube-apiserver ◀───────▶ etcd      │
 │    ▲          ▲                    │
 │ scheduler    controller manager    │
 └─────▲──────────────▲───────────────┘
       │ watch        │ watch
 ┌─────┴───────┐ ┌────┴────────┐
 │ kubelet     │ │ kubelet     │
 │ kube-proxy  │ │ kube-proxy  │
 │ [Pod] [Pod] │ │ [Pod] [Pod] │
 │   node 1    │ │   node 2    │
 └─────────────┘ └─────────────┘
        WORKER NODES (the arms)

The flow of a kubectl apply -f deployment.yaml: the API server validates and writes to etcd → the Deployment controller creates a ReplicaSet → the ReplicaSet creates the Pod objects → the scheduler assigns them a node → that node’s kubelet starts the containers. Every actor only ever talks to the API server: that decoupling is what makes the system resilient.

The minimal manifest this chain consumes:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3                     # desired state, not a command
  selector:
    matchLabels: { app: api }     # which Pods this Deployment manages
  template:                       # template for the Pods to create
    metadata:
      labels: { app: api }        # must match the selector
    spec:
      containers:
        - name: api
          image: registry.io/api:1.4.2  # immutable tag, never latest
          resources:
            requests: { cpu: 100m, memory: 128Mi }  # for the scheduler
            limits: { memory: 256Mi }   # exceeded → OOMKilled

Key concepts to master

  • Pod — the smallest deployable unit: one or more containers sharing network (same IP) and volumes. Ephemeral by design: you almost never create a bare Pod — you go through a Deployment.
  • Deployment → ReplicaSet → Pods — the Deployment manages versions and rolling updates (new ReplicaSet scaled up progressively, old one scaled down, rollback possible); the ReplicaSet maintains the replica count.
  • Service — a stable virtual IP + DNS name in front of ephemeral Pods, selected by labels.
  • Ingress — the cluster’s HTTP reverse proxy: host/path routing (api.example.com → api service), TLS termination, a single entry point for N services. Requires an Ingress controller (nginx, Traefik).

The four ways to expose a service, compared:

ScopeTypical use
ClusterIPCluster-internal (default)Service → service traffic
NodePortPort (30000+) opened on every nodeTesting, on-prem without a load balancer
LoadBalancerPublic cloud IP, one per serviceDirect exposure (expensive)
IngressHTTP host/path routing + TLSOne entry point for N services
  • ConfigMap & Secret — config outside the image, injected as environment variables or mounted files (see the Secrets pitfall below).
  • Requests & limitsrequests = guaranteed resources, used by the scheduler to place the Pod; limits = ceiling (CPU throttled beyond it, RAM exceeded → OOMKilled). Without requests, the scheduler places blindly; without limits, one Pod can starve the node.
  • Probesliveness (“is the process alive?”: failure → container restart) and readiness (“can it receive traffic?”: failure → removed from the Service’s endpoints, no restart).

💡 Probe reflex — liveness must only test the process itself, never an external dependency: a liveness probe that pings the DB restarts the app in a loop whenever the DB hiccups. Readiness is the one allowed to wait for a dependency.

In an interview

“What does Kubernetes bring over Docker Compose?” — Compose starts a stack on one machine and stops there. K8s adds: multi-machine, a reconciliation loop (self-healing: dead Pod or node → automatic recreation), declarative scaling, rolling updates with rollback, built-in service discovery and load balancing. The price: operational complexity of a different order.

“Explain the declarative model.” — You describe the desired state in YAML manifests, stored in etcd; controllers continuously compare observed state to desired state and act to close the gap. Consequences: idempotence (kubectl apply can be replayed), self-repair, and version-controlled YAML in Git becomes the source of truth (the basis of GitOps).

“What happens when you apply a Deployment?” — API server → etcd → the Deployment controller creates a ReplicaSet → which creates the Pods → the scheduler picks the nodes → the kubelets start the containers → kube-proxy and Services make it all reachable. Walking calmly through this chain makes a very good impression.

“ClusterIP, NodePort, LoadBalancer: when to use which?” — walk through the table above, then land the pattern that impresses: one LoadBalancer IP per service gets expensive, so in practice a single LoadBalancer sits in front of an Ingress routing to N ClusterIP services.

“Liveness vs readiness probe?” — Liveness: detects a stuck process, failure = restart. Readiness: detects a Pod that isn’t ready (starting up, slow dependency), failure = removed from load balancing, no restart. Classic mistake to cite: putting an external dependency in the liveness probe → a cascade of pointless restarts.

Pitfalls & misconceptions

⚠️ Secrets are not encrypted — base64 decodes in one command. Without etcd encryption at rest and strict RBAC, a Secret is just a ConfigMap in disguise. In practice: Vault, External Secrets, or cloud provider secrets.

  • “We need Kubernetes” — often false: without multiple services, multiple machines or real scaling/HA needs, Compose + systemd or a PaaS is enough (see the callout above).
  • latest in production — combined with imagePullPolicy, you no longer know which version runs where, and rollback becomes impossible. Always an immutable tag (commit SHA, version).
  • No requests/limits — blind scheduling, one greedy Pod starves the others, and Pods without requests are the first evicted under memory pressure.
  • Confusing the probes (see the reflex above) — liveness only tests the process itself.
  • Treating Pods like servers — no kubectl exec to patch production: Pods are replaceable at any moment. Every change goes through the image or the manifests.

Going further

S'entraîner sur ce sujet → Practice this topic →