Aller au contenu
Conteneurs & Orchestration medium

kubectl cordon, drain et taint : maintenance et planification des nœuds Kubernetes

16 min de lecture

logo kubernetes

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.

  • Mettre un nœud en maintenance proprement avec le workflow cordondrain → 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
BesoinCommandeEffet
Empêcher les nouveaux pods d’arriver sur un nœudkubectl cordonMarque le nœud SchedulingDisabled
Réautoriser les pods sur un nœudkubectl uncordonRetire le marquage SchedulingDisabled
Évacuer tous les pods d’un nœud (maintenance)kubectl drainCordon + éviction de tous les pods
Restreindre durablement quels pods tournent sur un nœudkubectl taintSeuls les pods avec la toleration correspondante sont acceptés

Voici la procédure standard pour sortir un nœud du cluster, faire la maintenance, puis le réintégrer :

  1. Vérifiez l’état actuel du nœud

    Fenêtre de terminal
    kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    node-1 Ready <none> 90d v1.30.2
    node-2 Ready <none> 90d v1.30.2
    node-3 Ready <none> 90d v1.30.2
  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-1

    Cela vous donne la liste complète de ce qui sera évacué.

  3. Empêchez les nouveaux pods d’arriver (cordon)

    Fenêtre de terminal
    kubectl cordon node-1
    node/node-1 cordoned

    Vérification — le nœud affiche SchedulingDisabled :

    Fenêtre de terminal
    kubectl get node node-1
    NAME STATUS ROLES AGE VERSION
    node-1 Ready,SchedulingDisabled <none> 90d v1.30.2
  4. Évacuez les pods existants (drain)

    Fenêtre de terminal
    kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data
    node/node-1 already cordoned
    evicting pod default/api-gateway-7d4b8c
    evicting pod default/worker-9f2a1e
    pod/api-gateway-7d4b8c evicted
    pod/worker-9f2a1e evicted
    node/node-1 drained
  5. Faites votre maintenance (mise à jour OS, reboot, remplacement matériel…)

  6. Réactivez le nœud (uncordon)

    Fenêtre de terminal
    kubectl uncordon node-1
    node/node-1 uncordoned
  7. Vérifiez que le nœud est de nouveau prêt

    Fenêtre de terminal
    kubectl get node node-1
    NAME STATUS ROLES AGE VERSION
    node-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 »
Fenêtre de terminal
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.

Fenêtre de terminal
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 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.

Fenêtre de terminal
kubectl drain <nom-du-noeud> [options]

En pratique, un drain nu échoue presque toujours. Voici les options que vous utiliserez systématiquement :

OptionPourquoi c’est nécessaire
--ignore-daemonsetsLes DaemonSets sont liés au nœud — ils ne peuvent pas être évacués. Sans cette option, drain refuse de continuer
--delete-emptydir-dataLes pods avec des volumes emptyDir perdront leurs données. Cette option confirme que vous acceptez cette perte
--forceSupprime 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-evictionUtilise delete au lieu de l’API d’éviction (ignore les PodDisruptionBudgets)
Fenêtre de terminal
kubectl drain node-1 \
--ignore-daemonsets \
--delete-emptydir-data \
--timeout=300s

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.

Fenêtre de terminal
# Voir les PDB en place
kubectl get pdb --all-namespaces
NAMESPACE NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
prod api-gateway-pdb 2 N/A 1 30d

Si le drain bloque à cause d’un PDB :

Fenêtre de terminal
# Vérifier quel PDB bloque
kubectl get pdb -n prod -o wide
# Si c'est un drain d'urgence (perte de nœud), contourner le PDB
kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data --disable-eviction

Pour un rolling upgrade de tous les nœuds :

Fenêtre de terminal
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 ""
done

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.

Le principe est simple :

  1. Vous posez un taint sur un nœud (« ce nœud est spécial »)
  2. Seuls les pods qui déclarent une toleration correspondante peuvent y être planifiés
  3. Les pods sans toleration sont rejetés (ou évincés, selon l’effet)
