Skip to content

4. L'architecture

Formation suivie via le blog de Stéphane Robert : parcours Kubernetes.

Un cluster Kubernetes se divise en deux parties : le control plane, qui décide, et les worker nodes, qui exécutent.

Schéma de l’architecture Kubernetes. En haut, le control plane (le cerveau) avec quatre composants : API Server (point d’entrée unique), etcd (stocke l’état du cluster), Scheduler (assigne les Pods aux Nodes) et Controller Manager (maintient l’état souhaité). En bas, les worker nodes (les muscles) : deux nodes, chacun avec Kubelet, Kube-proxy et containerd, qui exécutent des Pods.

Tout passe par l’API Server : kubectl, les contrôleurs, le Scheduler et les kubelets ne se parlent jamais directement. L’état du cluster est stocké dans etcd. Chaque composant observe l’état souhaité et agit pour que l’état réel le rejoigne : c’est la boucle de réconciliation.

  • Les composants observent l’API Server, pas etcd. Personne d’autre que l’API Server ne sait où est etcd, ni comment lui parler.
  • De même, les kubelets fonctionnent en mode « pull » : ils interrogent régulièrement l’API Server plutôt que d’attendre des ordres.

Séparer le control plane des worker nodes rend le cluster extensible, résilient et protégeable.

Extensible

Pour gagner en capacité, il suffit d’ajouter des worker nodes : ils s’enregistrent auprès de l’API Server et le Scheduler commence aussitôt à leur confier des Pods. Le control plane n’a pas à être modifié, et il peut lui-même être renforcé de son côté, indépendamment des workers.

Résilient

Une panne d’un côté n’emporte pas l’autre. Si un worker tombe, le control plane le détecte et recrée ses Pods sur les autres nodes. Si le control plane tombe, les applications déjà lancées continuent de tourner sur les workers (voir plus bas).

Protégeable

Le control plane détient ce qui est sensible : etcd et ses Secrets, et les droits sur tout le cluster. Séparé des workers, il peut être isolé : machines dédiées où ne tourne aucune application, accès réseau restreint à l’API Server, etcd joignable uniquement par l’API Server. Une application compromise sur un worker n’a donc pas d’accès direct au cœur du cluster.

Le control plane prend les décisions pour l’ensemble du cluster. Il ne fait tourner aucune application lui-même.

API Server

Le point d’entrée unique du cluster. Il reçoit toutes les requêtes (de kubectl comme des autres composants), vérifie les droits, valide les objets puis les enregistre dans etcd. C’est le seul composant qui lit et écrit dans etcd.

etcd

La base de données clé-valeur qui stocke l’état du cluster : tous les objets (Deployments, Pods, Services, Secrets…) y sont enregistrés. C’est la source de vérité : perdre etcd sans sauvegarde, c’est perdre le cluster.

Scheduler

Il assigne les Pods aux Nodes. Pour chaque Pod qui n’a pas encore de node, il choisit le plus adapté selon les ressources demandées et les contraintes de placement. Il ne démarre pas le Pod : il note seulement sur quel node il doit tourner.

Controller Manager

Il maintient l’état souhaité. Il regroupe les contrôleurs (Deployment, ReplicaSet, Node…) qui comparent en permanence l’état réel à l’état demandé et corrigent les écarts : recréer un Pod qui a disparu, signaler un node qui ne répond plus…

Chez un fournisseur cloud, un cinquième composant s’ajoute : le cloud-controller-manager.

Toute interaction avec le cluster passe par l’API Server. Il a trois missions :

Mission Ce qu’il fait
1 Valider vérifie le format de la requête, l’authentification et les autorisations
2 Persister stocke l’état dans etcd
3 Exposer permet de relire cet état via l’API REST

Pour vérifier qu’il est en bonne santé :

Terminal window
kubectl get --raw='/healthz'

Ce contrôle fonctionne où que soit le control plane, y compris quand il est managé par un fournisseur cloud et qu’on n’a pas accès à ses machines.

Une base de données clé-valeur distribuée qui stocke tout l’état du cluster, et uniquement lui : tout ce qu’on peut obtenir avec un kubectl get.

Il décide sur quel Node déployer chaque Pod, en comparant les besoins du Pod aux ressources disponibles sur chaque Node. Il procède en deux étapes :

