Aller au contenu
English
English
Conteneurs & Orchestration medium

Fonctionnement des Worker Nodes Kubernetes

80 min de lecture

logo kubernetes

Les Worker Nodes sont les machines qui exécutent vos applications conteneurisées dans un cluster Kubernetes. Ce guide vous explique comment fonctionnent leurs trois composants essentiels, kubelet, kube-proxy et le container runtime, et comment les administrer au quotidien.

Ce que vous apprendrez :

  • Le rôle de chaque composant et leurs interactions
  • Comment le kubelet communique avec l'API Server (modèle pull)
  • L'interface CRI qui standardise l'exécution des conteneurs
  • Les commandes pour surveiller, drainer et dépanner vos nœuds

Un Worker Node est une machine, physique ou virtuelle, où s'exécutent les Pods. Chaque nœud porte trois composants qui collaborent pour faire tourner vos applications :

Architecture d'un Worker Node avec ses composants

ComposantRôleCommunique avec
kubeletAgent principal, gère les PodsAPI Server (pull), CRI
kube-proxyGère les règles réseau pour les ServicesAPI Server (watch EndpointSlices)
Container RuntimeExécute les conteneurskubelet via CRI

Tous les composants du Worker Node communiquent avec l'API Server en modèle pull : ils interrogent régulièrement l'API Server pour connaître l'état désiré, au lieu de recevoir des ordres poussés. Un nœud n'a donc aucun port à ouvrir vers le control plane.

Le kubelet est le composant central d'un Worker Node. C'est lui qui transforme les spécifications de Pods reçues de l'API Server en conteneurs réellement exécutés sur la machine. Sans kubelet, un nœud reste une machine inerte aux yeux du cluster.

Ces quatre missions tournent en boucle continue, et non une seule fois au démarrage. Le kubelet compare en permanence l'état désiré reçu de l'API Server avec l'état réel des conteneurs sur sa machine, puis agit dès qu'un écart apparaît. C'est ce qui explique qu'un conteneur tué à la main avec docker kill ou crictl stop réapparaisse quelques secondes après : le kubelet a constaté la disparition et l'a corrigée.

  1. Watch l'API Server pour les Pods assignés à son Node
  2. Crée/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

Le kubelet utilise le modèle pull : il établit une connexion persistante avec l'API Server et watch les changements concernant son Node.

Fenêtre de terminal
# Le kubelet watch les Pods pour son Node
GET /api/v1/pods?fieldSelector=spec.nodeName=worker-1&watch=true

Si l'API Server devient temporairement indisponible, le kubelet continue de faire tourner les Pods existants. Il ne peut simplement plus recevoir de nouvelles instructions. Cette autonomie du nœud est une résilience majeure de l'architecture Kubernetes.

Le kubelet envoie régulièrement un heartbeat à l'API Server pour signaler que le Node est vivant. Ce heartbeat contient :

  • L'état du Node (Ready, NotReady, Unknown)
  • Les ressources disponibles (CPU, mémoire, disque)
  • Les conditions (DiskPressure, MemoryPressure, PIDPressure)

Ce battement se produit toutes les dix secondes, et il se constate directement : relevez deux fois de suite l'horodatage du bail, le Lease que le kubelet renouvelle dans l'espace de noms kube-node-lease.

Fenêtre de terminal
kubectl get lease <node> -n kube-node-lease -o jsonpath='{.spec.renewTime}'

Retenez surtout ce qui se passe quand ces battements s'arrêtent, parce que le délai surprend souvent. Mesuré sur le cluster de la formation en arrêtant un nœud : il passe NotReady au bout de 50 secondes, et ses Pods ne sont replacés ailleurs qu'environ cinq minutes après. Cette lenteur est voulue : un réseau brièvement instable ne doit pas déclencher le déplacement de toutes les applications d'une machine.

Le kubelet exécute trois types de probes pour surveiller les conteneurs. La distinction tient dans la colonne Action si échec : la livenessProbe tue et redémarre le conteneur, là où la readinessProbe se contente de le retirer des destinations de trafic sans y toucher. Une livenessProbe trop agressive sur une application lente à répondre sous charge produit donc l'inverse de l'effet recherché, un redémarrage en pleine montée de trafic. La startupProbe existe pour ce cas : elle suspend les deux autres tant que l'application n'a pas fini de démarrer.

ProbeObjectifAction si échec
livenessProbeLe conteneur est-il vivant ?Redémarre le conteneur
readinessProbeLe conteneur peut-il recevoir du trafic ?Retire des EndpointSlices
startupProbeLe conteneur a-t-il démarré ?Bloque liveness/readiness
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
failureThreshold: 3 # Après 3 échecs → redémarrage

Le kubelet peut aussi créer des Static Pods à partir de manifests locaux, sans passer par l'API Server :

