Pourquoi Kubernetes, et pourquoi Docker Desktop

13 min
Public
développeur ou étudiant qui a déjà tapé docker run et docker compose up, jamais kubectl
Durée
25 à 35 min
Module
1/8
Compétence visée
expliquer ce que Kubernetes ajoute à Docker, nommer les composants du control plane et d'un nœud, et justifier le choix de Docker Desktop pour apprendre

En une image

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.

Comment ça marche

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èreDocker ComposeDocker SwarmKubernetes
Périmètreune machineplusieurs machinesplusieurs machines (des milliers)
Relance automatiquerestart: seulementouioui, avec sondes de santé (readiness, liveness)
Mise à jour sans coupurenonoui, basiqueoui, progressive, avec retour arrière en une commande
Répartition de charge intégréenonouioui (Services), plus Ingress, DNS interne
Montée en charge automatiquenonnonoui (HPA, sur CPU, mémoire ou métriques métier)
Écosystème (Helm, opérateurs, cloud)petitimmense
Courbe d'apprentissagedoucemoyenneraide, 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.

ComposantRôle en une phrase
kube-apiservercontrol planeReçoit toutes les commandes (kubectl, contrôleurs, kubelet) en HTTPS sur le port 6443 ; rien ne se passe sans passer par lui.
etcdcontrol planeBase 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-schedulercontrol planeChoisit sur quel nœud placer chaque nouveau Pod (ressources libres, contraintes). Il ne lance rien lui-même.
kube-controller-managercontrol planeRegroupe 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 ».
kubeletchaque nœudL'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-proxychaque nœudProgramme les règles réseau pour qu'une adresse de Service arrive au bon Pod.
runtimechaque nœudLe 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.

OptionInstallationNœudsLoadBalancer sur localhostImages locales sans registrePour qui
Docker Desktop (ce cours)un clic dans l'application1 (kubeadm) ou plusieurs (kind, 4.38+)ouiouiapprendre et développer sur son poste
minikubebinaire + pilote (Docker, VM)1, multi possiblevia minikube tunnelvia minikube image loadapprendre, add-ons prêts à l'emploi
kindbinaire, nœuds = conteneurs Dockerplusieursnon (port-forward)via kind load docker-imagetests automatisés, CI
Cloud (EKS, GKE, AKS…)compte, facturation, réseauautant que vouluvraie IP publiquenon, il faut un registreproduction

Pas à pas

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.

  1. 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.

    bash
    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 :

    text
    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/tcp

    Ce qu'il faut voir : une ligne, Up, le port 8089 de ta machine redirigé vers le 80 du conteneur.

  2. 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.

    bash
    curl -s http://localhost:8089

    Sous Windows, tape curl.exe (le curl sans extension est un alias PowerShell d'Invoke-WebRequest). Sortie réelle (deux premières lignes) :

    text
    Hostname: a08812487583
    IP: 127.0.0.1
  3. Regarde bien ce qui va se passer : tuer le conteneur. C'est la panne de trois heures du matin, en accéléré.

    bash
    docker rm -f essai-whoami
    docker ps --filter name=essai-whoami --format "table {{.Names}}\t{{.Status}}"

    Sortie réelle :

    text
    essai-whoami
    NAMES     STATUS

    Ce 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.

  4. 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.

    bash
    kubectl get nodes

    Sortie réelle :

    text
    NAME             STATUS   ROLES           AGE   VERSION
    docker-desktop   Ready    control-plane   18d   v1.34.1

    Ce 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).

  5. 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.

    bash
    kubectl get pods -n kube-system -l tier=control-plane

    Sortie réelle :

    text
    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          18d

    Ce 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.

  6. Voir le reste de kube-system. Sans le filtre, tu découvres les composants du nœud et les ajouts propres à Docker Desktop.

    bash
    kubectl get pods -n kube-system

    Sortie 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) :

    text
    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          18d

    Ce 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.

  7. 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.

    bash
    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 :

    text
    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 minutes

    Ce 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.

  8. Où parle-t-on ? Une dernière commande pour situer la porte d'entrée que toutes les autres emprunteront.

    bash
    kubectl cluster-info

    Sortie réelle :

    text
    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.

Si ça coince

  • 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.
  • La commande 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.

À retenir

  • Docker exécute un conteneur et s'arrête là ; Kubernetes maintient un état désiré (combien de copies, quelle image, quel port) et corrige en boucle l'écart avec l'état observé.
  • Control plane = 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).
  • Nœud = 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 : un clic, un nœud 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.
  • Les manifestes que tu écriras ici fonctionnent sans changement sur minikube, kind, EKS, GKE ou AKS.
  • Prochaine leçon : installer Docker Desktop sur ton système, activer Kubernetes, et voir apparaître le contexte docker-desktop.

Pour aller plus loin

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).