
Vous devez mettre un nœud en maintenance — mise à jour de l’OS, remplacement de disque, patching de sécurité — sans interrompre les services qui tournent dessus. Kubernetes fournit un workflow en 3 étapes : kubectl cordon empêche les nouveaux pods d’arriver, kubectl drain évacue les pods existants vers d’autres nœuds, et kubectl uncordon réactive le nœud après la maintenance. Pour un contrôle plus fin et permanent, kubectl taint restreint quels pods peuvent tourner sur quels nœuds via un système de taints et tolerations.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Mettre un nœud en maintenance proprement avec le workflow
cordon→drain→ maintenance →uncordon - Évacuer les pods d’un nœud en gérant les DaemonSets, les PodDisruptionBudgets et les volumes éphémères
- Contrôler le placement des pods avec les taints et les tolerations
- Distinguer les 3 effets de taint : NoSchedule, PreferNoSchedule, NoExecute
- Diagnostiquer les problèmes de drain bloqué ou de pods non planifiés
Quelle commande pour quel besoin ?
Section intitulée « Quelle commande pour quel besoin ? »| Besoin | Commande | Effet |
|---|---|---|
| Empêcher les nouveaux pods d’arriver sur un nœud | kubectl cordon | Marque le nœud SchedulingDisabled |
| Réautoriser les pods sur un nœud | kubectl uncordon | Retire le marquage SchedulingDisabled |
| Évacuer tous les pods d’un nœud (maintenance) | kubectl drain | Cordon + éviction de tous les pods |
| Restreindre durablement quels pods tournent sur un nœud | kubectl taint | Seuls les pods avec la toleration correspondante sont acceptés |
Workflow de maintenance complet
Section intitulée « Workflow de maintenance complet »Voici la procédure standard pour sortir un nœud du cluster, faire la maintenance, puis le réintégrer :
-
Vérifiez l’état actuel du nœud
Fenêtre de terminal kubectl get nodesNAME STATUS ROLES AGE VERSIONnode-1 Ready <none> 90d v1.30.2node-2 Ready <none> 90d v1.30.2node-3 Ready <none> 90d v1.30.2 -
Identifiez les pods qui tournent sur le nœud
Fenêtre de terminal kubectl get pods --all-namespaces -o wide --field-selector spec.nodeName=node-1Cela vous donne la liste complète de ce qui sera évacué.
-
Empêchez les nouveaux pods d’arriver (cordon)
Fenêtre de terminal kubectl cordon node-1node/node-1 cordonedVérification — le nœud affiche
SchedulingDisabled:Fenêtre de terminal kubectl get node node-1NAME STATUS ROLES AGE VERSIONnode-1 Ready,SchedulingDisabled <none> 90d v1.30.2 -
Évacuez les pods existants (drain)
Fenêtre de terminal kubectl drain node-1 --ignore-daemonsets --delete-emptydir-datanode/node-1 already cordonedevicting pod default/api-gateway-7d4b8cevicting pod default/worker-9f2a1epod/api-gateway-7d4b8c evictedpod/worker-9f2a1e evictednode/node-1 drained -
Faites votre maintenance (mise à jour OS, reboot, remplacement matériel…)
-
Réactivez le nœud (uncordon)
Fenêtre de terminal kubectl uncordon node-1node/node-1 uncordoned -
Vérifiez que le nœud est de nouveau prêt
Fenêtre de terminal kubectl get node node-1NAME STATUS ROLES AGE VERSIONnode-1 Ready <none> 90d v1.30.2
kubectl cordon et uncordon : le verrou de planification
Section intitulée « kubectl cordon et uncordon : le verrou de planification »cordon — bloquer les nouveaux pods
Section intitulée « cordon — bloquer les nouveaux pods »kubectl cordon <nom-du-noeud>Le nœud passe en SchedulingDisabled. Les pods déjà en cours d’exécution ne sont pas affectés — seuls les nouveaux pods ne seront plus planifiés dessus.
uncordon — débloquer
Section intitulée « uncordon — débloquer »kubectl uncordon <nom-du-noeud>Le nœud redevient éligible à la planification. Les pods existants sur d’autres nœuds ne sont pas automatiquement redéplacés — Kubernetes ne rééquilibre pas les pods tout seul.
kubectl drain : évacuer un nœud
Section intitulée « kubectl drain : évacuer un nœud »kubectl drain fait 2 choses : il cordon le nœud (s’il ne l’est pas déjà), puis il évacue tous les pods en demandant leur suppression. Le scheduler recrée les pods (gérés par un controller) sur d’autres nœuds.
kubectl drain <nom-du-noeud> [options]Les options indispensables
Section intitulée « Les options indispensables »En pratique, un drain nu échoue presque toujours. Voici les options que vous utiliserez systématiquement :
| Option | Pourquoi c’est nécessaire |
|---|---|
--ignore-daemonsets | Les DaemonSets sont liés au nœud — ils ne peuvent pas être évacués. Sans cette option, drain refuse de continuer |
--delete-emptydir-data | Les pods avec des volumes emptyDir perdront leurs données. Cette option confirme que vous acceptez cette perte |
--force | Supprime les pods non gérés par un controller (pods créés manuellement). Ces pods ne seront pas recréés |
--grace-period=<seconds> | Durée laissée aux pods pour s’arrêter proprement (override du terminationGracePeriodSeconds du pod) |
--timeout=<duration> | Durée maximale du drain. Par défaut : infini |
--pod-selector=<selector> | N’évacue que les pods matchant le selector |
--disable-eviction | Utilise delete au lieu de l’API d’éviction (ignore les PodDisruptionBudgets) |
La commande de drain standard en production
Section intitulée « La commande de drain standard en production »kubectl drain node-1 \ --ignore-daemonsets \ --delete-emptydir-data \ --timeout=300sPodDisruptionBudget : le garde-fou du drain
Section intitulée « PodDisruptionBudget : le garde-fou du drain »Un PodDisruptionBudget (PDB) empêche le drain de supprimer trop de pods d’un même service en même temps. Si votre PDB exige minAvailable: 2 et qu’il ne reste que 2 pods disponibles, le drain bloquera tant qu’un pod n’est pas remonté sur un autre nœud.
# Voir les PDB en placekubectl get pdb --all-namespacesNAMESPACE NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGEprod api-gateway-pdb 2 N/A 1 30dSi le drain bloque à cause d’un PDB :
# Vérifier quel PDB bloquekubectl get pdb -n prod -o wide
# Si c'est un drain d'urgence (perte de nœud), contourner le PDBkubectl drain node-1 --ignore-daemonsets --delete-emptydir-data --disable-evictionDrainer plusieurs nœuds séquentiellement
Section intitulée « Drainer plusieurs nœuds séquentiellement »Pour un rolling upgrade de tous les nœuds :
for node in $(kubectl get nodes -l role=worker -o name); do echo "=== Draining $node ===" kubectl drain "$node" --ignore-daemonsets --delete-emptydir-data --timeout=300s
echo "=== Maintenance sur $node ===" # Votre commande de maintenance ici (SSH, reboot, etc.)
echo "=== Uncordoning $node ===" kubectl uncordon "$node"
echo "=== Attente que le nœud soit Ready ===" kubectl wait --for=condition=Ready "$node" --timeout=120s echo ""donekubectl taint : contrôler le placement des pods
Section intitulée « kubectl taint : contrôler le placement des pods »Les taints sont un mécanisme permanent pour contrôler quels pods peuvent tourner sur quels nœuds. Contrairement à cordon (qui bloque tout), un taint est sélectif : seuls les pods avec la toleration correspondante sont acceptés.
Comment ça fonctionne
Section intitulée « Comment ça fonctionne »Le principe est simple :
- Vous posez un taint sur un nœud (« ce nœud est spécial »)
- Seuls les pods qui déclarent une toleration correspondante peuvent y être planifiés
- Les pods sans toleration sont rejetés (ou évincés, selon l’effet)
# Ajouter un taintkubectl taint nodes <noeud> <clé>=<valeur>:<effet>
# Supprimer un taintkubectl taint nodes <noeud> <clé>-Les 3 effets de taint
Section intitulée « Les 3 effets de taint »| Effet | Comportement |
|---|---|
| NoSchedule | Les nouveaux pods sans toleration ne sont pas planifiés. Les pods déjà en place ne sont pas touchés |
| PreferNoSchedule | Le scheduler évite ce nœud si possible, mais y planifie un pod s’il n’y a pas d’autre choix |
| NoExecute | Les pods existants sans toleration sont évacués. Les nouveaux sans toleration sont refusés |
Cas d’usage courants
Section intitulée « Cas d’usage courants »Réserver des nœuds GPU pour les workloads ML
Section intitulée « Réserver des nœuds GPU pour les workloads ML »# Taint sur les nœuds GPUkubectl taint nodes gpu-node-1 gpu=true:NoSchedulekubectl taint nodes gpu-node-2 gpu=true:NoScheduleSeuls les pods avec cette toleration seront acceptés :
apiVersion: v1kind: Podmetadata: name: training-jobspec: tolerations: - key: "gpu" operator: "Equal" value: "true" effect: "NoSchedule" containers: - name: trainer image: ml-training:latestNœud dédié à un environnement (prod uniquement)
Section intitulée « Nœud dédié à un environnement (prod uniquement) »kubectl taint nodes prod-node-1 env=production:NoScheduletolerations: - key: "env" operator: "Equal" value: "production" effect: "NoSchedule"Nœud en observation (potentiellement instable)
Section intitulée « Nœud en observation (potentiellement instable) »Utilisez PreferNoSchedule pour que le scheduler évite ce nœud sans évacuer les pods existants :
kubectl taint nodes node-3 stability=suspect:PreferNoScheduleÉvacuer les pods immédiatement (NoExecute)
Section intitulée « Évacuer les pods immédiatement (NoExecute) »kubectl taint nodes node-1 maintenance=urgent:NoExecuteTous les pods sans toleration pour maintenance=urgent:NoExecute sont immédiatement évacués. C’est plus radical que drain car c’est instantané.
Les pods avec toleration peuvent définir un délai d’expulsion :
tolerations: - key: "maintenance" operator: "Equal" value: "urgent" effect: "NoExecute" tolerationSeconds: 300 # Le pod a 5 min avant d'être évacuéTolerations : la syntaxe complète
Section intitulée « Tolerations : la syntaxe complète »| Champ | Valeur | Description |
|---|---|---|
key | "gpu", "env"… | Doit correspondre à la clé du taint |
operator | Equal (défaut) | Clé + valeur doivent matcher |
operator | Exists | Seule la clé doit exister (valeur ignorée) |
value | "true", "production"… | Doit correspondre à la valeur du taint (si operator=Equal) |
effect | NoSchedule, PreferNoSchedule, NoExecute | Doit correspondre à l’effet du taint. Si vide : matche tous les effets |
tolerationSeconds | 300 | Durée avant évacuation (uniquement avec NoExecute) |
Toleration universelle — accepte tous les taints d’un nœud :
tolerations: - operator: "Exists"Voir et supprimer les taints
Section intitulée « Voir et supprimer les taints »# Voir les taints d'un nœudkubectl describe node node-1 | grep -A5 Taints
# Ou en JSONkubectl get node node-1 -o jsonpath='{.spec.taints}' | python3 -m json.tool
# Supprimer un taint par sa clékubectl taint nodes node-1 gpu-
# Supprimer un taint par clé + effetkubectl taint nodes node-1 gpu:NoSchedule-Taints automatiques posés par Kubernetes
Section intitulée « Taints automatiques posés par Kubernetes »Kubernetes pose automatiquement des taints sur les nœuds dans certaines situations. Les connaître aide au diagnostic :
| Taint automatique | Posé quand | Effet |
|---|---|---|
node.kubernetes.io/not-ready | Le nœud n’est pas Ready | NoExecute |
node.kubernetes.io/unreachable | Le nœud n’est plus joignable | NoExecute |
node.kubernetes.io/disk-pressure | Espace disque insuffisant | NoSchedule |
node.kubernetes.io/memory-pressure | Mémoire insuffisante | NoSchedule |
node.kubernetes.io/pid-pressure | Trop de processus | NoSchedule |
node.kubernetes.io/unschedulable | Nœud cordoné | NoSchedule |
Bonnes pratiques
Section intitulée « Bonnes pratiques »Maintenance de nœuds
Section intitulée « Maintenance de nœuds »- Suivez toujours le workflow
cordon→drain→ maintenance →uncordon - Mettez un
--timeoutsur le drain pour éviter un blocage infini dans vos scripts - Vérifiez les PodDisruptionBudgets avant de drainer — un PDB trop strict peut bloquer tout le drain
- Drainez les nœuds un par un dans un rolling upgrade pour maintenir la capacité du cluster
- Après
uncordon, vérifiez que le nœud estReadyaveckubectl wait --for=condition=Ready
Taints et tolerations
Section intitulée « Taints et tolerations »-
Utilisez les taints pour les cas permanents : nœuds GPU, nœuds dédiés par environnement, nœuds spot
-
Documentez vos conventions de taints dans un document partagé — les taints sans documentation deviennent des mystères
-
Préférez
PreferNoSchedulequand vous voulez orienter le placement sans être strict -
Testez vos tolerations en dry-run avant de les appliquer :
Fenêtre de terminal kubectl run test-toleration --image=busybox --dry-run=client -o yaml -- sleep 3600# Ajoutez la toleration dans le YAML généré, puis apply
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
drain bloque indéfiniment | Un PDB empêche l’éviction | Vérifiez kubectl get pdb -A. Augmentez le nombre de réplicas ou le maxUnavailable du PDB |
drain échoue : cannot delete DaemonSet-managed pod | DaemonSets non ignorés | Ajoutez --ignore-daemonsets |
drain échoue : cannot delete pod with local data | Pods avec emptyDir | Ajoutez --delete-emptydir-data (les données seront perdues) |
drain échoue : pod not managed by a controller | Pods créés manuellement (sans Deployment) | Ajoutez --force (le pod ne sera pas recréé) |
Pod non planifié : node(s) had taints that the pod didn't tolerate | Le nœud a un taint, le pod n’a pas la toleration | Ajoutez la toleration dans le manifest du pod |
| Pod évacué d’un nœud sans explication | Un taint NoExecute a été ajouté au nœud | Vérifiez avec kubectl describe node NOEUD puis cherchez la section Taints |
Taints not-ready ou unreachable apparaissent | Le nœud a un problème (réseau, kubelet…) | Vérifiez avec kubectl describe node NOEUD — conditions et events |
Pods ne reviennent pas après uncordon | Le scheduler ne rééquilibre pas les pods existants | Redémarrez les Deployments avec kubectl rollout restart deploy/NOM |
taint supprimé mais pods toujours rejetés | Plusieurs taints sur le nœud | Listez les taints avec kubectl get node NOEUD -o jsonpath='{.spec.taints}' — supprimez tous les taints concernés |
À retenir
Section intitulée « À retenir »kubectl cordonempêche les nouveaux pods d’arriver sur un nœud, mais ne touche pas les pods existants.kubectl drainévacue les pods du nœud (et fait un cordon automatique). Options quasi obligatoires :--ignore-daemonsetset--delete-emptydir-data.kubectl uncordonréactive le nœud — mais les pods ne reviennent pas automatiquement (le scheduler ne rééquilibre pas).- Les PodDisruptionBudgets protègent vos services pendant un drain — ne les contournez qu’en urgence avec
--disable-eviction. - Les taints sont des restrictions permanentes sur les nœuds — 3 effets :
NoSchedule(bloque les nouveaux),PreferNoSchedule(évite si possible),NoExecute(évacue les existants). - Un pod doit déclarer une toleration correspondante pour être accepté sur un nœud tainté.
- Kubernetes pose automatiquement des taints quand un nœud est en difficulté (not-ready, disk-pressure, memory-pressure…).