Aller au contenu
Conteneurs & Orchestration medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Scheduling avancé : Affinity, Taints, Tolerations et Topology Spread

18 min de lecture

logo kubernetes

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

  • Cibler des nœuds spécifiques avec nodeSelector et nodeAffinity
  • 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
MécanismeObjectifAgit sur
nodeNameForcer un nœud précis (bypass scheduler)Nœud
nodeSelectorFiltrer par labels (simple)Nœud
nodeAffinityFiltrer par labels (avancé, préférence/contrainte)Nœud
podAffinityRapprocher des Pods liésPods
podAntiAffinityÉloigner des PodsPods
topologySpreadConstraintsRépartir les replicas entre domainesTopologie
taints / tolerationsProtéger/réserver des nœudsNœud

Kubernetes définit des labels standardisés pour la topologie. Préférez-les aux labels custom quand ils existent :

LabelDescription
kubernetes.io/hostnameNom du nœud
topology.kubernetes.io/zoneZone de disponibilité
topology.kubernetes.io/regionRégion
node.kubernetes.io/instance-typeType d’instance (cloud)
Fenêtre de terminal
# Voir les labels d'un nœud
kubectl get nodes --show-labels

Le champ nodeName lie directement un Pod à un nœud, sans passer par le scheduler.

apiVersion: v1
kind: Pod
metadata:
name: pod-on-specific-node
spec:
containers:
- name: my-container
image: my-image
nodeName: my-node-1

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

nodeSelector est la méthode la plus simple pour contraindre un Pod à des nœuds ayant un label spécifique.

Fenêtre de terminal
kubectl label nodes node-gpu accelerator=nvidia-gpu
apiVersion: v1
kind: Pod
metadata:
name: pod-gpu
spec:
containers:
- name: my-container
image: my-image
nodeSelector:
accelerator: nvidia-gpu
  • Seuls les nœuds ayant accelerator=nvidia-gpu peuvent accueillir ce Pod
  • Si aucun nœud ne correspond, le Pod reste en Pending
  • Limitation : égalités strictes uniquement (key=value)

nodeAffinity offre plus de flexibilité que nodeSelector avec deux modes :

ModeComportement
requiredDuringSchedulingIgnoredDuringExecutionObligatoire — le Pod ne sera pas programmé si aucun nœud ne correspond
preferredDuringSchedulingIgnoredDuringExecutionPréférence — Kubernetes essaie de respecter la règle, mais peut la contourner
apiVersion: v1
kind: Pod
metadata:
name: pod-gpu-required
spec:
containers:
- name: my-container
image: my-image
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: accelerator
operator: In
values:
- nvidia-gpu
- amd-gpu

Le Pod ne sera programmé que sur un nœud avec accelerator=nvidia-gpu ou accelerator=amd-gpu.

apiVersion: v1
kind: Pod
metadata:
name: pod-gpu-preferred
spec:
containers:
- name: my-container
image: my-image
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: accelerator
operator: In
values:
- nvidia-gpu

Kubernetes préférera un nœud GPU, mais programmera le Pod ailleurs si aucun n’est disponible.

OpérateurDescription
InLa valeur est dans la liste
NotInLa valeur n’est pas dans la liste
ExistsLa clé existe (peu importe la valeur)
DoesNotExistLa clé n’existe pas
GtValeur supérieure à (numérique)
LtValeur 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.

Utile pour réduire la latence entre services liés (ex : application + cache Redis).

apiVersion: v1
kind: Pod
metadata:
name: web-app
labels:
app: web
spec:
containers:
- name: web-container
image: my-web-image
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: kubernetes.io/hostname

Ce Pod sera programmé sur le même nœud qu’un Pod ayant app=redis.

Utile pour éviter les SPOF — répartir les replicas d’un même service.

apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
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-image

Kubernetes empêchera plusieurs Pods app=web de tourner sur le même nœud.

topologyKeyEffet
kubernetes.io/hostnameRépartition par nœud
topology.kubernetes.io/zoneRépartition par zone
topology.kubernetes.io/regionRé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é.

BesoinpodAntiAffinitytopologySpreadConstraints
”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
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
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-image
ParamètreDescription
maxSkewÉcart maximum toléré entre domaines (1 = équilibré)
topologyKeyLabel définissant les domaines (kubernetes.io/hostname, topology.kubernetes.io/zone)
whenUnsatisfiableDoNotSchedule (strict) ou ScheduleAnyway (best-effort)
labelSelectorSélectionne les Pods concernés par le spread
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: web

6. 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
Fenêtre de terminal
kubectl taint nodes <node-name> <key>=<value>:<effect>
EffetComportement
NoScheduleBloque les nouveaux Pods sans toleration
PreferNoScheduleÉvite les nouveaux Pods sans toleration (best-effort)
NoExecuteBloque + expulse les Pods existants sans toleration
Fenêtre de terminal
# Tainter le nœud GPU
kubectl taint nodes node-gpu accelerator=nvidia-gpu:NoSchedule

Tous les Pods standards seront bloqués sur ce nœud.

