
Kubernetes ne programme pas les Pods “au hasard”. Vous pouvez contrôler précisément où s’exécutent vos workloads grâce à plusieurs mécanismes complémentaires. Ce guide présente les outils modernes de scheduling : du simple nodeSelector jusqu’aux topologySpreadConstraints pour la haute disponibilité.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Cibler des nœuds spécifiques avec
nodeSelectoretnodeAffinity - Gérer les relations entre Pods avec
podAffinity/podAntiAffinity - Répartir proprement les replicas avec
topologySpreadConstraints - Protéger et réserver des nœuds avec
taints/tolerations - Débugger les Pods bloqués en
Pending
Vue d’ensemble des mécanismes
Section intitulée « Vue d’ensemble des mécanismes »| Mécanisme | Objectif | Agit sur |
|---|---|---|
nodeName | Forcer un nœud précis (bypass scheduler) | Nœud |
nodeSelector | Filtrer par labels (simple) | Nœud |
nodeAffinity | Filtrer par labels (avancé, préférence/contrainte) | Nœud |
podAffinity | Rapprocher des Pods liés | Pods |
podAntiAffinity | Éloigner des Pods | Pods |
topologySpreadConstraints | Répartir les replicas entre domaines | Topologie |
taints / tolerations | Protéger/réserver des nœuds | Nœud |
Labels standardisés Kubernetes
Section intitulée « Labels standardisés Kubernetes »Kubernetes définit des labels standardisés pour la topologie. Préférez-les aux labels custom quand ils existent :
| Label | Description |
|---|---|
kubernetes.io/hostname | Nom du nœud |
topology.kubernetes.io/zone | Zone de disponibilité |
topology.kubernetes.io/region | Région |
node.kubernetes.io/instance-type | Type d’instance (cloud) |
# Voir les labels d'un nœudkubectl get nodes --show-labels1. nodeName : contourner le scheduler
Section intitulée « 1. nodeName : contourner le scheduler »Le champ nodeName lie directement un Pod à un nœud, sans passer par le scheduler.
apiVersion: v1kind: Podmetadata: name: pod-on-specific-nodespec: containers: - name: my-container image: my-image nodeName: my-node-1Cas d’usage :
- Dépannage ou tests sur un nœud précis
- Situations très spécifiques où vous savez exactement où placer le Pod
À éviter en production — préférez nodeSelector ou nodeAffinity pour des règles dynamiques.
2. nodeSelector : le filtre simple
Section intitulée « 2. nodeSelector : le filtre simple »nodeSelector est la méthode la plus simple pour contraindre un Pod à des nœuds ayant un label spécifique.
Poser un label sur un nœud
Section intitulée « Poser un label sur un nœud »kubectl label nodes node-gpu accelerator=nvidia-gpuUtiliser nodeSelector
Section intitulée « Utiliser nodeSelector »apiVersion: v1kind: Podmetadata: name: pod-gpuspec: containers: - name: my-container image: my-image nodeSelector: accelerator: nvidia-gpu- Seuls les nœuds ayant
accelerator=nvidia-gpupeuvent accueillir ce Pod - Si aucun nœud ne correspond, le Pod reste en
Pending - Limitation : égalités strictes uniquement (
key=value)
3. nodeAffinity : préférences et contraintes
Section intitulée « 3. nodeAffinity : préférences et contraintes »nodeAffinity offre plus de flexibilité que nodeSelector avec deux modes :
| Mode | Comportement |
|---|---|
requiredDuringSchedulingIgnoredDuringExecution | Obligatoire — le Pod ne sera pas programmé si aucun nœud ne correspond |
preferredDuringSchedulingIgnoredDuringExecution | Préférence — Kubernetes essaie de respecter la règle, mais peut la contourner |
Affinité obligatoire
Section intitulée « Affinité obligatoire »apiVersion: v1kind: Podmetadata: name: pod-gpu-requiredspec: containers: - name: my-container image: my-image affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: accelerator operator: In values: - nvidia-gpu - amd-gpuLe Pod ne sera programmé que sur un nœud avec accelerator=nvidia-gpu ou accelerator=amd-gpu.
Affinité préférée
Section intitulée « Affinité préférée »apiVersion: v1kind: Podmetadata: name: pod-gpu-preferredspec: containers: - name: my-container image: my-image affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 preference: matchExpressions: - key: accelerator operator: In values: - nvidia-gpuKubernetes préférera un nœud GPU, mais programmera le Pod ailleurs si aucun n’est disponible.
Opérateurs disponibles
Section intitulée « Opérateurs disponibles »| Opérateur | Description |
|---|---|
In | La valeur est dans la liste |
NotIn | La valeur n’est pas dans la liste |
Exists | La clé existe (peu importe la valeur) |
DoesNotExist | La clé n’existe pas |
Gt | Valeur supérieure à (numérique) |
Lt | Valeur inférieure à (numérique) |
4. podAffinity et podAntiAffinity : relations entre Pods
Section intitulée « 4. podAffinity et podAntiAffinity : relations entre Pods »Ces mécanismes influencent la répartition des Pods les uns par rapport aux autres, pas par rapport aux nœuds.
podAffinity : rapprocher des Pods
Section intitulée « podAffinity : rapprocher des Pods »Utile pour réduire la latence entre services liés (ex : application + cache Redis).
apiVersion: v1kind: Podmetadata: name: web-app labels: app: webspec: containers: - name: web-container image: my-web-image affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - redis topologyKey: kubernetes.io/hostnameCe Pod sera programmé sur le même nœud qu’un Pod ayant app=redis.
podAntiAffinity : éloigner des Pods
Section intitulée « podAntiAffinity : éloigner des Pods »Utile pour éviter les SPOF — répartir les replicas d’un même service.
apiVersion: apps/v1kind: Deploymentmetadata: name: web-appspec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - web topologyKey: kubernetes.io/hostname containers: - name: web-container image: my-web-imageKubernetes empêchera plusieurs Pods app=web de tourner sur le même nœud.
topologyKey : niveau de la contrainte
Section intitulée « topologyKey : niveau de la contrainte »| topologyKey | Effet |
|---|---|
kubernetes.io/hostname | Répartition par nœud |
topology.kubernetes.io/zone | Répartition par zone |
topology.kubernetes.io/region | Répartition par région |
5. topologySpreadConstraints : répartir proprement les replicas
Section intitulée « 5. topologySpreadConstraints : répartir proprement les replicas »topologySpreadConstraints est le mécanisme moderne pour contrôler la répartition des Pods entre domaines de topologie (nœuds, zones, régions). Il est souvent plus adapté que podAntiAffinity pour la haute disponibilité.
Pourquoi utiliser topologySpreadConstraints ?
Section intitulée « Pourquoi utiliser topologySpreadConstraints ? »| Besoin | podAntiAffinity | topologySpreadConstraints |
|---|---|---|
| ”Jamais 2 Pods sur le même nœud” | ✅ Strict | ⚠️ Possible mais pas l’usage principal |
| ”Répartir équitablement entre zones” | ⚠️ Complexe | ✅ Conçu pour |
| ”Tolérer un léger déséquilibre” | ❌ | ✅ maxSkew |
| ”Répartir sur zones ET nœuds” | ⚠️ Complexe | ✅ Multiple constraints |
Exemple basique
Section intitulée « Exemple basique »apiVersion: apps/v1kind: Deploymentmetadata: name: web-appspec: replicas: 6 selector: matchLabels: app: web template: metadata: labels: app: web spec: topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: web containers: - name: web-container image: my-web-imageParamètres clés
Section intitulée « Paramètres clés »| Paramètre | Description |
|---|---|
maxSkew | Écart maximum toléré entre domaines (1 = équilibré) |
topologyKey | Label définissant les domaines (kubernetes.io/hostname, topology.kubernetes.io/zone) |
whenUnsatisfiable | DoNotSchedule (strict) ou ScheduleAnyway (best-effort) |
labelSelector | Sélectionne les Pods concernés par le spread |
Répartition multi-niveaux (zones + nœuds)
Section intitulée « Répartition multi-niveaux (zones + nœuds) »spec: topologySpreadConstraints: # D'abord répartir entre zones - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: web # Puis répartir entre nœuds dans chaque zone - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: app: web6. Taints et Tolerations : protéger et réserver des nœuds
Section intitulée « 6. Taints et Tolerations : protéger et réserver des nœuds »Les taints et tolerations fonctionnent à l’inverse de l’affinité :
- Un taint sur un nœud repousse les Pods par défaut
- Une toleration sur un Pod lui permet d’ignorer ce taint
Ajouter un taint à un nœud
Section intitulée « Ajouter un taint à un nœud »kubectl taint nodes <node-name> <key>=<value>:<effect>Effets disponibles
Section intitulée « Effets disponibles »| Effet | Comportement |
|---|---|
NoSchedule | Bloque les nouveaux Pods sans toleration |
PreferNoSchedule | Évite les nouveaux Pods sans toleration (best-effort) |
NoExecute | Bloque + expulse les Pods existants sans toleration |
Exemple : réserver des nœuds GPU
Section intitulée « Exemple : réserver des nœuds GPU »# Tainter le nœud GPUkubectl taint nodes node-gpu accelerator=nvidia-gpu:NoScheduleTous les Pods standards seront bloqués sur ce nœud.
Ajouter une toleration à un Pod
Section intitulée « Ajouter une toleration à un Pod »apiVersion: v1kind: Podmetadata: name: pod-gpuspec: containers: - name: my-container image: my-image tolerations: - key: "accelerator" operator: "Equal" value: "nvidia-gpu" effect: "NoSchedule" # Pour CIBLER le nœud, ajoutez aussi un selector nodeSelector: accelerator: nvidia-gpuOpérateurs de toleration
Section intitulée « Opérateurs de toleration »| Opérateur | Description |
|---|---|
Equal | La clé et la valeur doivent correspondre |
Exists | La clé doit exister (valeur ignorée) |
tolerationSeconds : tolérer temporairement NoExecute
Section intitulée « tolerationSeconds : tolérer temporairement NoExecute »Avec NoExecute, vous pouvez permettre à un Pod de rester temporairement sur un nœud devenu tainté :
tolerations:- key: "node.kubernetes.io/not-ready" operator: "Exists" effect: "NoExecute" tolerationSeconds: 300Le Pod restera 5 minutes sur un nœud devenu not-ready avant d’être évincé. Utile pour tolérer des perturbations réseau temporaires.
Taints courants du système
Section intitulée « Taints courants du système »| Taint | Description |
|---|---|
node.kubernetes.io/not-ready | Nœud pas prêt |
node.kubernetes.io/unreachable | Nœud injoignable |
node.kubernetes.io/disk-pressure | Pression disque |
node.kubernetes.io/memory-pressure | Pression mémoire |
node.kubernetes.io/unschedulable | Nœud en cordon |
Supprimer un taint
Section intitulée « Supprimer un taint »kubectl taint nodes <node-name> <key>:<effect>-# Exemple :kubectl taint nodes node-gpu accelerator:NoSchedule-7. Stratégies combinées : exemples pratiques
Section intitulée « 7. Stratégies combinées : exemples pratiques »Exemple 1 : Nœuds GPU dédiés
Section intitulée « Exemple 1 : Nœuds GPU dédiés »Objectif : réserver des nœuds GPU aux workloads ML/AI.
-
Labelliser et tainter les nœuds GPU
Fenêtre de terminal kubectl label nodes node-gpu-1 node-gpu-2 accelerator=nvidia-gpukubectl taint nodes node-gpu-1 node-gpu-2 accelerator=nvidia-gpu:NoSchedule -
Créer le Pod ML avec toleration + nodeAffinity
apiVersion: v1kind: Podmetadata:name: ml-trainingspec:containers:- name: trainingimage: tensorflow/tensorflow:latest-gputolerations:- key: "accelerator"operator: "Equal"value: "nvidia-gpu"effect: "NoSchedule"affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: acceleratoroperator: Invalues:- nvidia-gpu
Exemple 2 : Haute disponibilité multi-zones
Section intitulée « Exemple 2 : Haute disponibilité multi-zones »Objectif : répartir 6 replicas équitablement entre 3 zones.
apiVersion: apps/v1kind: Deploymentmetadata: name: web-haspec: replicas: 6 selector: matchLabels: app: web-ha template: metadata: labels: app: web-ha spec: topologySpreadConstraints: - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: web-ha - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: app: web-ha containers: - name: web image: nginx:alpineRésultat : 2 replicas par zone, répartis sur différents nœuds dans chaque zone.
Exemple 3 : Nœuds infrastructure réservés
Section intitulée « Exemple 3 : Nœuds infrastructure réservés »Objectif : réserver des nœuds pour les DaemonSets et composants d’infrastructure.
# Tainter les nœuds infrakubectl taint nodes infra-1 infra-2 dedicated=infrastructure:NoSchedulekubectl label nodes infra-1 infra-2 node-role=infrastructure# DaemonSet monitoring avec tolerationapiVersion: apps/v1kind: DaemonSetmetadata: name: prometheus-node-exporterspec: selector: matchLabels: app: node-exporter template: metadata: labels: app: node-exporter spec: tolerations: - key: "dedicated" operator: "Equal" value: "infrastructure" effect: "NoSchedule" # Ce DaemonSet tourne sur TOUS les nœuds, y compris infra containers: - name: exporter image: prom/node-exporter8. Comment choisir le bon mécanisme ?
Section intitulée « 8. Comment choisir le bon mécanisme ? »| Objectif | Solution recommandée |
|---|---|
| Cibler un type de nœud (GPU, SSD) | nodeSelector ou nodeAffinity |
| Réserver un nœud à certains workloads | taints + tolerations (+ affinity pour cibler) |
| Rapprocher des Pods liés | podAffinity |
| Éloigner quelques Pods sensibles | podAntiAffinity |
| Répartir des replicas entre nœuds/zones | topologySpreadConstraints |
| Tolérer temporairement un nœud instable | tolerationSeconds |
9. Débugger les Pods en Pending
Section intitulée « 9. Débugger les Pods en Pending »Un Pod reste Pending quand le scheduler ne trouve pas de nœud satisfaisant toutes les contraintes.
Commandes de diagnostic
Section intitulée « Commandes de diagnostic »# Voir pourquoi un Pod est Pendingkubectl describe pod <pod-name>
# Voir les events récents (très utile)kubectl get events --sort-by=.lastTimestamp
# Voir les labels de tous les nœudskubectl get nodes --show-labels
# Voir les taints d'un nœudkubectl describe node <node-name> | grep -A5 TaintsCauses courantes
Section intitulée « Causes courantes »| Symptôme dans events | Cause probable | Solution |
|---|---|---|
FailedScheduling: 0/X nodes available | Aucun nœud ne satisfait les contraintes | Vérifier labels/taints/affinity |
node(s) didn't match Pod's node affinity/selector | Label manquant sur les nœuds | kubectl label nodes |
node(s) had taint that pod didn't tolerate | Toleration manquante | Ajouter toleration au Pod |
node(s) didn't match pod topology spread constraints | Contraintes de spread trop strictes | Réduire maxSkew ou ajouter des nœuds |
Insufficient cpu/memory | Ressources insuffisantes | Ajuster requests ou ajouter des nœuds |
Exemple de diagnostic complet
Section intitulée « Exemple de diagnostic complet »# 1. Identifier le problèmekubectl describe pod my-pending-pod | grep -A10 Events
# 2. Vérifier les nœuds disponibleskubectl get nodes -o wide
# 3. Vérifier les labelskubectl get nodes --show-labels | grep -E "accelerator|zone"
# 4. Vérifier les taintsfor node in $(kubectl get nodes -o name); do echo "=== $node ===" kubectl describe $node | grep -A3 TaintsdoneÀ retenir
Section intitulée « À retenir »| Mécanisme | Rôle | Côté |
|---|---|---|
nodeSelector | Filtre simple par label | Pod → Nœud |
nodeAffinity | Filtre avancé (préférence/contrainte) | Pod → Nœud |
podAffinity | Rapproche des Pods | Pod → Pod |
podAntiAffinity | Éloigne des Pods | Pod → Pod |
topologySpreadConstraints | Répartit les replicas | Pod → Topologie |
taints | Repousse les Pods non autorisés | Nœud |
tolerations | Autorise un Pod sur nœud tainté | Pod |
Points essentiels :
topologySpreadConstraintsest le mécanisme moderne pour la répartition — ne l’oubliez pas- Une toleration n’attire pas un Pod — elle l’autorise seulement
- Pour cibler un nœud tainté : toleration +
nodeSelector/nodeAffinity - L’inter-pod affinity peut ralentir le scheduling dans les grands clusters
- Utilisez labels standardisés (
kubernetes.io/hostname,topology.kubernetes.io/zone)