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ée | Usage typique | |
|---|---|---|
| ClusterIP | Interne au cluster (défaut) | Trafic service → service |
| NodePort | Port (30000+) ouvert sur chaque node | Test, on-prem sans load balancer |
| LoadBalancer | IP publique cloud, une par service | Exposition directe (coûteuse) |
| Ingress | Routage HTTP host/path + TLS | Un 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 & limits —
requests= 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. - Probes — liveness (« 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).
latesten 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 execpour patcher en prod : les Pods sont remplaçables à tout instant. Toute modification passe par l’image ou les manifests.
Pour aller plus loin
- Kubernetes — Concepts : la doc officielle, en particulier Workloads et Services & networking
- Le tutoriel officiel « Kubernetes Basics » — interactif, ~1h
- Pratiquer en local avec kind ou minikube : déployer un Deployment + Service + Ingress, tuer un Pod avec
kubectl delete podet regarder la réconciliation le recréer - « Kubernetes the Hard Way » (Kelsey Hightower) pour démonter le moteur pièce par pièce
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:
| Scope | Typical use | |
|---|---|---|
| ClusterIP | Cluster-internal (default) | Service → service traffic |
| NodePort | Port (30000+) opened on every node | Testing, on-prem without a load balancer |
| LoadBalancer | Public cloud IP, one per service | Direct exposure (expensive) |
| Ingress | HTTP host/path routing + TLS | One entry point for N services |
- ConfigMap & Secret — config outside the image, injected as environment variables or mounted files (see the Secrets pitfall below).
- Requests & limits —
requests= 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. - Probes — liveness (“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).
latestin production — combined withimagePullPolicy, 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 execto patch production: Pods are replaceable at any moment. Every change goes through the image or the manifests.
Going further
- Kubernetes — Concepts: the official docs, especially Workloads and Services & networking
- The official “Kubernetes Basics” tutorial — interactive, ~1h
- Practice locally with kind or minikube: deploy a Deployment + Service + Ingress, kill a Pod with
kubectl delete podand watch reconciliation recreate it - “Kubernetes the Hard Way” (Kelsey Hightower) to take the engine apart piece by piece