apiVersion: v1
kind: Pod
metadata:
name: pod-gpu
spec:
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-gpu
OpérateurDescription
EqualLa clé et la valeur doivent correspondre
ExistsLa 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: 300

Le Pod restera 5 minutes sur un nœud devenu not-ready avant d’être évincé. Utile pour tolérer des perturbations réseau temporaires.

TaintDescription
node.kubernetes.io/not-readyNœud pas prêt
node.kubernetes.io/unreachableNœud injoignable
node.kubernetes.io/disk-pressurePression disque
node.kubernetes.io/memory-pressurePression mémoire
node.kubernetes.io/unschedulableNœud en cordon
Fenêtre de terminal
kubectl taint nodes <node-name> <key>:<effect>-
# Exemple :
kubectl taint nodes node-gpu accelerator:NoSchedule-

Objectif : réserver des nœuds GPU aux workloads ML/AI.

  1. Labelliser et tainter les nœuds GPU

    Fenêtre de terminal
    kubectl label nodes node-gpu-1 node-gpu-2 accelerator=nvidia-gpu
    kubectl taint nodes node-gpu-1 node-gpu-2 accelerator=nvidia-gpu:NoSchedule
  2. Créer le Pod ML avec toleration + nodeAffinity

    apiVersion: v1
    kind: Pod
    metadata:
    name: ml-training
    spec:
    containers:
    - name: training
    image: tensorflow/tensorflow:latest-gpu
    tolerations:
    - key: "accelerator"
    operator: "Equal"
    value: "nvidia-gpu"
    effect: "NoSchedule"
    affinity:
    nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchExpressions:
    - key: accelerator
    operator: In
    values:
    - nvidia-gpu

Objectif : répartir 6 replicas équitablement entre 3 zones.

apiVersion: apps/v1
kind: Deployment
metadata:
name: web-ha
spec:
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:alpine

Résultat : 2 replicas par zone, répartis sur différents nœuds dans chaque zone.

Objectif : réserver des nœuds pour les DaemonSets et composants d’infrastructure.

Fenêtre de terminal
# Tainter les nœuds infra
kubectl taint nodes infra-1 infra-2 dedicated=infrastructure:NoSchedule
kubectl label nodes infra-1 infra-2 node-role=infrastructure
# DaemonSet monitoring avec toleration
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: prometheus-node-exporter
spec:
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-exporter
ObjectifSolution recommandée
Cibler un type de nœud (GPU, SSD)nodeSelector ou nodeAffinity
Réserver un nœud à certains workloadstaints + tolerations (+ affinity pour cibler)
Rapprocher des Pods liéspodAffinity
Éloigner quelques Pods sensiblespodAntiAffinity
Répartir des replicas entre nœuds/zonestopologySpreadConstraints
Tolérer temporairement un nœud instabletolerationSeconds

Un Pod reste Pending quand le scheduler ne trouve pas de nœud satisfaisant toutes les contraintes.

Fenêtre de terminal
# Voir pourquoi un Pod est Pending
kubectl 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œuds
kubectl get nodes --show-labels
# Voir les taints d'un nœud
kubectl describe node <node-name> | grep -A5 Taints
Symptôme dans eventsCause probableSolution
FailedScheduling: 0/X nodes availableAucun nœud ne satisfait les contraintesVérifier labels/taints/affinity
node(s) didn't match Pod's node affinity/selectorLabel manquant sur les nœudskubectl label nodes
node(s) had taint that pod didn't tolerateToleration manquanteAjouter toleration au Pod
node(s) didn't match pod topology spread constraintsContraintes de spread trop strictesRéduire maxSkew ou ajouter des nœuds
Insufficient cpu/memoryRessources insuffisantesAjuster requests ou ajouter des nœuds
Fenêtre de terminal
# 1. Identifier le problème
kubectl describe pod my-pending-pod | grep -A10 Events
# 2. Vérifier les nœuds disponibles
kubectl get nodes -o wide
# 3. Vérifier les labels
kubectl get nodes --show-labels | grep -E "accelerator|zone"
# 4. Vérifier les taints
for node in $(kubectl get nodes -o name); do
echo "=== $node ==="
kubectl describe $node | grep -A3 Taints
done
MécanismeRôleCôté
nodeSelectorFiltre simple par labelPod → Nœud
nodeAffinityFiltre avancé (préférence/contrainte)Pod → Nœud
podAffinityRapproche des PodsPod → Pod
podAntiAffinityÉloigne des PodsPod → Pod
topologySpreadConstraintsRépartit les replicasPod → Topologie
taintsRepousse les Pods non autorisésNœud
tolerationsAutorise un Pod sur nœud taintéPod

Points essentiels :

  1. topologySpreadConstraints est le mécanisme moderne pour la répartition — ne l’oubliez pas
  2. Une toleration n’attire pas un Pod — elle l’autorise seulement
  3. Pour cibler un nœud tainté : toleration + nodeSelector/nodeAffinity
  4. L’inter-pod affinity peut ralentir le scheduling dans les grands clusters
  5. Utilisez labels standardisés (kubernetes.io/hostname, topology.kubernetes.io/zone)

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn