Contactez-nous

Drainer les noeuds pour la maintenance (kubectl drain)

Apprenez à utiliser la commande `kubectl drain` pour évacuer en toute sécurité les Pods d'un noeud Kubernetes avant une opération de maintenance, minimisant ainsi les interruptions.

Préparer un noeud pour une intervention : le rôle du drainage

Lorsqu'il est nécessaire d'intervenir sur un noeud worker d'un cluster Kubernetes – que ce soit pour une mise à jour du système d'exploitation, un remplacement matériel, une mise à niveau du Kubelet, ou simplement pour retirer le noeud du cluster – il est primordial de le faire sans perturber les applications qui y sont exécutées. Tenter d'arrêter ou de redémarrer un noeud sans précaution entraînerait une interruption brutale des Pods présents, potentiellement une perte de données et une indisponibilité des services.

La commande kubectl drain est l'outil standard de Kubernetes conçu spécifiquement pour gérer cette situation. Son objectif est de vider "gracieusement" un noeud de ses Pods applicatifs avant de le rendre indisponible. Cela signifie qu'elle demande au cluster de déplacer les charges de travail vers d'autres noeuds disponibles, tout en respectant les règles de terminaison définies pour chaque Pod.

Le drainage est donc une étape essentielle avant toute opération de maintenance planifiée sur un noeud. Il assure une transition en douceur pour les applications et maintient la disponibilité globale du service hébergé par le cluster.

Comprendre la commande `kubectl drain` et ses options

La syntaxe de base de la commande est simple :

kubectl drain  [options]

Cependant, plusieurs options sont cruciales pour contrôler son comportement et gérer différents scénarios. Voici les plus importantes :

  • --ignore-daemonsets : Par défaut, drain échoue s'il trouve des Pods gérés par un DaemonSet sur le noeud. En effet, ces Pods sont censés tourner sur (presque) tous les noeuds et ne peuvent pas être recréés ailleurs par le contrôleur DaemonSet lui-même. Utiliser cette option indique à drain d'ignorer ces Pods et de procéder à l'expulsion des autres. C'est une option très fréquemment utilisée.
  • --delete-local-data (remplace --delete-emptydir-data dans les versions plus récentes) : Si des Pods utilisent des volumes emptyDir, leur contenu est perdu lors de leur suppression. Par défaut, drain échoue pour éviter une perte de données potentielle si de tels Pods sont présents. Cette option force l'expulsion même si des Pods utilisent emptyDir ou d'autres types de stockage local éphémère. A utiliser avec prudence, en sachant que les données de ces volumes seront perdues.
  • --force : Cette option permet de continuer l'expulsion même si certains Pods ne sont pas gérés par un contrôleur (comme un ReplicaSet, Deployment, StatefulSet, Job, etc.). Sans cette option, drain échoue pour ces Pods "orphelins" car ils ne seraient pas recréés ailleurs après leur suppression. L'utilisation de --force implique que ces Pods seront supprimés définitivement. A utiliser avec une extrême prudence.
  • --grace-period= : Permet de spécifier la période de grâce (en secondes) accordée aux Pods pour terminer proprement avant d'être forcés à s'arrêter (SIGKILL). Si la valeur est négative, la période de grâce par défaut spécifiée dans la définition du Pod est utilisée.
  • --timeout= : Définit une durée maximale pour l'ensemble de l'opération de drainage (par exemple, 5m0s pour 5 minutes). Si le drainage n'est pas terminé dans ce délai, l'opération échoue. Utile pour éviter des blocages indéfinis.
  • --dry-run=client ou --dry-run=server : Permet de simuler l'opération sans réellement marquer le noeud comme non planifiable ni expulser les Pods. Très utile pour vérifier quels Pods seraient affectés et si des blocages potentiels existent (comme des Pods non gérés ou utilisant emptyDir sans les options correspondantes).

Le processus de drainage détaillé : Cordon et Eviction