1. Filtrage

Éliminer les Nodes impossibles :

  • pas assez de CPU ou de mémoire ;
  • taints incompatibles avec les tolérations du Pod ;
  • contraintes d’affinité non respectées.

2. Scoring

Classer les Nodes restants selon :

  • la répartition de charge (spread) ;
  • les affinités préférées ;
  • les ressources disponibles.

En continu, le Scheduler :

  1. observe les Pods qui n’ont pas encore de nodeName ;
  2. calcule le meilleur node pour chacun ;
  3. écrit l’association Pod → node dans l’API Server, qui la persiste dans etcd.

C’est le composant qui rend Kubernetes auto-correctif : il réconcilie en permanence l’état souhaité avec l’état actuel, grâce à une boucle de contrôle :

watch (observe l’état via l’API Server) → compare (souhaité vs actuel) → act (corrige l’écart)

Il regroupe plusieurs contrôleurs spécialisés dans un seul processus :

Contrôleur Rôle
Deployment Controller gère les rolling updates et les rollbacks
ReplicaSet Controller maintient le nombre de répliques
Node Controller détecte les Nodes en panne
Job Controller gère les Jobs et leur complétion
Service Account Controller crée les comptes de service par défaut
Endpoints Controller maintient les endpoints des Services

Un composant optionnel, qui n’existe que sur les clusters déployés chez un fournisseur cloud (AWS, GCP, Azure…). C’est l’un des composants d’intégration.

Il n’est nécessaire que si le cluster a besoin de ressources chez le fournisseur : load balancer, route, disque… Il faut alors lui configurer les droits IAM associés.

La communication entre composants : le modèle pull

Section titled “La communication entre composants : le modèle pull”

Dans Kubernetes, ce sont les composants qui appellent l’API Server, et non l’inverse : les kubelets vont eux-mêmes chercher ce qu’ils doivent faire. Conséquence : une panne du control plane ne coupe pas les applications déjà lancées (voir plus bas).

Composant Communique avec Via
kubectl API Server requêtes HTTPS
Scheduler API Server watch + update
Controller Manager API Server watch + update
kubelet API Server watch + report
API Server etcd lecture/écriture directe

Un worker node est une machine physique ou virtuelle où s’exécutent les Pods. Chaque node porte trois composants qui collaborent pour faire tourner les applications :

kubelet

L’agent de Kubernetes sur le node. Il récupère auprès de l’API Server les Pods qui lui sont assignés, demande au runtime de démarrer leurs conteneurs, vérifie qu’ils restent en bonne santé et remonte leur état.
Parle à : l’API Server (pull) et le runtime (CRI).

kube-proxy

Il traduit les Services en règles réseau sur le node, pour que le trafic envoyé à l’adresse d’un Service arrive bien sur l’un de ses Pods.
Parle à : l’API Server (watch des EndpointSlices).

Runtime de conteneurs

C’est lui qui télécharge les images et lance réellement les conteneurs (containerd, CRI-O).
Parle à : le kubelet, via le CRI.

Côté node aussi, c’est le modèle pull : le kubelet et kube-proxy interrogent régulièrement l’API Server pour connaître l’état désiré, au lieu de recevoir des ordres poussés. Seule exception : la connexion de l’API Server vers le kubelet pour les logs et l’exec.

Le composant central du node : il transforme les spécifications de Pods reçues de l’API Server en conteneurs réellement exécutés sur la machine. Sa boucle :

  1. Watch l’API Server pour connaître les Pods assignés à son node.
  2. Crée ou supprime les conteneurs via le runtime (CRI).
  3. Surveille la santé des conteneurs (probes).
  4. Reporte l’état du node et des Pods à l’API Server.

Toutes les 10 secondes, le kubelet envoie un heartbeat à l’API Server pour signaler que le node est vivant. Il contient :

  • l’état du node : Ready, NotReady ou Unknown ;
  • les ressources disponibles : CPU, mémoire, disque ;
  • les conditions : DiskPressure, MemoryPressure, PIDPressure.

Si les battements s’arrêtent :