Fenêtre de terminal
# Le répertoire surveillé se lit dans la configuration du kubelet
grep staticPodPath /var/lib/kubelet/config.yaml
Sortie sur un nœud kubeadm
staticPodPath: /etc/kubernetes/manifests

/etc/kubernetes/manifests/ n'est pas un défaut du kubelet mais la valeur que kubeadm écrit dans staticPodPath. Sans ce champ, le kubelet ne lit aucun manifeste local, et déposer un fichier n'a aucun effet. La nuance compte à l'examen comme en incident : sur un nœud dont vous n'avez pas fait l'installation, c'est la configuration du kubelet qu'il faut lire, pas un chemin supposé.

Les composants du control plane, API Server, etcd, Scheduler et Controller Manager, sont déployés ainsi par kubeadm. C'est exactement ce qui permet de démarrer un cluster alors que l'API Server n'existe pas encore pour les créer.

Cette autonomie a une contrepartie : un Static Pod ne peut référencer aucune ressource de l'API, ni Secret ni ConfigMap. La logique est cohérente, le kubelet lisant ces manifestes sans interroger l'API Server : il n'a aucun moyen d'en résoudre les références.

Le piège tient à l'endroit où l'erreur apparaît. Le fichier est accepté à l'écriture, le Pod n'apparaît jamais dans kubectl get pods, et rien ne signale pourquoi. Seuls les journaux du kubelet le disent :

Fenêtre de terminal
journalctl -u kubelet --no-pager | grep static
Sortie
Could not process manifest file
err="static pods may not reference configmaps"
path="/etc/kubernetes/manifests/test-static.yaml"

Cette restriction vaut sur toutes les versions maintenues : ne comptez pas la contourner.

Le CRI (Container Runtime Interface) est l'interface standardisée entre le kubelet et le runtime de conteneurs. Introduite dans Kubernetes 1.5, elle permet de changer de runtime sans modifier le kubelet.

Le point à retenir de cet enchaînement : 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.

Flux kubelet → CRI → containerd → conteneur

  1. L'API Server notifie le kubelet qu'un Pod doit tourner sur ce Node

  2. Le kubelet appelle le runtime via CRI (gRPC) pour :

    • Télécharger l'image (si absente)
    • Créer le conteneur
    • Démarrer le conteneur
  3. Le runtime (containerd) crée le conteneur avec les namespaces et cgroups Linux

Kubernetes ne livre aucun runtime : la colonne « Défaut depuis » désigne le choix retenu par les distributions et les installateurs, pas un composant intégré au projet. Ce qui a changé en 1.24, c'est la suppression de dockershim, le pont interne qui permettait au kubelet de piloter Docker sans passer par CRI. containerd s'est imposé comme choix par défaut de kubeadm, des offres managées et de la plupart des distributions ; CRI-O reste le runtime de référence sur OpenShift.

RuntimeDescriptionDéfaut depuis
containerdLéger, production-ready, projet CNCFK8s 1.24+
CRI-OConçu spécifiquement pour KubernetesAlternative
Docker Engine + cri-dockerdDocker via un adaptateur CRIToujours documenté

Le kubelet respecte la politique imagePullPolicy pour décider quand télécharger les images :

PolicyComportement
IfNotPresentPull si l'image n'existe pas localement (défaut)
AlwaysPull à chaque création de conteneur (défaut pour :latest)
NeverNe pull jamais, utilise uniquement le cache local
containers:
- name: app
image: nginx:1.25@sha256:a484819eb60211f5299034ac80f6a681b06f89e65866ce91f356ed7c72af059c
imagePullPolicy: IfNotPresent # Défaut si tag présent

Il existe un cas où kubectl ne peut rien pour vous : quand le kubelet lui-même ne répond plus. Le runtime, lui, continue de tourner, et un outil dédié permet de l'interroger directement sur le nœud. C'est un geste d'administration, décrit avec le runtime dans CNI, CSI et CRI.

kube-proxy gère les règles réseau qui permettent aux Services de fonctionner. Il s'exécute sur chaque Node du cluster (en tant que DaemonSet).

Une précision importante : kube-proxy ne voit pas passer le trafic. Malgré son nom, il n'est pas un proxy applicatif placé dans le chemin des paquets ; il programme le noyau Linux pour que celui-ci réécrive les adresses de destination. Le routage effectif revient donc au noyau, ce qui explique qu'un kube-proxy arrêté ne coupe pas les connexions existantes : les règles déjà installées continuent de s'appliquer.

  1. Watch les Services et EndpointSlices via l'API Server
  2. Configure les règles réseau pour router le trafic vers les bons Pods
  3. Load balance le trafic entre les Pods d'un même Service

kube-proxy sait programmer ces règles de trois façons différentes. Le choix ne se pose qu'à grande échelle, au-delà du millier de Services, et le mode iptables reste le défaut sur Linux : c'est celui de votre cluster, et il convient à la très grande majorité des installations.