Lorsqu'on exécute kubectl drain , plusieurs actions séquentielles se produisent :

  1. Marquage comme non planifiable (Cordon) : La première action est de marquer le noeud comme "unschedulable". Cela équivaut à exécuter kubectl cordon . Cette étape empêche le Scheduler Kubernetes d'assigner de nouveaux Pods à ce noeud pendant que le processus de drainage est en cours. Les Pods existants continuent de fonctionner.
  2. Identification et Eviction des Pods : Ensuite, drain liste tous les Pods s'exécutant sur le noeud cible, en excluant potentiellement ceux gérés par des DaemonSets si --ignore-daemonsets est utilisé. Pour chaque Pod identifié (qui n'est pas un DaemonSet si ignoré), drain tente de l'expulser en utilisant l'API d'Eviction de Kubernetes.
  3. Respect des PodDisruptionBudgets (PDB) : L'API d'Eviction est cruciale car elle respecte les PodDisruptionBudgets (PDB) définis pour les applications. Un PDB spécifie le nombre minimum de Pods d'une application qui doivent rester disponibles pendant une interruption volontaire (comme un drainage). Si l'expulsion d'un Pod viole un PDB, l'API d'Eviction refusera l'expulsion, et drain attendra ou échouera (selon le timeout et la configuration du PDB).
  4. Terminaison Gracieuse : Si l'expulsion est autorisée, le Pod reçoit un signal de terminaison (SIGTERM par défaut) et dispose de sa période de grâce (terminationGracePeriodSeconds) pour s'arrêter proprement (sauvegarder l'état, fermer les connexions, etc.). Si le Pod ne s'arrête pas dans ce délai, il reçoit un signal SIGKILL.
  5. Attente et Vérification : La commande drain attend que tous les Pods ciblés soient effectivement terminés et supprimés du noeud avant de se terminer avec succès.

Après la maintenance : réintégrer le noeud

Une fois les opérations de maintenance terminées sur le noeud (mises à jour appliquées, matériel remplacé, etc.) et que celui-ci est de nouveau prêt à accueillir des charges de travail, il faut le réintégrer dans le pool de ressources actives du cluster. Cela se fait simplement en retirant le marquage "unschedulable" appliqué par drain (ou par cordon).

La commande pour rendre un noeud de nouveau disponible pour la planification de Pods est :

kubectl uncordon 

Après l'exécution de cette commande, le Scheduler Kubernetes considérera de nouveau ce noeud comme une cible potentielle pour les nouveaux Pods ou les Pods qui doivent être replanifiés.

Considérations importantes et bonnes pratiques

Capacité du Cluster : Avant de drainer un noeud, assurez-vous toujours qu'il y a suffisamment de capacité (CPU, mémoire, etc.) disponible sur les autres noeuds du cluster pour accueillir les Pods qui seront expulsés. Sinon, ces Pods resteront à l'état "Pending" jusqu'à ce que des ressources se libèrent ou que de nouveaux noeuds soient ajoutés.

PodDisruptionBudgets (PDBs) : Configurez des PDBs pour vos applications critiques. C'est la meilleure façon de garantir la haute disponibilité pendant les opérations de maintenance planifiées comme le drainage. Sans PDB, drain pourrait potentiellement arrêter toutes les instances d'une application simultanément si elles se trouvent sur le même noeud.

Drainage Progressif : Dans les grands clusters ou lors de la maintenance de plusieurs noeuds, il est préférable de drainer les noeuds un par un ou par petits groupes, en vérifiant l'état du cluster et des applications entre chaque opération. Cela limite l'impact sur la capacité globale et permet de détecter plus facilement les problèmes.

Utiliser --dry-run : Prenez l'habitude d'utiliser --dry-run=client ou --dry-run=server avant d'exécuter la commande drain réelle. Cela vous donne un aperçu précieux des conséquences potentielles et des options que vous pourriez avoir besoin d'ajouter (comme --ignore-daemonsets ou --delete-local-data).