Fenêtre de terminal
# Ajouter un taint
kubectl taint nodes <noeud> <clé>=<valeur>:<effet>
# Supprimer un taint
kubectl taint nodes <noeud> <clé>-
EffetComportement
NoScheduleLes nouveaux pods sans toleration ne sont pas planifiés. Les pods déjà en place ne sont pas touchés
PreferNoScheduleLe scheduler évite ce nœud si possible, mais y planifie un pod s’il n’y a pas d’autre choix
NoExecuteLes pods existants sans toleration sont évacués. Les nouveaux sans toleration sont refusés
Fenêtre de terminal
# Taint sur les nœuds GPU
kubectl taint nodes gpu-node-1 gpu=true:NoSchedule
kubectl taint nodes gpu-node-2 gpu=true:NoSchedule

Seuls les pods avec cette toleration seront acceptés :

apiVersion: v1
kind: Pod
metadata:
name: training-job
spec:
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
containers:
- name: trainer
image: ml-training:latest

Nœud dédié à un environnement (prod uniquement)

Section intitulée « Nœud dédié à un environnement (prod uniquement) »
Fenêtre de terminal
kubectl taint nodes prod-node-1 env=production:NoSchedule
tolerations:
- key: "env"
operator: "Equal"
value: "production"
effect: "NoSchedule"

Utilisez PreferNoSchedule pour que le scheduler évite ce nœud sans évacuer les pods existants :

Fenêtre de terminal
kubectl taint nodes node-3 stability=suspect:PreferNoSchedule
Fenêtre de terminal
kubectl taint nodes node-1 maintenance=urgent:NoExecute

Tous 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é
ChampValeurDescription
key"gpu", "env"Doit correspondre à la clé du taint
operatorEqual (défaut)Clé + valeur doivent matcher
operatorExistsSeule la clé doit exister (valeur ignorée)
value"true", "production"Doit correspondre à la valeur du taint (si operator=Equal)
effectNoSchedule, PreferNoSchedule, NoExecuteDoit correspondre à l’effet du taint. Si vide : matche tous les effets
tolerationSeconds300Durée avant évacuation (uniquement avec NoExecute)

Toleration universelle — accepte tous les taints d’un nœud :

tolerations:
- operator: "Exists"
Fenêtre de terminal
# Voir les taints d'un nœud
kubectl describe node node-1 | grep -A5 Taints
# Ou en JSON
kubectl 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é + effet
kubectl taint nodes node-1 gpu:NoSchedule-

Kubernetes pose automatiquement des taints sur les nœuds dans certaines situations. Les connaître aide au diagnostic :

Taint automatiquePosé quandEffet
node.kubernetes.io/not-readyLe nœud n’est pas ReadyNoExecute
node.kubernetes.io/unreachableLe nœud n’est plus joignableNoExecute
node.kubernetes.io/disk-pressureEspace disque insuffisantNoSchedule
node.kubernetes.io/memory-pressureMémoire insuffisanteNoSchedule
node.kubernetes.io/pid-pressureTrop de processusNoSchedule
node.kubernetes.io/unschedulableNœud cordonéNoSchedule
  • Suivez toujours le workflow cordondrain → maintenance → uncordon
  • Mettez un --timeout sur 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 est Ready avec kubectl wait --for=condition=Ready
  • 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 PreferNoSchedule quand 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
SymptômeCause probableSolution
drain bloque indéfinimentUn PDB empêche l’évictionVérifiez kubectl get pdb -A. Augmentez le nombre de réplicas ou le maxUnavailable du PDB
drain échoue : cannot delete DaemonSet-managed podDaemonSets non ignorésAjoutez --ignore-daemonsets
drain échoue : cannot delete pod with local dataPods avec emptyDirAjoutez --delete-emptydir-data (les données seront perdues)
drain échoue : pod not managed by a controllerPods 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 tolerateLe nœud a un taint, le pod n’a pas la tolerationAjoutez la toleration dans le manifest du pod
Pod évacué d’un nœud sans explicationUn taint NoExecute a été ajouté au nœudVérifiez avec kubectl describe node NOEUD puis cherchez la section Taints
Taints not-ready ou unreachable apparaissentLe 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 uncordonLe scheduler ne rééquilibre pas les pods existantsRedémarrez les Deployments avec kubectl rollout restart deploy/NOM
taint supprimé mais pods toujours rejetésPlusieurs taints sur le nœudListez les taints avec kubectl get node NOEUD -o jsonpath='{.spec.taints}' — supprimez tous les taints concernés
  • kubectl cordon empê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-daemonsets et --delete-emptydir-data.
  • kubectl uncordon ré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…).

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