Deux repères si vous croisez des recommandations anciennes : le mode IPVS est déprécié depuis Kubernetes 1.35, et le remplaçant pour les gros clusters est désormais nftables, en version générale depuis la 1.33. Le détail de ces modes et leur réglage appartiennent au réseau des Pods.

kube-proxy s'exécute comme un DaemonSet : un Pod sur chaque Node du cluster, y compris ceux ajoutés plus tard. Cette forme garantit que tous les nœuds savent router le trafic vers les Services.

Fenêtre de terminal
# Voir le DaemonSet kube-proxy
kubectl get ds -n kube-system kube-proxy
# Voir les Pods kube-proxy sur tous les nœuds
kubectl get pods -n kube-system -l k8s-app=kube-proxy -o wide

Un nœud se pilote : on en ajoute quand la charge monte, on en sort un du service pour le mettre à jour, on en retire un définitivement. Ces trois gestes supposent la répartition des rôles décrite ci-dessus, et relèvent de l'exploitation plutôt que de l'architecture : chacun a sa page dédiée.

GesteCe qu'il faitOù il est traité
kubectl cordonle nœud n'accepte plus de nouveaux PodsGérer les nœuds
kubectl drainles Pods existants sont évacués vers les autres nœudsGérer les nœuds
kubeadm joinun nouveau nœud rejoint le clusterInstaller avec kubeadm

Ces cinq symptômes se répartissent en deux niveaux qu'il ne faut pas confondre. Les deux premiers concernent le nœud et se diagnostiquent en SSH sur la machine, avec journalctl et df. Les trois suivants concernent les Pods et se traitent entièrement avec kubectl, sans jamais toucher au nœud. Identifiez donc le niveau avant d'agir : redémarrer un kubelet parce que des Pods sont en CrashLoopBackOff déplace le problème sans le résoudre.

SymptômeCause probableSolution
Node NotReadykubelet arrêté ou crashsystemctl restart kubelet
Node NotReady + DiskPressureDisque pleincrictl rmi --prune, puis revoir les seuils imageGC* du kubelet
Pods en PendingPas de Node avec ressources suffisantesAjouter un Node ou libérer des ressources
Pods en ImagePullBackOffImage introuvable ou registry inaccessibleVérifier le nom de l'image et les credentials
Pods en CrashLoopBackOffConteneur qui crashe au démarragekubectl logs <pod> pour voir l'erreur
Fenêtre de terminal
# Diagnostic rapide d'un Node NotReady
kubectl describe node <nom-du-node> | grep -A5 Conditions
# Vérifier les logs kubelet
journalctl -u kubelet --since "10 minutes ago" | grep -i error

Les questions portent uniquement sur ce qui vient d'être expliqué : rôle de chaque composant, modèle en tirage, délais de bascule d'un nœud et séquence de maintenance. Si une réponse vous échappe, la section correspondante plus haut contient l'explication.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
5 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • Le kubelet est l'agent principal qui gère les Pods sur chaque Node
  • Le kubelet communique avec l'API Server via le modèle pull (watch)
  • Le CRI standardise la communication kubelet ↔ runtime de conteneurs
  • containerd s'est imposé comme runtime par défaut des distributions après le retrait de dockershim en Kubernetes 1.24
  • Le kube-proxy programme le noyau pour router les Services : iptables par défaut, nftables recommandé à grande échelle, IPVS déprécié depuis 1.35
  • kube-proxy s'exécute en DaemonSet sur tous les Nodes
  • Un Node NotReady signifie que le kubelet n'envoie plus de heartbeat depuis plus de 50 secondes, l'éviction des Pods suit environ 5 minutes plus tard
  • Utilisez cordon → drain → maintenance → uncordon pour la maintenance

Deux exercices sur le kubelet, dans l'ordre où on le rencontre : d'abord ce qu'il sait faire seul, ensuite ce qui se passe quand il refuse de démarrer. Le premier pose un Pod statique, géré par le kubelet du nœud et non par le scheduler : vous déposez le manifeste à l'endroit que désigne sa configuration, vous voyez le Pod apparaître dans l'API sous son nom de miroir, et le runtime du worker doit réellement exécuter le conteneur.

Le second part d'un kubelet qui s'arrête aussitôt lancé, nœud NotReady et application dégradée. Rien ne se diagnostique depuis kubectl : la cause est dans le journal du service, qui reproche quelque chose de précis à sa configuration. Il faut le lire, corriger le fichier sans rien casser d'autre, et prouver que le nœud et son application sont revenus.

  • CNI, CSI et CRI : Les trois interfaces par lesquelles le kubelet délègue réseau, stockage et exécution.
  • Haute disponibilité : Ce qui change côté nœuds quand le control plane passe à trois instances.
  • Installer avec kubeadm : La jointure d'un worker au cluster, commande par commande.
  • Gérer les nœuds : Les labels, taints et remplacements de nœud au quotidien.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens ce site gratuitement, sans publicité, sans profilage et sans compte à créer. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn