1. Architecture et cluster local
Sections
Réglages de lecture
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
Code et termes techniques en anglais
Désactivé : la voix de la page lit tout, sans pause entre deux voix.
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
D’autres voix : Microsoft Edge propose des voix naturelles en ligne, et Windows (Paramètres › Heure et langue › Voix), macOS et Android en installent d’autres.
Code complet : code/kubernetes. La configuration du cluster est kind/cluster.yaml, et les commandes de cette leçon sont dans l01/run.sh. check.sh les exécute contre un vrai cluster et compare leurs sorties avec les fichiers de expected, après avoir remplacé les âges, les noms générés et les adresses IP par des marqueurs.
De docker run à un état désiré
Section intitulée « De docker run à un état désiré »Dans le cours Conteneurs WSL, vous avez construit une image et l’avez lancée avec une commande. Cette commande agissait tout de suite : elle démarrait un conteneur. Si le conteneur plantait, rien ne le relançait ; si la machine redémarrait, rien ne le redémarrait ; pour en lancer trois copies, vous lanciez la commande trois fois.
Kubernetes fonctionne à l’inverse. Vous ne lui dites pas quoi faire ; vous décrivez ce qui doit exister (« trois copies de cette image, joignables sous ce nom »), vous stockez cette description dans le cluster, et un ensemble de programmes la compare sans cesse avec ce qui tourne réellement et corrige l’écart. Un développeur C# a déjà écrit cette boucle, sous la forme d’un BackgroundService qui se réveille, lit l’état attendu dans une base de données, regarde la réalité, et agit sur l’écart. Kubernetes, ce sont des dizaines de boucles de ce genre, appelées contrôleurs, qui partagent une même base de données.
| Vous connaissez | Dans Kubernetes |
|---|---|
docker run d’une image |
un Pod : un ou plusieurs conteneurs placés ensemble (leçon 2) |
| lancer la commande trois fois, et redémarrer à la main | un Deployment : un nombre voulu de pods identiques, maintenus en vie et mis à jour (leçon 3) |
-p 8080:8080 et un nom d’hôte |
un Service : un nom et une adresse stables devant des pods qui changent (leçon 4) |
docker compose up avec un fichier YAML |
kubectl apply -f avec des manifestes YAML |
une boucle BackgroundService qui converge vers une cible |
un contrôleur |
appsettings.json et les variables d’environnement |
les ConfigMaps et les Secrets (leçon 5) |
L’architecture
Section intitulée « L’architecture »Un cluster a un plan de contrôle, qui stocke et décide, et des nœuds, qui exécutent les conteneurs. Rien sur un nœud ne parle directement à la base de données : chaque composant, kubectl compris, passe par le serveur d’API.
flowchart LR kubectl[kubectl] -->|HTTPS| api subgraph cp[Plan de contrôle] api[Serveur d'API] <--> etcd[(etcd)] sched[Ordonnanceur] -->|observe et assigne les pods| api cm[Gestionnaire de contrôleurs] -->|observe et réconcilie| api end subgraph node[Nœud] kubelet[kubelet] -->|observe ses pods, rapporte leur état| api kubelet --> cri[Runtime de conteneurs : containerd] proxy[kube-proxy] -->|observe les Services| api end- Le serveur d’API (
kube-apiserver) est une API REST sur HTTPS. Il authentifie l’appelant, valide chaque objet, le stocke, et permet aux autres composants d’observer (watch) les changements au lieu de les interroger en boucle. - etcd est le magasin clé-valeur derrière lui : le seul endroit où l’état du cluster est conservé.
- L’ordonnanceur (scheduler) guette les pods sans nœud, en choisit un, et enregistre ce choix. Il ne démarre rien lui-même.
- Le gestionnaire de contrôleurs (controller manager) exécute les contrôleurs intégrés : le contrôleur de ReplicaSet crée ou supprime des pods jusqu’à ce que leur nombre corresponde, le contrôleur de Deployment gère les ReplicaSets, et ainsi de suite.
- Sur chaque nœud, le kubelet guette les pods affectés à son nœud, demande au runtime de conteneurs (containerd ici) de démarrer leurs conteneurs, exécute leurs sondes, et rapporte leur état.
- kube-proxy traduit les Services en règles réseau sur chaque nœud.
La boucle est assez courte pour être lue. La fonction manageReplicas du contrôleur de ReplicaSet calcule diff := len(activePods) - int(*(rs.Spec.Replicas)), crée des pods quand la différence est négative et en supprime quand elle est positive. Elle ne sait pas pourquoi des pods manquent : un plantage, un pod supprimé et un nouveau ReplicaSet se ressemblent tous. C’est ce qui rend le modèle robuste, et c’est aussi pourquoi « j’ai supprimé le pod et il est revenu » surprend les débutants.
Choisir un cluster local
Section intitulée « Choisir un cluster local »Quatre outils font tourner un cluster sur un poste de développement. Tous fournissent un vrai serveur d’API ; ils diffèrent par la façon dont ils créent les nœuds.
| Outil | Les nœuds sont | Version vérifiée le 2026-09-15 | Bon pour |
|---|---|---|---|
| kind | des conteneurs Docker ou Podman | v0.33.0, Kubernetes 1.37.0 par défaut | la CI et les tests : le projet Kubernetes se teste avec ; plusieurs nœuds sur une machine |
| k3d | des conteneurs qui exécutent k3s, une distribution plus légère | v5.9.0 | une faible empreinte, avec un répartiteur de charge et Traefik intégrés |
| minikube | une VM ou un conteneur | v1.39.0, Kubernetes 1.37.0 par défaut | des modules (tableau de bord, ingress, métriques) activés d’une commande |
| Docker Desktop | kubeadm, ou kind depuis le nouveau provisionneur | dépend de la version de Docker Desktop | un cluster en cochant une case, si vous utilisez déjà Docker Desktop |
Ce cours utilise kind : il est petit, scriptable, identique sur les trois systèmes d’exploitation, et tourne dans GitHub Actions. Tout ce qu’il enseigne s’applique aux trois autres, et aux clusters managés (leçon 15), sauf quand une leçon dit le contraire.
kind a besoin de Docker (Docker Desktop sous Windows et macOS, Docker Engine ou Docker Desktop sous Linux) ou de Podman. L’outil wslc du cours Conteneurs WSL ne fait pas partie de ses fournisseurs.
Installer kind et kubectl
Section intitulée « Installer kind et kubectl »kubectl est le client en ligne de commande. Sa version doit rester à une version mineure au plus de celle du cluster ; ici, les deux sont en 1.37.0. Les commandes ci-dessous téléchargent les versions épinglées, comme le font le démarrage rapide de kind et les pages d’installation de kubectl.
mkdir $HOME\bin -Forcecurl.exe -Lo $HOME\bin\kind.exe https://kind.sigs.k8s.io/dl/v0.33.0/kind-windows-amd64curl.exe -Lo $HOME\bin\kubectl.exe https://dl.k8s.io/release/v1.37.0/bin/windows/amd64/kubectl.exe# Ajoute $HOME\bin au PATH de l'utilisateur, puis ouvre un nouveau terminal[Environment]::SetEnvironmentVariable('Path', "$([Environment]::GetEnvironmentVariable('Path','User'));$HOME\bin", 'User')curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.33.0/kind-linux-amd64curl -LO https://dl.k8s.io/release/v1.37.0/bin/linux/amd64/kubectlchmod +x ./kind ./kubectlsudo mv ./kind ./kubectl /usr/local/bin/# Apple silicon ; darwin-amd64 sur un Mac Intelcurl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.33.0/kind-darwin-arm64curl -LO https://dl.k8s.io/release/v1.37.0/bin/darwin/arm64/kubectlchmod +x ./kind ./kubectlsudo mv ./kind ./kubectl /usr/local/bin/Chaque version publie aussi une somme de contrôle SHA-256 à côté du binaire (kind, kubectl.exe.sha256 ou kubectl.sha256 pour kubectl) : comparez-la avec Get-FileHash ou sha256sum avant de faire confiance à un téléchargement. Les téléchargements Windows ont été exécutés sous Windows 11, et la ligne PATH est à vérifier : la machine de l’auteur avait déjà ses outils dans le chemin. Les commandes Linux suivent les mêmes pages et la CI du cours exécute leur équivalent sous Linux ; l’onglet macOS est à vérifier.
> kind versionkind v0.33.0 go1.26.7 windows/amd64> kubectl version --clientClient Version: v1.37.0Kustomize Version: v5.8.1Créer le cluster
Section intitulée « Créer le cluster »Un fichier de configuration kind nomme le cluster, liste ses nœuds, et épingle l’image de nœud par digest, pour que tout le monde exécute le même Kubernetes :
# Le cluster du cours : un nœud, épinglé sur Kubernetes v1.37.0.# kind create cluster --config kind/cluster.yamlkind: ClusterapiVersion: kind.x-k8s.io/v1alpha4name: learn-k8snodes: - role: control-plane image: kindest/node:v1.37.0@sha256:a1ed56cfb0e7b93589bdf97c8cd566405a265939e3620fc4f5de89adff580ae5 # Leçon 4 : un service NodePort sur 30080 répond sur http://localhost:30080 extraPortMappings: - containerPort: 30080 hostPort: 30080 listenAddress: 127.0.0.1Le digest est celui que kind v0.33.0 utilise par défaut, déclaré dans son defaults/image.go. Un seul nœud est à la fois le plan de contrôle et l’unique worker : assez pour les leçons 1 à 7, et léger pour un portable.
> kind create cluster --config kind/cluster.yamlCreating cluster "learn-k8s" ... ✓ Ensuring node image (kindest/node:v1.37.0) 🖼️ ✓ Preparing nodes 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾Set kubectl context to "kind-learn-k8s"You can now use your cluster with:
kubectl cluster-info --context kind-learn-k8sDans un terminal, chaque étape affiche d’abord un indicateur d’activité, et kind termine par un lien vers sa documentation ; les lignes ci-dessus sont l’état final. Cela a pris 58 secondes sur la machine de l’auteur, avec l’image de nœud déjà téléchargée. Le « nœud » est un conteneur Docker nommé learn-k8s-control-plane, qui exécute systemd, containerd, le kubelet, et le plan de contrôle sous forme de conteneurs à l’intérieur.
Faire le tour
Section intitulée « Faire le tour »> kubectl versionClient Version: v1.37.0Kustomize Version: v5.8.1Server Version: v1.37.0> kubectl get nodesNAME STATUS ROLES AGE VERSIONlearn-k8s-control-plane Ready control-plane 11m v1.37.0Le plan de contrôle décrit plus haut est bien là, sous forme de pods dans l’espace de noms kube-system :
> kubectl get pods -n kube-systemNAME READY STATUS RESTARTS AGEcoredns-559f6c778d-jlzvq 1/1 Running 0 11mcoredns-559f6c778d-nvpd5 1/1 Running 0 11metcd-learn-k8s-control-plane 1/1 Running 0 11mkindnet-8bcfl 1/1 Running 0 11mkube-apiserver-learn-k8s-control-plane 1/1 Running 0 11mkube-controller-manager-learn-k8s-control-plane 1/1 Running 0 11mkube-proxy-kxrkk 1/1 Running 0 11mkube-scheduler-learn-k8s-control-plane 1/1 Running 0 11metcd,kube-apiserver,kube-controller-manageretkube-schedulerse terminent par le nom du nœud : ce sont des pods statiques, que le kubelet démarre à partir de fichiers sur le nœud avant même qu’un serveur d’API existe. Le serveur d’API en montre ensuite une copie en lecture seule.coredns-559f6c778d-…a deux parties après son nom :559f6c778didentifie le ReplicaSet (un hash du modèle de pod), et les cinq derniers caractères sont aléatoires. CoreDNS répond aux requêtes DNS du cluster (leçon 4).kindnetest le plugin réseau de kind (le « CNI » du journal de création), etkube-proxytourne une fois par nœud : tous deux n’ont qu’un suffixe aléatoire, parce qu’un DaemonSet les crée (leçon 7).
Les fichiers des pods statiques sont sur le nœud, qui est un conteneur, donc docker exec peut les lister :
> docker exec learn-k8s-control-plane ls /etc/kubernetes/manifestsetcd.yamlkube-apiserver.yamlkube-controller-manager.yamlkube-scheduler.yamlkubectl parle HTTPS
Section intitulée « kubectl parle HTTPS »kubectl est un client HTTP bien élevé. Augmentez sa verbosité et chaque requête apparaît :
> kubectl get nodes -v=6I0915 00:17:57.387396 70612 round_trippers.go:632] "Response" verb="GET" url="https://127.0.0.1:55531/api/v1/nodes?limit=500" status="200 OK" milliseconds=6NAME STATUS ROLES AGE VERSIONlearn-k8s-control-plane Ready control-plane 116s v1.37.0Les lignes qui la précèdent, omises ici, indiquent quel fichier kubeconfig a été chargé et listent les feature gates du client.
L’adresse, les certificats et l’utilisateur viennent du fichier kubeconfig. Un contexte réunit un cluster, un utilisateur et un espace de noms par défaut sous un même nom :
> kubectl config get-contextsCURRENT NAME CLUSTER AUTHINFO NAMESPACE* kind-learn-k8s kind-learn-k8s kind-learn-k8skubectl config use-context <name> change de contexte, --context <name> vise un autre cluster le temps d’une commande, et -n <namespace> un autre espace de noms. Le port, 55531 ici, est choisi par Docker quand kind crée le nœud ; kind get kubeconfig --name learn-k8s réaffiche le fichier si vous le perdez.
L’API se documente aussi elle-même. kubectl explain lit le schéma que publie le serveur, et correspond donc toujours à la version du cluster :
> kubectl explain pod.spec.restartPolicyKIND: PodVERSION: v1
FIELD: restartPolicy <string>ENUM: Always Never OnFailure
DESCRIPTION: Restart policy for all containers within the pod. One of Always, OnFailure, Never. In some contexts, only a subset of those values may be permitted. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy
Possible enum values: - `"Always"` - `"Never"` - `"OnFailure"`kubectl api-resources liste chaque sorte d’objet, avec son nom court et son groupe d’API. Le groupe apps contient les charges de travail des leçons suivantes :
> kubectl api-resources --api-group=appsNAME SHORTNAMES APIVERSION NAMESPACED KINDcontrollerrevisions apps/v1 true ControllerRevisiondaemonsets ds apps/v1 true DaemonSetdeployments deploy apps/v1 true Deploymentreplicasets rs apps/v1 true ReplicaSetstatefulsets sts apps/v1 true StatefulSetUn premier état désiré
Section intitulée « Un premier état désiré »l01/hello.yaml demande deux copies de la plus petite image qui soit, pause, qui ne fait rien et ne se termine jamais. Le nœud l’a déjà, donc rien n’est téléchargé.
# Leçon 1 : un état désiré. Deux copies de la plus petite image qui soit : pause ne fait rien et ne se termine jamais.apiVersion: apps/v1kind: Deploymentmetadata: name: hellospec: replicas: 2 selector: matchLabels: app: hello template: metadata: labels: app: hello spec: containers: - name: pause image: registry.k8s.io/pause:3.10Chaque manifeste a les mêmes quatre champs de premier niveau : apiVersion et kind disent ce qu’est l’objet, metadata le nomme, et spec est l’état désiré. Les contrôleurs en ajoutent un cinquième, status, avec ce qu’ils observent.
Avant de stocker quoi que ce soit, le serveur d’API peut valider un fichier. Une faute de frappe dans un nom de champ, replica: au lieu de replicas: dans l01/hello-invalid.yaml, est rejetée :
> kubectl apply --dry-run=server -f l01/hello-invalid.yamlError from server (BadRequest): error when creating "l01/hello-invalid.yaml": Deployment in version "v1" cannot be handled as a Deployment: strict decoding error: unknown field "spec.replica"> kubectl apply --dry-run=server -f l01/hello.yamldeployment.apps/hello created (server dry run)Sans cluster, kubeconform vérifie les mêmes fichiers contre les schémas JSON publiés. Le script validate.sh du cours l’exécute sur chaque manifeste, sous Windows, Linux et macOS.
Chaque leçon travaille dans son propre espace de noms (namespace), une partition nommée du cluster, pour que supprimer l’espace de noms à la fin retire tout ce que la leçon a créé :
> kubectl create namespace l01namespace/l01 created> kubectl apply -n l01 -f l01/hello.yamldeployment.apps/hello created> kubectl rollout status -n l01 deployment/hellodeployment "hello" successfully rolled out> kubectl get -n l01 rs,pods -l app=helloNAME DESIRED CURRENT READY AGEreplicaset.apps/hello-6f8b555b4b 2 2 2 1s
NAME READY STATUS RESTARTS AGEpod/hello-6f8b555b4b-6q7s9 1/1 Running 0 1spod/hello-6f8b555b4b-fv257 1/1 Running 0 1skubectl rollout status attend que les pods soient prêts ; avant sa dernière ligne, il affiche un nombre variable de lignes Waiting for deployment…, omises ici et dans le reste du cours quand elles n’apportent rien. Un seul fichier a créé trois sortes d’objets : le Deployment, un ReplicaSet qu’il a créé, et deux pods que le ReplicaSet a créés. -l app=hello les sélectionne par label, de la même façon que le ReplicaSet trouve « ses » pods.
Changer l’état désiré
Section intitulée « Changer l’état désiré »l01/hello-3-replicas.yaml diffère d’une ligne. kubectl diff l’envoie au serveur, qui calcule ce qui changerait sans rien stocker :
> kubectl diff -n l01 -f l01/hello-3-replicas.yaml...- generation: 1+ generation: 2...- replicas: 2+ replicas: 3(La vraie sortie est un diff unifié entre deux répertoires temporaires ; les lignes ci-dessus en sont les changements.) generation compte les changements de spec : les contrôleurs la comparent avec la génération sur laquelle ils ont agi en dernier, et c’est ainsi que kubectl rollout status sait si un changement a été pris en compte.
Supprimer ce qui est désiré
Section intitulée « Supprimer ce qui est désiré »Supprimez les deux pods, et le contrôleur de ReplicaSet constate qu’il en manque deux :
> kubectl delete pods -n l01 -l app=hellopod "hello-6f8b555b4b-6q7s9" deleted from l01 namespacepod "hello-6f8b555b4b-fv257" deleted from l01 namespace> kubectl rollout status -n l01 deployment/hellodeployment "hello" successfully rolled out> kubectl get events -n l01 --field-selector involvedObject.kind=ReplicaSet -o custom-columns=REASON:.reason,MESSAGE:.messageREASON MESSAGESuccessfulCreate Created pod: hello-6f8b555b4b-6q7s9SuccessfulCreate Created pod: hello-6f8b555b4b-fv257SuccessfulCreate Created pod: hello-6f8b555b4b-xlnl4SuccessfulCreate Created pod: hello-6f8b555b4b-wtgnmDeux nouveaux pods, avec de nouveaux noms, créés en moins d’une seconde. Les événements sont le journal du contrôleur : les deux premières lignes datent de l’apply, les deux dernières de sa réaction à la suppression. Pour retirer les pods pour de bon, on change l’état désiré : on supprime le Deployment, ou tout l’espace de noms.
> kubectl delete namespace l01namespace "l01" deletedNettoyer
Section intitulée « Nettoyer »Un cluster kind continue de tourner, et d’utiliser de la mémoire, jusqu’à ce que vous le supprimiez. Ses images restent en cache dans Docker, donc le prochain kind create cluster est plus rapide :
kind delete cluster --name learn-k8sÀ retenir
Section intitulée « À retenir »- Un cluster Kubernetes stocke des états désirés dans etcd, derrière un serveur d’API auquel parle chaque composant ; les contrôleurs et les kubelets observent ces états et convergent vers eux.
- L’ordonnanceur ne fait que choisir un nœud, le kubelet démarre les conteneurs, et le contrôleur de ReplicaSet compte les pods : chacun fait un petit travail en boucle.
- kind exécute chaque nœud comme un conteneur Docker. C’est un vrai cluster, épinglé par le digest de l’image de nœud, et le même sous Windows, Linux et macOS.
kubectlest un client HTTPS configuré par un kubeconfig ; vérifiez le contexte courant avant les commandes destructrices.kubectl explain,api-resourceset--dry-run=serverinterrogent le cluster lui-même, donc leurs réponses correspondent à sa version.- Supprimer un pod géré par un contrôleur déclenche son remplacement ; on change ce qui existe en changeant l’état désiré.
Exercices
Section intitulée « Exercices »1. Passez hello à quatre réplicas avec kubectl scale -n l01 deployment/hello --replicas=4, vérifiez le résultat, puis relancez kubectl apply -n l01 -f l01/hello.yaml. Combien de réplicas reste-t-il, et pourquoi ?
Solution
> kubectl scale -n l01 deployment/hello --replicas=4deployment.apps/hello scaled> kubectl get -n l01 deployment helloNAME READY UP-TO-DATE AVAILABLE AGEhello 4/4 4 4 4s> kubectl apply -n l01 -f l01/hello.yamldeployment.apps/hello configured> kubectl get -n l01 deployment helloNAME READY UP-TO-DATE AVAILABLE AGEhello 2/2 2 2 5sDeux. kubectl scale a changé l’état désiré stocké à 4, mais le fichier dit toujours 2, et apply aligne l’objet stocké sur le fichier. Un changement impératif dure jusqu’au prochain apply : dans une équipe qui applique les manifestes depuis Git, c’est le fichier qui fait foi (leçon 14).
2. Quelle image exécute l’ordonnanceur, et avec quels arguments de ligne de commande ? Utilisez kubectl get pod … -o jsonpath plutôt que de lire le fichier sur le nœud.
Solution
> kubectl get pod -n kube-system kube-scheduler-learn-k8s-control-plane -o jsonpath='{.spec.containers[0].image}{"\n"}{range .spec.containers[0].command[*]}{@}{"\n"}{end}'registry.k8s.io/kube-scheduler:v1.37.0kube-scheduler--authentication-kubeconfig=/etc/kubernetes/scheduler.conf--authorization-kubeconfig=/etc/kubernetes/scheduler.conf--bind-address=127.0.0.1--kubeconfig=/etc/kubernetes/scheduler.conf--leader-elect=trueL’ordonnanceur joint le serveur d’API avec son propre kubeconfig, comme kubectl. --leader-elect=true permet à plusieurs copies de tourner dans un plan de contrôle hautement disponible, une seule d’entre elles ordonnançant. Sous Windows PowerShell, gardez les apostrophes autour de l’expression JSONPath.
3. Que se passe-t-il pendant une mise à jour progressive si vous ne fixez pas maxSurge ? Répondez avec kubectl explain, sans chercher sur le web.
Solution
> kubectl explain deployment.spec.strategy.rollingUpdate.maxSurgeGROUP: appsKIND: DeploymentVERSION: v1
FIELD: maxSurge <IntOrString>
DESCRIPTION: The maximum number of pods that can be scheduled above the desired number of pods. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up. Defaults to 25%. Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when the rolling update starts, such that the total number of old and new pods do not exceed 130% of desired pods. Once old pods have been killed, new ReplicaSet can be scaled up further, ensuring that total number of pods running at any time during the update is at most 130% of desired pods. IntOrString is a type that can hold an int32 or a string. When used in JSON or YAML marshalling and unmarshalling, it produces or consumes the inner type. This allows you to have, for example, a JSON field that can accept a name or number.La valeur par défaut est 25 % des réplicas désirés, arrondie au supérieur : avec 2 réplicas, un pod supplémentaire peut exister pendant la mise à jour. La leçon 3 la fixe explicitement.
- Documentation de Kubernetes : Architecture du cluster, Contrôleurs, Objets dans Kubernetes, Espaces de noms, Organiser l’accès aux clusters avec des fichiers kubeconfig, Validation des champs côté serveur
- Annonce de Kubernetes v1.37 et versions maintenues
- kind : démarrage rapide, configuration, notes de version v0.33.0
- Code source en v1.37.0 :
replica_set.go