Délai Ce qui se passe
50 secondes le node passe NotReady
≈ 5 minutes plus tard ses Pods sont replacés sur d’autres nodes
Probe Question posée Action si échec
livenessProbe Le conteneur est-il vivant ? redémarre le conteneur
readinessProbe Le conteneur peut-il recevoir du trafic ? le retire des EndpointSlices, sans y toucher
startupProbe Le conteneur a-t-il fini de démarrer ? bloque liveness et readiness

Tout se joue dans la colonne Action si échec : la livenessProbe tue et redémarre, la readinessProbe se contente de couper le trafic.

Un Static Pod est un Pod géré directement par le kubelet, et non par l’API Server. Au lieu d’attendre un kubectl, le kubelet surveille un dossier sur le disque du node : dès qu’un fichier YAML y apparaît, il tente de lancer le Pod.

Impossible de deviner où est ce dossier : il faut le demander à la configuration du kubelet.

Terminal window
grep staticPodPath /var/lib/kubelet/config.yaml

Ce dossier est vital : c’est lui qui permet de démarrer le cluster sans API Server. Pour avoir un API Server, il faut un cluster ; pour avoir un cluster, il faut un API Server. Les Static Pods cassent ce cercle.

Le CRI (Container Runtime Interface) est l’interface standardisée entre le kubelet et le runtime. Le kubelet ne crée jamais un conteneur lui-même : il émet une demande normalisée en gRPC et délègue tout le reste au runtime. C’est cette indirection qui permet de remplacer containerd par CRI-O sans toucher au kubelet.

  1. Le kubelet apprend par l’API Server qu’un Pod doit tourner sur son node.
  2. Il appelle le runtime via le CRI pour télécharger l’image, créer puis démarrer le conteneur.
  3. Le runtime crée le conteneur avec les namespaces et les cgroups Linux.

Plusieurs runtimes peuvent jouer ce rôle :

Runtime Description Statut
containerd Léger, prêt pour la production. Le plus répandu, utilisé par la plupart des distributions et des offres cloud (GKE, EKS, AKS, Kind, k3s). Né chez Docker, il est aujourd’hui un projet indépendant de la CNCF. par défaut depuis Kubernetes 1.24
CRI-O Conçu uniquement pour Kubernetes : il implémente le CRI et rien de plus. Utilisé notamment par Red Hat OpenShift. alternative
Docker Engine + cri-dockerd Le moteur Docker complet. Il n’implémente pas le CRI : Kubernetes ne peut l’utiliser qu’à travers un adaptateur externe, cri-dockerd. toujours documenté
Policy Comportement
IfNotPresent télécharge l’image seulement si elle n’existe pas localement (par défaut)
Always télécharge à chaque création de conteneur (par défaut pour le tag :latest)
Never ne télécharge jamais, utilise uniquement le cache local

Il gère les règles réseau qui font fonctionner les Services :

  • il watch les Services et les EndpointSlices via l’API Server ;
  • il configure les règles réseau pour router le trafic vers les bons Pods ;
  • il répartit le trafic entre les Pods d’un même Service.

Il s’exécute comme un DaemonSet : un Pod sur chaque node du cluster, y compris ceux ajoutés plus tard. Tous les nodes savent ainsi router le trafic vers les Services.

Mode Statut
iptables par défaut sur Linux, convient à la très grande majorité des installations
IPVS déprécié depuis Kubernetes 1.35
nftables —
Commande Effet
kubectl cordon le nœud n’accepte plus de nouveaux Pods
kubectl drain les Pods existants sont évacués vers les autres nœuds
kubeadm join un nouveau nœud rejoint le cluster
Symptôme Cause probable Solution
Node NotReady kubelet arrêté ou crashé systemctl restart kubelet
Node NotReady + DiskPressure disque plein crictl rmi --prune, puis revoir les seuils imageGC* du kubelet
Pods en Pending aucun node n’a assez de ressources ajouter un node ou libérer des ressources
Pods en ImagePullBackOff image introuvable ou registry inaccessible vérifier le nom de l’image et les identifiants
Pods en CrashLoopBackOff le conteneur plante au démarrage kubectl logs <pod> pour voir l’erreur

Pour diagnostiquer un node NotReady :

Terminal window
# Diagnostic rapide
kubectl describe node <nom-du-node> | grep -A5 Conditions
# Vérifier les logs du kubelet
journalctl -u kubelet --since "10 minutes ago" | grep -i error

Voir aussi la fiche Troubleshooting pour les Pods.

