Tu connais le thermostat de ton salon : tu règles 21 °C et tu n'y penses plus. Il mesure la température, la compare à ta consigne, chauffe ou s'arrête, et si quelqu'un ouvre la fenêtre il rattrape sans que tu bouges. Kubernetes fait exactement ça avec tes conteneurs. Tu déclares « je veux trois copies de mon API, toujours joignables sur le port 8080 » ; il compare en permanence ce que tu veux à ce qui tourne, et corrige : un conteneur meurt à trois heures du matin, il en relance un ; une machine tombe, il replace les conteneurs ailleurs ; tu changes la version de l'image, il remplace les copies une par une sans couper le service. Avec docker run, tu es le thermostat : c'est toi qui surveilles et qui relances. Dans cette leçon, tu vas voir la différence de tes yeux : tuer un conteneur Docker et constater qu'il reste mort, puis regarder les « pièces » de Kubernetes qui tournent déjà sur ta machine, prêtes à faire ce travail à ta place.
Trois situations que tout le monde finit par vivre avec Docker seul. Un : ton API tourne dans un conteneur sur un serveur ; un soir elle plante (fuite mémoire, dépendance externe en panne) et personne ne le voit avant le matin. --restart=always relance le processus, mais ne te dit rien, ne vérifie pas que l'application répond vraiment, et ne fait rien si c'est le serveur entier qui s'éteint. Deux : tu as dix conteneurs de la même API derrière un reverse proxy et tu dois livrer la version 2 sans que les utilisateurs voient une erreur ; à la main, ça veut dire arrêter, redémarrer et retirer du proxy chaque conteneur dans le bon ordre, dix fois, sans se tromper. Trois : ton trafic ne tient plus sur une machine ; il faut en ajouter deux, décider quel conteneur va où, et que les conteneurs se trouvent entre eux alors que leurs adresses IP changent à chaque redémarrage.
Ces trois problèmes, c'est le métier d'un orchestrateur. Docker en propose deux réponses partielles : Compose décrit plusieurs conteneurs dans un fichier mais reste sur une seule machine et ne surveille rien ; Swarm ajoute le multi-machines et les réplicas mais son écosystème est resté petit. Kubernetes (K8s : « K », huit lettres, « s »), né chez Google en 2014 et aujourd'hui géré par la CNCF, est devenu le standard : c'est lui que tu retrouveras chez AWS (EKS), Google (GKE), Azure (AKS), OVH, Scaleway, et sur les serveurs de la plupart des entreprises.
| Critère | Docker Compose | Docker Swarm | Kubernetes |
|---|---|---|---|
| Périmètre | une machine | plusieurs machines | plusieurs machines (des milliers) |
| Relance automatique | restart: seulement | oui | oui, avec sondes de santé (readiness, liveness) |
| Mise à jour sans coupure | non | oui, basique | oui, progressive, avec retour arrière en une commande |
| Répartition de charge intégrée | non | oui | oui (Services), plus Ingress, DNS interne |
| Montée en charge automatique | non | non | oui (HPA, sur CPU, mémoire ou métriques métier) |
| Écosystème (Helm, opérateurs, cloud) | — | petit | immense |
| Courbe d'apprentissage | douce | moyenne | raide, d'où ce cours |
Un cluster Kubernetes se compose de deux parties. Le control plane décide ; les nœuds exécutent. Sur Docker Desktop, les deux vivent sur la même machine virtuelle, mais les rôles restent les mêmes qu'en production.
| Composant | Où | Rôle en une phrase |
|---|---|---|
kube-apiserver | control plane | Reçoit toutes les commandes (kubectl, contrôleurs, kubelet) en HTTPS sur le port 6443 ; rien ne se passe sans passer par lui. |
etcd | control plane | Base clé-valeur qui stocke l'état désiré et l'état observé de tout le cluster ; le perdre sans sauvegarde, c'est perdre le cluster. |
kube-scheduler | control plane | Choisit sur quel nœud placer chaque nouveau Pod (ressources libres, contraintes). Il ne lance rien lui-même. |
kube-controller-manager | control plane | Regroupe les boucles de réconciliation : « il manque une réplique ? j'en crée une », « un nœud ne répond plus ? je marque ses Pods à replacer ». |
kubelet | chaque nœud | L'agent qui lit dans l'API les Pods qui lui sont assignés, demande au runtime de lancer les conteneurs, et remonte leur état. |
kube-proxy | chaque nœud | Programme les règles réseau pour qu'une adresse de Service arrive au bon Pod. |
| runtime | chaque nœud | Le logiciel qui exécute vraiment les conteneurs : containerd ou CRI-O en général, docker://29.3.1 sur Docker Desktop. |
Le cœur du modèle, c'est la déclaration d'état désiré. Tu n'ordonnes pas « lance un conteneur » ; tu écris (ou tu fais générer) un objet qui dit « je veux un Deployment api, image traefik/whoami:v1.10, 3 réplicas ». L'API l'enregistre dans etcd. Les contrôleurs comparent ensuite, en boucle et pour toujours, cet état désiré à l'état observé remonté par les kubelets ; chaque écart déclenche une action. C'est pour ça qu'un Pod supprimé renaît : personne ne l'a « redémarré », le contrôleur a simplement constaté 2 au lieu de 3 et corrigé. Tu verras la boucle à l'œuvre en leçon 04.
Pourquoi apprendre sur Docker Desktop plutôt que sur minikube, kind ou un cluster cloud ? Parce que tu l'as sans doute déjà, qu'un clic active un cluster complet, que les images que tu construis avec docker build sont directement visibles par Kubernetes (même magasin d'images, pas de registre à installer), et que les Services de type LoadBalancer obtiennent l'adresse localhost : tu ouvres ton navigateur et ça répond, comme en production derrière un vrai équilibreur de charge. Ses limites sont tout aussi claires : un seul nœud (pas de « machine qui tombe » à simuler), pas de NetworkPolicy appliquée par défaut (le CNI intégré les ignore), pas de stockage réseau ni d'IP publique. Tout ce que tu écriras ici s'appliquera tel quel sur un vrai cluster ; seule la façon d'y accéder changera.
| Option | Installation | Nœuds | LoadBalancer sur localhost | Images locales sans registre | Pour qui |
|---|---|---|---|---|---|
| Docker Desktop (ce cours) | un clic dans l'application | 1 (kubeadm) ou plusieurs (kind, 4.38+) | oui | oui | apprendre et développer sur son poste |
| minikube | binaire + pilote (Docker, VM) | 1, multi possible | via minikube tunnel | via minikube image load | apprendre, add-ons prêts à l'emploi |
| kind | binaire, nœuds = conteneurs Docker | plusieurs | non (port-forward) | via kind load docker-image | tests automatisés, CI |
| Cloud (EKS, GKE, AKS…) | compte, facturation, réseau | autant que voulu | vraie IP publique | non, il faut un registre | production |
Toutes les commandes se tapent dans PowerShell (Windows) ou dans un terminal (macOS, Linux) ; elles sont identiques. Rien de ce qui suit ne modifie ton cluster : tu observes.
Lancer un conteneur avec Docker, comme tu sais déjà le faire. traefik/whoami est une mini application web qui affiche le nom de la machine qui répond : c'est l'image fil rouge des trois premiers modules.
docker run -d --name essai-whoami -p 8089:80 traefik/whoami:v1.10
docker ps --filter name=essai-whoami --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"Sortie réelle :
a088124875835ed375ddb25a4e4246faf62416a9772751ec7eedecc48bb758f8
NAMES IMAGE STATUS PORTS
essai-whoami traefik/whoami:v1.10 Up Less than a second 0.0.0.0:8089->80/tcp, [::]:8089->80/tcpCe qu'il faut voir : une ligne, Up, le port 8089 de ta machine redirigé vers le 80 du conteneur.
L'interroger. Le Hostname renvoyé est l'identifiant du conteneur : retiens ce détail, il deviendra le nom d'un Pod en leçon 04.
curl -s http://localhost:8089Sous Windows, tape curl.exe (le curl sans extension est un alias PowerShell d'Invoke-WebRequest). Sortie réelle (deux premières lignes) :
Hostname: a08812487583
IP: 127.0.0.1Regarde bien ce qui va se passer : tuer le conteneur. C'est la panne de trois heures du matin, en accéléré.
docker rm -f essai-whoami
docker ps --filter name=essai-whoami --format "table {{.Names}}\t{{.Status}}"Sortie réelle :
essai-whoami
NAMES STATUSCe qu'il faut voir : le tableau est vide. Personne ne relancera ce conteneur ; curl http://localhost:8089 échoue désormais (curl: (52) Empty reply from server, (56) Recv failure ou Connection refused selon le moment où Docker Desktop libère le port). Docker a fait ce qu'on lui a dit, ni plus ni moins. Garde cette image en tête : en leçon 04, tu feras la même chose à un Pod et il sera de retour avant que tu aies fini de relire la ligne.
Vérifier que Kubernetes est là. Si tu n'as pas encore activé Kubernetes dans Docker Desktop, saute à la leçon 02 et reviens ; sinon, regarde ton cluster.
kubectl get nodesSortie réelle :
NAME STATUS ROLES AGE VERSION
docker-desktop Ready control-plane 18d v1.34.1Ce qu'il faut voir : un seul nœud, Ready, rôle control-plane. Sur Docker Desktop, la machine qui décide et celle qui exécute sont la même. AGE est l'âge de ton cluster, VERSION celle de Kubernetes (1.34 ici).
Toucher le control plane du doigt. Les quatre composants « qui décident » tournent eux-mêmes comme des Pods, dans le namespace réservé kube-system, étiquetés tier=control-plane.
kubectl get pods -n kube-system -l tier=control-planeSortie réelle :
NAME READY STATUS RESTARTS AGE
etcd-docker-desktop 1/1 Running 0 18d
kube-apiserver-docker-desktop 1/1 Running 0 18d
kube-controller-manager-docker-desktop 1/1 Running 0 18d
kube-scheduler-docker-desktop 1/1 Running 0 18dCe qu'il faut voir : etcd, l'API server, le controller-manager et le scheduler, chacun 1/1 Running, 0 redémarrage. Leur nom se termine par le nom du nœud qui les héberge. Compare avec le tableau de « Comment ça marche » : tu as maintenant un visage pour chaque rôle.
Voir le reste de kube-system. Sans le filtre, tu découvres les composants du nœud et les ajouts propres à Docker Desktop.
kubectl get pods -n kube-systemSortie réelle sur la machine du cours (la ligne metrics-server vient d'un composant qu'on installera au module 6 : tu ne l'as pas encore, c'est normal) :
NAME READY STATUS RESTARTS AGE
coredns-66bc5c9577-6gwhl 1/1 Running 0 18d
coredns-66bc5c9577-9p89d 1/1 Running 0 18d
etcd-docker-desktop 1/1 Running 0 18d
kube-apiserver-docker-desktop 1/1 Running 0 18d
kube-controller-manager-docker-desktop 1/1 Running 0 18d
kube-proxy-2ddzv 1/1 Running 0 18d
kube-scheduler-docker-desktop 1/1 Running 0 18d
metrics-server-7c5fdf4664-6882s 1/1 Running 0 26m
storage-provisioner 1/1 Running 0 18d
vpnkit-controller 1/1 Running 0 18dCe qu'il faut voir : kube-proxy (les règles réseau du nœud), coredns ×2 (le DNS interne qui permettra à frontend d'appeler api par son nom), et deux pièces spécifiques à Docker Desktop : storage-provisioner (crée des volumes hostpath à la demande, module 5) et vpnkit-controller (c'est lui qui donne localhost aux Services LoadBalancer, leçon 04). Le kubelet, lui, n'apparaît pas : ce n'est pas un Pod mais un service système du nœud.
Facultatif : voir que Kubernetes n'est « que » des conteneurs. Si l'option « Show system containers » est cochée dans Docker Desktop (leçon 02), Docker te montre les conteneurs qui portent le control plane. C'est le même Docker que celui de l'étape 1.
docker ps --filter "name=k8s_kube-apiserver_" --filter "name=k8s_etcd_" --filter "name=k8s_kube-scheduler_" --filter "name=k8s_kube-controller-manager_" --format "table {{.Names}}\t{{.Status}}"Sortie réelle :
NAMES STATUS
k8s_etcd_etcd-docker-desktop_kube-system_0b753cb7812d40a401f3a8f63b18f779_0 Up 44 minutes
k8s_kube-apiserver_kube-apiserver-docker-desktop_kube-system_647244f1c75810d936baf4253b7903ef_0 Up 44 minutes
k8s_kube-scheduler_kube-scheduler-docker-desktop_kube-system_b44739859c757a4712b786569a89a1f3_0 Up 44 minutes
k8s_kube-controller-manager_kube-controller-manager-docker-desktop_kube-system_10b0d524eef4b9a12d5827ba17a36f4f_0 Up 44 minutesCe qu'il faut voir : le nom Docker est construit k8s_<conteneur>_<pod>_<namespace>_<uid>_<n>. Si l'option n'est pas cochée, le tableau est vide : rien de grave, c'est un réglage d'affichage. L'AGE de 18 jours côté Kubernetes et le Up 44 minutes côté Docker ne se contredisent pas : le cluster existe depuis 18 jours, Docker Desktop a été relancé il y a 44 minutes et a recréé ses conteneurs sans perdre l'état, stocké dans etcd.
Où parle-t-on ? Une dernière commande pour situer la porte d'entrée que toutes les autres emprunteront.
kubectl cluster-infoSortie réelle :
Kubernetes control plane is running at https://kubernetes.docker.internal:6443
CoreDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.Ce qu'il faut voir : l'API server écoute en HTTPS sur le port 6443 de kubernetes.docker.internal, un nom que Docker Desktop fait pointer vers ta machine. Quand Docker Desktop est arrêté, c'est ce port qui « refuse la connexion » (leçon 02, « Si ça coince »). Rien à nettoyer : le conteneur essai-whoami a été supprimé à l'étape 3 et tu n'as rien créé dans Kubernetes.
docker: Error response from daemon: failed to set up container networking: … Bind for 0.0.0.0:8089 failed: port is already allocated → un autre conteneur publie déjà le port 8089 (message provoqué en lançant deux fois l'étape 1 ; si c'est un programme hors Docker qui l'occupe, le message dit ports are not available). Change le port côté machine (-p 8090:80) ou repère l'occupant : Get-NetTCPConnection -LocalPort 8089 (PowerShell) / lsof -i :8089 (macOS, Linux).kubectl : Le terme 'kubectl' n'est pas reconnu (ou kubectl: command not found) → Kubernetes n'est pas activé, ou le terminal a été ouvert avant l'activation et n'a pas rechargé le PATH. Leçon 02 pour l'activer ; puis ferme et rouvre le terminal.Unable to connect to the server: dial tcp 127.0.0.1:6443: connectex: No connection could be made because the target machine actively refused it. → personne n'écoute sur 6443 : Docker Desktop est arrêté ou Kubernetes est désactivé. Lance Docker Desktop, attends « Kubernetes is up and running » dans la vue Kubernetes, réessaie.kubectl get pods -n kube-system -l tier=control-plane renvoie No resources found → tu es probablement sur un autre cluster (contexte minikube, kind-…) où le control plane n'est pas étiqueté pareil ou n'est pas visible. kubectl config current-context doit répondre docker-desktop ; la leçon 03 explique comment corriger.docker ps --filter "name=k8s_…" ne montre rien alors que Kubernetes tourne → « Show system containers (advanced) » n'est pas coché dans les réglages Kubernetes de Docker Desktop. Ce n'est pas une panne ; l'étape 7 est facultative.kube-apiserver (porte d'entrée, port 6443), etcd (mémoire du cluster), kube-scheduler (choisit le nœud), kube-controller-manager (boucles de réconciliation).kubelet (l'agent), kube-proxy (réseau des Services), runtime (docker://29.3.1 sur Docker Desktop) ; kubectl get pods -n kube-system -l tier=control-plane montre les quatre premiers en train de tourner.docker-desktop v1.34.1, images locales partagées avec Kubernetes, LoadBalancer sur localhost ; limites : un seul nœud, pas de NetworkPolicy appliquée, pas de vrai cloud.docker-desktop.Le nœud unique de Docker Desktop cache une différence importante avec un vrai cluster : en production, le control plane vit sur des machines dédiées (souvent trois, pour qu'etcd garde un quorum) et n'accueille aucun Pod applicatif, grâce à une « taint » node-role.kubernetes.io/control-plane:NoSchedule. Ici, kubectl describe node docker-desktop montre Taints: <none> : tout tourne au même endroit. La page Kubernetes Components de la documentation officielle détaille chaque composant vu dans cette leçon, avec les variantes possibles (cloud-controller-manager, autres runtimes).