
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 ? »Ces quatre commandes agissent toutes sur les nœuds, mais elles ne se remplacent pas. Les trois premières servent une opération ponctuelle de maintenance et se défont aussitôt après ; kubectl taint pose au contraire une contrainte durable, qui survit aux redémarrages du nœud tant que personne ne la retire. Le tableau donne l'effet exact de chacune, à lire avant de choisir.
| 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. Elle suppose que le cluster dispose de la capacité restante pour absorber les pods évacués : sur un cluster à trois nœuds saturés, le drain se bloquera faute de place. Les sorties affichées viennent d'un cluster à trois nœuds en v1.30.2, les vôtres différeront sur les noms et les âges mais pas sur la structure.
-
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 »Ces deux commandes forment une paire : elles basculent le champ spec.unschedulable du nœud, rien de plus. Aucun pod n'est déplacé, aucune donnée n'est touchée, l'opération est instantanée et réversible. C'est le geste le plus sûr du guide, et celui par lequel commencer toute intervention sur un nœud.
cordon, bloquer les nouveaux pods
Section intitulée « cordon, bloquer les nouveaux pods »La commande prend le nom exact du nœud tel que renvoyé par kubectl get nodes.
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 »C'est l'opération inverse, à lancer une fois la maintenance terminée et le nœud revenu en Ready.
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.
Le nom du nœud est le seul argument obligatoire, mais la commande sans option échoue dès qu'un DaemonSet tourne sur le nœud, ce qui est le cas de tous les clusters réels.
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 »Voici la combinaison à retenir. Le --timeout=300s est le seul ajout par rapport au minimum vital : sans lui, un drain bloqué par un PodDisruptionBudget attend indéfiniment et fige le script de maintenance. Notez l'absence volontaire de --force, on veut être averti si un pod n'est géré par aucun controller.
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, traitez les nœuds un par un : la boucle ci-dessous ne passe au suivant qu'une fois le précédent revenu en Ready, grâce au kubectl wait final qui bloque jusqu'à la condition ou l'expiration du délai. Le sélecteur -l role=worker évite d'attraper les nœuds de control plane.
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 mécanisme repose sur une correspondance vérifiée par le scheduler à chaque tentative de placement. Le taint vit sur le nœud, la toleration vit dans le manifest du pod, et rien ne se passe tant que les deux ne se correspondent pas :
- 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)
Un taint se compose de trois parties : une clé, une valeur facultative et un effet obligatoire. Le tiret final de la seconde forme est ce qui distingue une suppression d'un ajout.
# 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 »L'effet décide de ce qui arrive aux pods, et la différence porte surtout sur les pods déjà en cours d'exécution. NoSchedule et PreferNoSchedule ne touchent que les placements futurs, NoExecute évacue immédiatement l'existant. C'est le seul des trois qui provoque une interruption de service s'il est posé sans précaution.
| 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 »Voici quatre situations réelles, classées du besoin le plus permanent au plus urgent. Elles illustrent chacune un effet différent, ce qui aide à choisir : réservation de matériel coûteux, cloisonnement par environnement, mise en observation d'un nœud suspect, et évacuation immédiate.
Réserver des nœuds GPU pour les workloads ML
Section intitulée « Réserver des nœuds GPU pour les workloads ML »Les nœuds GPU coûtent cher et rien n'empêche par défaut un pod quelconque de s'y installer. Le taint NoSchedule les rend inaccessibles à tout ce qui ne le tolère pas explicitement.
# 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:1.4.2Nœud dédié à un environnement (prod uniquement)
Section intitulée « Nœud dédié à un environnement (prod uniquement) »Même principe pour empêcher des pods de test ou de recette d'atterrir sur du matériel réservé à la production. Attention, ce taint isole le placement mais ne constitue pas une frontière de sécurité : il n'empêche personne d'ajouter la toleration à son manifest.
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) »À réserver aux situations où vous n'avez pas le temps d'un drain propre, matériel qui lâche ou incident de sécurité. Contrairement à drain, cet effet ne respecte aucun PodDisruptionBudget et ne laisse pas de délai d'arrêt gracieux négocié.
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 »Une toleration se déclare sous spec.tolerations dans le manifest du pod. Deux comportements par défaut méritent d'être connus car ils élargissent la correspondance sans qu'on le demande : un operator omis vaut Equal, et un effect laissé vide accepte les trois effets du taint correspondant.
| 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 »Un nœud peut porter plusieurs taints simultanément, y compris des taints posés par Kubernetes lui-même. Commencez donc toujours par les lister avant d'en retirer un, sinon vous corrigerez un symptôme sans débloquer le placement.
# 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 »Les règles ci-dessous viennent des erreurs qui coûtent le plus cher en exploitation : un drain lancé sans filet dans un script automatisé, et un taint posé un jour de crise que plus personne ne sait interpréter six mois après. Elles sont séparées en deux blocs, car maintenance et placement n'ont ni la même temporalité ni les mêmes conséquences.
Maintenance de nœuds
Section intitulée « Maintenance de nœuds »Ces cinq réflexes valent pour toute intervention sur un nœud en production, du patch mensuel à la mise à jour de version du cluster.
- 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 »Un taint est une décision d'architecture, pas un réglage d'exploitation : il change durablement où les pods peuvent tourner et se répercute sur chaque manifest de l'équipe.
-
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 »Les pannes se répartissent en deux familles : un drain qui refuse d'aller au bout, et un pod qui ne trouve plus de nœud. Dans les deux cas, le message d'erreur exact est la clé du diagnostic, c'est pourquoi la première colonne reprend les chaînes renvoyées par kubectl. Cherchez la vôtre plutôt que de partir des symptômes.
| 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…).