L’idée clé : Kubernetes définit des interfaces mais ne les implémente pas lui-même. Des composants externes s’en chargent. C’est ce qui permet au même cluster de tourner sur un poste de travail comme chez un fournisseur cloud, avec les mêmes objets.

CNI — le réseau

Container Network Interface. Donne une adresse IP aux Pods et leur permet de communiquer entre eux.
Où : un plugin sur chaque nœud.
Exemples : Calico, Cilium, Flannel.

CSI — le stockage

Container Storage Interface. Fournit les volumes persistants demandés par les Pods.
Où : un contrôleur, et un plugin sur chaque nœud.
Exemples : les drivers des fournisseurs cloud, Longhorn.

Cloud Controller Manager — le cloud

Fait le lien avec le fournisseur cloud : load balancers, disques, informations sur les machines.
Où : côté control plane.

Ingress Controller — le trafic HTTP

Applique les règles HTTP/HTTPS déclarées dans les objets Ingress.
Où : dans le cluster, sous forme de Pods.
Exemples : Traefik, HAProxy.

Un objet Kubernetes sans le composant qui l’implémente ne fait rien. C’est l’erreur de débutant la plus courante.

Ce que vous voyez Ce qui manque
Un Ingress ignoré, sans aucune erreur un Ingress Controller
Un PersistentVolumeClaim qui reste en Pending un driver CSI
Des nœuds qui restent en NotReady un plugin CNI

Kubernetes impose une règle réseau simple : chaque Pod a sa propre adresse IP et peut joindre n’importe quel autre Pod, même sur un autre nœud, sans traduction d’adresse. Mais il ne la met pas en œuvre lui-même : c’est le rôle du plugin CNI.

À chaque création de Pod, le runtime appelle le plugin CNI, qui :

  • crée l’interface réseau du Pod et lui attribue une adresse IP ;
  • installe les routes pour que le Pod soit joignable depuis les autres nœuds ;
  • pour certains plugins, applique aussi les NetworkPolicies, les règles qui filtrent le trafic entre Pods.
Plugin Points forts Cas d’usage typique
Calico Mature et performant. Gère complètement les NetworkPolicies, et y ajoute ses propres règles plus fines. Peut router sans tunnel (BGP) ou avec. Clusters de production, sur site ou dans le cloud, qui ont besoin de règles de sécurité réseau.
Flannel Très simple et léger, presque rien à configurer. En revanche, il n’applique pas les NetworkPolicies. Petits clusters, labs, apprentissage. C’est le CNI par défaut de k3s.
Cilium Basé sur eBPF (programmes exécutés dans le noyau Linux) : très performant, règles de sécurité jusqu’au niveau HTTP, observabilité du trafic avec Hubble. Peut remplacer kube-proxy. Grands clusters de production, besoins avancés en sécurité ou en observabilité.
Kindnet Minimal, installé automatiquement par Kind : aucune configuration. Clusters de test locaux avec Kind.

Ce qui se passe quand on crée un Deployment :

  1. kubectl apply -f deployment.yaml envoie le manifeste à l’API Server, qui le valide et l’enregistre dans etcd.

  2. Le contrôleur de Deployment, dans le Controller Manager, voit ce nouveau Deployment et crée un ReplicaSet. Le contrôleur de ReplicaSet crée à son tour les Pods. Ils n’ont pas encore de node : ils sont en Pending.

  3. Le Scheduler repère ces Pods sans node, choisit un node pour chacun et enregistre ce choix via l’API Server.

  4. Le kubelet du node choisi voit qu’un Pod lui est assigné. Il demande à containerd de télécharger l’image et de démarrer les conteneurs.

  5. Le kubelet remonte l’état du Pod à l’API Server : le Pod passe en Running. Si un Service sélectionne ce Pod, kube-proxy met à jour les règles réseau pour lui envoyer du trafic.

Les Pods déjà lancés continuent de tourner : les kubelets et containerd n’ont pas besoin du control plane pour faire vivre les conteneurs existants. En revanche, plus rien ne change : impossible de déployer, de mettre à l’échelle, et un Pod qui plante sur un node tombé n’est plus recréé ailleurs.

C’est pourquoi, en production, le control plane est réparti sur plusieurs machines (généralement 3), avec etcd répliqué entre elles.