
Le scheduler Kubernetes place les Pods sur les nœuds disponibles selon des règles précises. Par défaut, il équilibre la charge automatiquement. Mais en production, vous devez souvent contrôler ce placement : isoler des workloads, garantir la haute disponibilité, ou exploiter du matériel spécifique (GPU, SSD).
Vue d’ensemble des mécanismes
Section intitulée « Vue d’ensemble des mécanismes »| Mécanisme | Direction | Usage principal |
|---|---|---|
nodeSelector | Pod → Node | Placement simple par labels |
nodeAffinity | Pod → Node | Placement avancé avec expressions |
podAffinity | Pod → Pod | Co-localisation de Pods |
podAntiAffinity | Pod ↔ Pod | Séparation de Pods |
taints/tolerations | Node → Pod | Exclusion de nœuds sauf exception |
topologySpreadConstraints | Pod ↔ Topology | Répartition équilibrée |
# Voir les labels et taints d'un nœudkubectl describe node <node-name> | grep -A10 Labelskubectl describe node <node-name> | grep -A5 Taints
# Lister les nœuds avec leurs labelskubectl get nodes --show-labelsnodeSelector — Placement simple
Section intitulée « nodeSelector — Placement simple »Le nodeSelector est le mécanisme le plus simple : il place le Pod uniquement sur des nœuds ayant tous les labels spécifiés.
Labelliser un nœud
Section intitulée « Labelliser un nœud »# Ajouter un labelkubectl label node worker-1 disktype=ssd
# Vérifierkubectl get nodes -l disktype=ssd
# Supprimer un labelkubectl label node worker-1 disktype-Utiliser nodeSelector dans un Pod
Section intitulée « Utiliser nodeSelector dans un Pod »apiVersion: v1kind: Podmetadata: name: ssd-podspec: nodeSelector: disktype: ssd containers: - name: app image: nginx:1.25nodeAffinity — Placement avancé
Section intitulée « nodeAffinity — Placement avancé »nodeAffinity offre plus de flexibilité que nodeSelector avec des expressions logiques (In, NotIn, Exists, DoesNotExist, Gt, Lt).
Types d’affinité
Section intitulée « Types d’affinité »| Type | Comportement |
|---|---|
requiredDuringSchedulingIgnoredDuringExecution | Obligatoire — le Pod ne démarre pas si aucun nœud ne matche |
preferredDuringSchedulingIgnoredDuringExecution | Préféré — le scheduler essaie, mais place ailleurs si nécessaire |
Exemple : requiredDuringScheduling
Section intitulée « Exemple : requiredDuringScheduling »apiVersion: v1kind: Podmetadata: name: affinity-requiredspec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: topology.kubernetes.io/zone operator: In values: - eu-west-1a - eu-west-1b containers: - name: app image: nginx:1.25Le Pod ne démarrera que sur un nœud dans la zone eu-west-1a ou eu-west-1b.
Exemple : preferredDuringScheduling
Section intitulée « Exemple : preferredDuringScheduling »apiVersion: v1kind: Podmetadata: name: affinity-preferredspec: affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 80 preference: matchExpressions: - key: disktype operator: In values: - ssd - weight: 20 preference: matchExpressions: - key: disktype operator: In values: - hdd containers: - name: app image: nginx:1.25Le scheduler préfère les nœuds SSD (poids 80) mais accepte les HDD (poids 20) si nécessaire.
Opérateurs disponibles
Section intitulée « Opérateurs disponibles »| Opérateur | Description | Exemple |
|---|---|---|
In | Valeur dans la liste | zone In [a, b] |
NotIn | Valeur hors de la liste | env NotIn [prod] |
Exists | La clé existe (valeur ignorée) | gpu Exists |
DoesNotExist | La clé n’existe pas | spot DoesNotExist |
Gt | Valeur supérieure (numérique) | cores Gt 4 |
Lt | Valeur inférieure (numérique) | memory Lt 32 |
podAffinity et podAntiAffinity
Section intitulée « podAffinity et podAntiAffinity »Ces mécanismes définissent des règles de placement entre Pods, pas entre Pod et Node.
podAffinity — Co-localisation
Section intitulée « podAffinity — Co-localisation »Placer un Pod sur le même nœud (ou dans la même zone) qu’un autre Pod :
apiVersion: v1kind: Podmetadata: name: cache-clientspec: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: redis topologyKey: kubernetes.io/hostname containers: - name: client image: busybox:1.36 command: ['sh', '-c', 'sleep 3600']Ce Pod sera placé sur le même nœud (topologyKey: kubernetes.io/hostname) qu’un Pod ayant le label app=redis.
podAntiAffinity — Séparation
Section intitulée « podAntiAffinity — Séparation »Placer un Pod sur un nœud différent des autres réplicas (haute disponibilité) :
apiVersion: apps/v1kind: Deploymentmetadata: name: webspec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: web topologyKey: kubernetes.io/hostname containers: - name: nginx image: nginx:1.25Chaque réplica sera placé sur un nœud différent. Si vous n’avez que 2 nœuds, le 3ᵉ Pod restera Pending.
Taints et Tolerations
Section intitulée « Taints et Tolerations »Les taints sont appliqués aux nœuds pour repousser les Pods. Les tolerations permettent aux Pods de tolérer ces taints.
Appliquer un taint
Section intitulée « Appliquer un taint »# Syntaxe : kubectl taint nodes <node> <key>=<value>:<effect>kubectl taint nodes worker-1 dedicated=database:NoSchedule
# Vérifierkubectl describe node worker-1 | grep Taints
# Supprimer un taint (ajout du tiret final)kubectl taint nodes worker-1 dedicated=database:NoSchedule-Effects disponibles
Section intitulée « Effects disponibles »| Effect | Comportement |
|---|---|
NoSchedule | Nouveaux Pods sans toleration ne sont pas schedulés |
PreferNoSchedule | Le scheduler évite ce nœud mais l’utilise si nécessaire |
NoExecute | Pods existants sans toleration sont évincés |
Ajouter une toleration
Section intitulée « Ajouter une toleration »apiVersion: v1kind: Podmetadata: name: db-podspec: tolerations: - key: "dedicated" operator: "Equal" value: "database" effect: "NoSchedule" containers: - name: postgres image: postgres:15Toleration universelle
Section intitulée « Toleration universelle »Pour tolérer tous les taints d’un nœud (utile pour DaemonSets) :
tolerations:- operator: "Exists"Cas d’usage courants
Section intitulée « Cas d’usage courants »| Taint | Usage |
|---|---|
node-role.kubernetes.io/control-plane:NoSchedule | Protège le control plane |
dedicated=gpu:NoSchedule | Réserve des nœuds GPU |
maintenance=true:NoExecute | Évince les Pods avant maintenance |
topologySpreadConstraints — Répartition équilibrée
Section intitulée « topologySpreadConstraints — Répartition équilibrée »Ce mécanisme répartit les Pods de manière équilibrée sur une topologie (zones, nœuds).
Exemple : répartition sur les zones
Section intitulée « Exemple : répartition sur les zones »apiVersion: apps/v1kind: Deploymentmetadata: name: balanced-appspec: replicas: 6 selector: matchLabels: app: balanced template: metadata: labels: app: balanced spec: topologySpreadConstraints: - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: app: balanced containers: - name: app image: nginx:1.25| Paramètre | Description |
|---|---|
maxSkew | Différence maximale de Pods entre topologies (1 = équilibré) |
topologyKey | Label définissant la topologie (zone, nœud, rack) |
whenUnsatisfiable | DoNotSchedule (strict) ou ScheduleAnyway (best-effort) |
labelSelector | Pods concernés par la contrainte |
Résultat avec 3 zones et 6 réplicas
Section intitulée « Résultat avec 3 zones et 6 réplicas »| Zone | Pods |
|---|---|
| eu-west-1a | 2 |
| eu-west-1b | 2 |
| eu-west-1c | 2 |
Priorité et Preemption
Section intitulée « Priorité et Preemption »Kubernetes peut évincer des Pods de basse priorité pour faire de la place à des Pods de haute priorité.
Créer une PriorityClass
Section intitulée « Créer une PriorityClass »apiVersion: scheduling.k8s.io/v1kind: PriorityClassmetadata: name: high-priorityvalue: 1000000globalDefault: falsepreemptionPolicy: PreemptLowerPrioritydescription: "Pour les workloads critiques"Utiliser une PriorityClass
Section intitulée « Utiliser une PriorityClass »apiVersion: v1kind: Podmetadata: name: critical-podspec: priorityClassName: high-priority containers: - name: app image: nginx:1.25Exercices pratiques
Section intitulée « Exercices pratiques »Exercice 1 : nodeSelector (2 min)
Section intitulée « Exercice 1 : nodeSelector (2 min) »Labellisez un nœud avec tier=frontend et créez un Pod qui ne s’exécute que sur ce nœud.
Solution
# Labelliserkubectl label node worker-1 tier=frontend
# Podkubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: frontend-podspec: nodeSelector: tier: frontend containers: - name: nginx image: nginx:1.25EOF
# Vérifierkubectl get pod frontend-pod -o wideExercice 2 : Taint + Toleration (4 min)
Section intitulée « Exercice 2 : Taint + Toleration (4 min) »- Appliquez un taint
workload=ml:NoSchedulesur un nœud - Créez un Pod sans toleration (doit rester Pending)
- Créez un Pod avec toleration (doit démarrer)
Solution
# Taintkubectl taint nodes worker-1 workload=ml:NoSchedule
# Pod sans tolerationkubectl run no-toleration --image=nginx -o yaml --dry-run=client | \ kubectl apply -f -kubectl get pod no-toleration# Pending (si worker-1 est le seul nœud disponible)
# Pod avec tolerationkubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: ml-podspec: tolerations: - key: "workload" operator: "Equal" value: "ml" effect: "NoSchedule" containers: - name: app image: nginx:1.25EOF
# Nettoyagekubectl taint nodes worker-1 workload=ml:NoSchedule-Exercice 3 : podAntiAffinity HA (5 min)
Section intitulée « Exercice 3 : podAntiAffinity HA (5 min) »Créez un Deployment de 3 réplicas avec une anti-affinité garantissant qu’aucun Pod ne partage le même nœud.
Solution
kubectl apply -f - <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata: name: ha-appspec: replicas: 3 selector: matchLabels: app: ha-app template: metadata: labels: app: ha-app spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: ha-app topologyKey: kubernetes.io/hostname containers: - name: nginx image: nginx:1.25EOF
# Vérifier la distributionkubectl get pods -l app=ha-app -o wideDebugging du scheduling
Section intitulée « Debugging du scheduling »Quand un Pod reste Pending, le scheduler indique pourquoi dans les events :
# Events du Podkubectl describe pod <pod-name> | grep -A10 Events
# Messages courants# 0/3 nodes are available: 1 node(s) had untolerated taint, 2 node(s) didn't match Pod's node affinity# 0/3 nodes are available: 3 Insufficient cpu# 0/3 nodes are available: 3 node(s) didn't match pod anti-affinity rulesChecklist debugging
Section intitulée « Checklist debugging »-
Vérifier les events du Pod
Fenêtre de terminal kubectl describe pod <name> | tail -20 -
Vérifier les ressources disponibles
Fenêtre de terminal kubectl describe nodes | grep -A5 "Allocated resources" -
Vérifier les taints des nœuds
Fenêtre de terminal kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints -
Vérifier les labels des nœuds
Fenêtre de terminal kubectl get nodes --show-labels | tr ',' '\n'
À retenir
Section intitulée « À retenir »- nodeSelector : placement simple, tous les labels doivent matcher
- nodeAffinity : placement avancé avec expressions (In, NotIn, Exists, Gt, Lt)
- podAffinity/AntiAffinity : placement relatif entre Pods (co-localisation ou séparation)
- Taints repoussent les Pods, tolerations permettent aux Pods de passer
- topologySpreadConstraints : répartition équilibrée sur zones/nœuds
- PriorityClass : permet la preemption des Pods de basse priorité
- Un Pod Pending →
kubectl describe podpour voir la raison