Introduction
L'éviction sous pression de nœud est le mécanisme de Kubernetes pour récupérer les ressources d'un nœud lorsqu'elles deviennent insuffisantes. Lorsque la mémoire, le disque ou les inodes d'un nœud franchissent des seuils configurés, le kubelet commence à terminer des pods pour éviter que le nœud ne devienne instable. Une mauvaise configuration peut évincer des charges de travail critiques, provoquer des défaillances en cascade ou laisser le cluster silencieusement dégradé. Ce guide passe en revue les erreurs courantes commises par les opérateurs lors de la configuration des seuils d'éviction et explique comment les corriger avec des étapes pratiques et vérifiables.
Nous verrons comment inspecter votre configuration d'éviction actuelle, définir des seuils sûrs, valider les modifications, diagnostiquer les événements d'éviction et revenir en arrière en cas de problème. Chaque recommandation inclut des commandes, des sorties attendues ou des scénarios d'exemple afin que vous puissiez suivre dans votre propre cluster. L'objectif est la sécurité opérationnelle : observer d'abord, modifier une chose à la fois et vérifier avant de continuer.
Inventaire de version et d'environnement
Avant de modifier quoi que ce soit, sachez exactement ce que vous exécutez. Le comportement d'éviction sous pression de nœud dépend de la version de Kubernetes, de la configuration du kubelet et de la capacité du nœud. Commencez par collecter un inventaire en lecture seule.
Vérifiez la version du cluster et du kubelet :
kubectl version --short
kubectl get nodes -o wide
Exemple de sortie :
Client Version: v1.29.1
Server Version: v1.29.1
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
node-1 Ready control-plane 10d v1.29.1 10.0.0.10 <none> Ubuntu 22.04.3 LTS 5.15.0-91-generic containerd://1.7.11
node-2 Ready <none> 10d v1.29.1 10.0.0.11 <none> Ubuntu 22.04.3 LTS 5.15.0-91-generic containerd://1.7.11
Pour voir la configuration d'éviction actuelle, vérifiez les options de démarrage du kubelet ou son fichier de configuration. Sur un nœud basé sur systemd, exécutez :
systemctl status kubelet
ps aux | grep kubelet
Le kubelet peut utiliser un fichier de configuration spécifié par --config. Recherchez ce chemin, puis inspectez-le :
sudo cat /var/lib/kubelet/config.yaml
Extrait d'une configuration par défaut :
evictionHard:
memory.available: "100Mi"
nodefs.available: "10%"
imagefs.available: "15%"
nodefs.inodesFree: "5%"
evictionSoft:
memory.available: "200Mi"
nodefs.available: "20%"
evictionSoftGracePeriod:
memory.available: "1m30s"
nodefs.available: "1m30s"
evictionPressureTransitionPeriod: "5m"
Si le fichier de configuration est absent ou s'il n'y a pas de section d'éviction, le kubelet utilise les valeurs par défaut intégrées. Ces valeurs par défaut sont :
evictionHard:memory.available<100Mi,nodefs.available<10%,imagefs.available<15%,nodefs.inodesFree<5%evictionSoft: aucuneevictionPressureTransitionPeriod:5m
Confirmez la capacité réelle du nœud avec :
kubectl describe node node-1 | grep -A5 "Capacity:"
Cela vous donne la mémoire allouable, le stockage éphémère et les inodes afin que vous puissiez raisonner sur les seuils par rapport aux tailles réelles.
Vous disposez maintenant d'une base de référence. Notez les seuils actuels, la capacité et la version. Si quelque chose tourne mal plus tard, vous pourrez comparer avec cet instantané.
Chemin de configuration sûr
Modifier les seuils d'éviction est une opération au niveau du nœud qui affecte l'ordonnancement et la stabilité des pods. Suivez un chemin prudent et incrémental avec un plan de retour en arrière clair.
Étape 1 : Choisir le bon type de seuil
Kubernetes prend en charge deux politiques d'éviction : dure et douce.
- Éviction dure : Lorsqu'une ressource passe sous le seuil, le kubelet évince immédiatement les pods. Aucun délai de grâce.
- Éviction douce : Lorsqu'une ressource passe sous le seuil, le kubelet attend un délai de grâce avant d'évincer. Si la ressource remonte au-dessus du seuil pendant le délai de grâce, aucune éviction n'a lieu.
Erreur : Utiliser l'éviction douce avec un long délai de grâce peut permettre à un nœud de sombrer dans la pression mémoire avant que les pods ne soient évincés. Utiliser l'éviction dure avec un seuil très élevé peut provoquer des tempêtes d'éviction soudaines.
Recommandation : Commencez par l'éviction dure pour la mémoire car la pression mémoire est immédiate et peut faire planter le nœud. Utilisez l'éviction douce pour la pression disque, où les pics temporaires sont courants.
Étape 2 : Définir des seuils basés sur des mesures de référence
Ne copiez pas les seuils d'un article de blog. Ils doivent être liés à la taille de votre nœud et au profil de charge de travail. Pour un nœud de 16 Go de RAM, un seuil memory.available de 100Mi ne représente que 0,6 % de la mémoire totale, ce qui peut être trop bas. Un meilleur point de départ pourrait être 500Mi ou 5 % de la mémoire allouable.
Pour mesurer l'utilisation actuelle de la mémoire sous charge :
kubectl top node node-1
Exemple de sortie :
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
node-1 1200m 30% 11Gi 68%
Si le nœud fonctionne régulièrement à 75 % de mémoire, définir memory.available à 10 % du total donne une marge avant l'éviction. Pour un nœud de 16 Gio, 10 % représentent environ 1,6 Gio. La chaîne de seuil serait :
evictionHard:
memory.available: "1.6Gi"
Pour le disque, surveillez nodefs.available et nodefs.inodesFree. Utilisez df -h et df -i sur le nœud :
df -h /
df -i /
Définissez des seuils qui laissent suffisamment de marge pour les journaux de conteneurs et les pulls d'images. Une erreur courante est de définir nodefs.available trop bas, ce qui provoque des évictions lorsque le runtime de conteneurs a besoin d'espace pour de nouvelles images.
Étape 3 : Appliquer la configuration un nœud à la fois
Ne modifiez jamais la configuration du kubelet sur tous les nœuds simultanément. Si le seuil est trop agressif, vous pourriez évincer des pods sur tout le cluster en même temps.
Pour modifier la configuration du kubelet sur un nœud :
- Modifiez le fichier de configuration du kubelet (par exemple,
/var/lib/kubelet/config.yaml). - Redémarrez le kubelet :
sudo systemctl restart kubelet
- Vérifiez que le kubelet fonctionne et que le nœud est toujours Ready :
systemctl status kubelet
kubectl get node node-1
Si le nœud devient NotReady ou si des pods commencent à être évincés de manière inattendue, restaurez le fichier et redémarrez à nouveau le kubelet.
Étape 4 : Utiliser un DaemonSet ou un pool de nœuds géré pour la cohérence
Dans Kubernetes géré (EKS, GKE, AKS), vous ne pouvez souvent pas modifier directement la configuration du kubelet. À la place, vous modifiez les arguments du kubelet du pool de nœuds ou utilisez un modèle de lancement personnalisé. Par exemple, sur EKS avec un groupe de nœuds autogéré, vous pouvez passer des arguments supplémentaires au kubelet dans le script user data :
--eviction-hard=memory.available<500Mi,nodefs.available<10%
Pour GKE, vous pouvez configurer les configurations système du nœud via la spécification kubelet-config du pool de nœuds.
Testez toujours sur un seul pool de nœuds ou un nœud canari avant de déployer en production.
Vérification et diagnostic
Après avoir modifié les paramètres d'éviction, vous devez vérifier que le kubelet a bien pris en compte la nouvelle configuration et que l'éviction se comporte comme prévu. Vous devez également diagnostiquer les événements d'éviction déjà en cours.
Vérifier la configuration active du kubelet
Vous pouvez confirmer les seuils d'éviction actifs en consultant les journaux du kubelet au démarrage ou en utilisant l'endpoint /configz du kubelet (s'il est activé). Une méthode plus simple consiste à vérifier les conditions d'état du nœud :
kubectl describe node node-1 | grep -A10 "Conditions:"
Recherchez des conditions comme MemoryPressure, DiskPressure, PIDPressure. Si l'une d'elles est True, le nœud est actuellement sous pression et des évictions peuvent être imminentes.
Vous pouvez également inspecter les journaux du kubelet pour les messages liés à l'éviction :
journalctl -u kubelet --since "10 minutes ago" | grep -i evict
Exemple de ligne de journal :
I0202 10:15:23.123456 1234 eviction_manager.go:340] "Eviction manager: attempting to reclaim" resourceName="memory.available"
Cela vous indique que le kubelet tente activement de récupérer de la mémoire.
Simuler la pression en toute sécurité
Pour tester vos seuils sans attendre une vraie crise, vous pouvez délibérément créer une pression sur un nœud en utilisant un pod qui consomme des ressources. Pour une pression mémoire :
apiVersion: v1
kind: Pod
metadata:
name: memory-eater
spec:
containers:
- name: memory-eater
image: polinux/stress
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "2G", "--vm-hang", "0"]
resources:
requests:
memory: "2G"
cpu: "100m"
limits:
memory: "2G"
cpu: "200m"
Planifiez ce pod sur le nœud cible en utilisant nodeSelector ou nodeName. Le pod demande 2G de mémoire, ce qui ajoute de la pression. Avec un seuil de, par exemple, 500Mi de mémoire disponible, vous devriez voir le kubelet commencer à évincer des pods. Surveillez les événements :
kubectl get events --field-selector involvedObject.name=memory-eater --watch
Si l'éviction ne se produit pas comme prévu, vérifiez les seuils réels dans la configuration du kubelet et l'utilisation actuelle de la mémoire du nœud. Il se peut que le nœud ait suffisamment de marge.
Diagnostiquer les raisons et l'ordre des évictions
Lorsqu'un pod est évincé, son motif de statut sera Evicted. L'événement inclura un message comme :
The node was low on resource: memory. Threshold quantity: 500Mi, available: 420Mi.
Utilisez ces messages pour comprendre quelle ressource a déclenché l'éviction et de combien le nœud était en dessous du seuil.
Pour voir quels pods ont été récemment évincés :
kubectl get pods --all-namespaces --field-selector=status.phase=Failed -o wide
Inspectez ensuite chaque pod échoué :
kubectl describe pod <evicted-pod> -n <namespace> | grep -A10 "Events:"
L'événement montrera la raison de l'éviction et l'horodatage.
Vérifiez également la priorité des pods. Le kubelet évince les pods dans un ordre spécifique : les pods BestEffort d'abord, puis les pods Burstable qui utilisent plus que leurs demandes, puis les pods Guaranteed (avec quelques réserves). Dans les versions récentes, la priorité des pods et la classe QoS comptent toutes deux. Une erreur courante est d'avoir des pods système critiques sans classe de priorité, ce qui en fait des candidats à l'éviction. Définissez toujours des classes de priorité appropriées pour les charges de travail critiques.
Modes de défaillance et récupération
Même avec une planification minutieuse, les mauvaises configurations d'éviction peuvent causer de graves problèmes. Voici les modes de défaillance les plus courants et comment s'en remettre.
Défaillance 1 : Tempêtes d'éviction
Symptôme : De nombreux pods sont évincés soudainement sur un ou plusieurs nœuds, provoquant une interruption de service.
Cause : Seuil d'éviction dure trop élevé (par exemple, memory.available: 2Gi sur un nœud de 8Gi) provoquant des évictions immédiates dès que la mémoire descend sous 25 %. Ou éviction douce avec un délai de grâce très court.
Récupération :
- Augmentez immédiatement le seuil ou passez à l'éviction douce avec un délai de grâce plus long.
- Redémarrez le kubelet sur les nœuds affectés.
- Surveillez le réordonnancement des pods et la pression sur les nœuds.
Exemple de configuration révisée :
evictionSoft:
memory.available: "500Mi"
evictionSoftGracePeriod:
memory.available: "2m"
evictionHard:
memory.available: "200Mi"
Cela donne aux pods un délai de grâce de 2 minutes avant l'éviction dure à 200Mi disponibles.
Défaillance 2 : Le nœud devient NotReady en raison de l'épuisement des ressources
Symptôme : Le nœud passe à NotReady, le kubelet peut planter, les pods du nœud deviennent inaccessibles.
Cause : Seuils d'éviction trop bas, de sorte que le nœud manque de mémoire ou de disque avant que le kubelet n'évince quoi que ce soit. Le noyau peut tuer par OOM des processus critiques, y compris le kubelet lui-même.
Récupération :
- Libérez manuellement des ressources : supprimez les pods inutiles, nettoyez les anciennes images de conteneurs ou redémarrez le nœud.
- Une fois le nœud Ready, révisez les seuils à la hausse pour donner plus de marge.
- Envisagez de définir des ressources réservées au système (
--system-reserved,--kube-reserved) afin que le kubelet tienne compte de la surcharge système.
Exemple d'options du kubelet :
--system-reserved=cpu=500m,memory=1Gi
--kube-reserved=cpu=500m,memory=1Gi
Défaillance 3 : Éviction de pods système critiques
Symptôme : CoreDNS, kube-proxy ou d'autres pods système sont évincés, ce qui perturbe le réseau du cluster ou le DNS.
Cause : Aucune classe de priorité définie pour les pods système, ils sont donc traités comme BestEffort ou Burstable et évincés tôt.
Récupération :
- Assurez-vous que les pods critiques ont des classes de priorité élevées. Par exemple,
system-cluster-criticalousystem-node-critical. - Si vous utilisez un cluster autogéré, ajoutez
priorityClassName: system-node-criticalaux manifestes des pods de kube-system. - S'ils sont évincés, les pods devraient être reprogrammés rapidement une fois la pression sur le nœud retombée. Sinon, supprimez manuellement les pods pour forcer leur recréation.
Défaillance 4 : Pression disque due aux pulls d'images
Symptôme : Les nœuds montrent la condition DiskPressure True même si les données d'application sont petites. Des évictions peuvent se produire lors de nouveaux déploiements.
Cause : Seuil imagefs.available trop élevé ou nodefs.available prend en compte les couches d'images. Les images de conteneurs remplissent le système de fichiers.
Récupération :
- Nettoyez régulièrement les images inutilisées avec
crictl rmi --pruneou une tâche cron. - Augmentez le seuil
imagefs.availablepour permettre des pulls d'images temporaires. - Utilisez un système de fichiers dédié pour les images de conteneurs si possible (par exemple, une partition séparée
/var/lib/containerd).
Exemple de commande de nettoyage :
sudo crictl rmi --prune
Pièges courants et comment les éviter
Les pièges suivants reviennent dans les clusters réels. Chacun est décrit avec pourquoi il se produit, comment l'éviter et comment s'en remettre si vous y tombez.
Piège 1 : Ignorer les différences de capacité des nœuds
Pourquoi cela arrive : Les équipes copient la même configuration d'éviction sur tous les nœuds, en supposant une taille de ressource uniforme. Mais les nœuds d'un cluster hétérogène peuvent avoir des tailles de mémoire ou de disque différentes. Un seuil de disque de 10 % sur un nœud de 50 Go est de 5 Go, mais sur un nœud de 200 Go, il est de 20 Go. Le comportement diffère.
Comment l'éviter : Définissez des seuils en pourcentage pour le disque et en valeurs absolues pour la mémoire, ou adaptez les seuils de mémoire absolus à chaque groupe de nœuds. Utilisez des taints ou des pools de nœuds pour regrouper les nœuds de capacité similaire.
Récupération : Si les évictions ne se produisent que sur certains nœuds, vérifiez leur capacité et ajustez le seuil en conséquence.
Piège 2 : Délais de grâce d'éviction douce trop agressifs
Pourquoi cela arrive : Un délai de grâce court (par exemple, 10 secondes) ne laisse presque pas de temps aux pods pour terminer les requêtes en cours ou nettoyer. Cela entraîne des terminaisons brutales et des erreurs client.
Comment l'éviter : Définissez des délais de grâce d'au moins 1 à 2 minutes pour les charges de travail non critiques. Pour les charges de travail avec état qui nécessitent un nettoyage plus long, envisagez des hooks preStop et des délais de grâce plus longs.
Récupération : Allongez le délai de grâce et redémarrez le kubelet.
Piège 3 : Ne pas surveiller les événements d'éviction
Pourquoi cela arrive : Les événements d'éviction sont éphémères et faciles à manquer. Sans surveillance, vous ne remarquez que lorsque les services échouent.
Comment l'éviter : Mettez en place des alertes sur les conditions de nœud MemoryPressure, DiskPressure, PIDPressure, et sur les événements d'éviction de pods. Utilisez des outils comme Prometheus avec kube-state-metrics et node exporter.
Exemple de règle d'alerte Prometheus :
groups:
- name: node-pressure
rules:
- alert: NodeUnderMemoryPressure
expr: kube_node_status_condition{condition="MemoryPressure",status="true"} == 1
for: 5m
labels:
severity: warning
annotations:
summary: "Node {{ $labels.node }} is under memory pressure"
Récupération : Une fois les alertes en place, vous pouvez réagir avant que les évictions ne se multiplient.
Piège 4 : Confondre la sémantique de evictionHard et evictionSoft
Pourquoi cela arrive : Les opérateurs définissent à la fois des seuils durs et doux pour le même signal sans comprendre que les seuils durs priment sur les doux. Si evictionHard est inférieur à evictionSoft, le seuil dur se déclenchera en premier et évincera immédiatement, rendant le seuil doux sans objet.
Comment l'éviter : Utilisez les seuils durs comme dernier recours et les seuils doux comme avertissement précoce avec grâce. Assurez-vous que le seuil doux est inférieur (c'est-à-dire se déclenche plus tard) au seuil dur. Par exemple, doux à 500Mi disponibles, dur à 200Mi disponibles.
Récupération : Corrigez l'ordre et redémarrez le kubelet.
Piège 5 : Ne pas tenir compte des budgets de perturbation de pods (PDB)
Pourquoi cela arrive : Même avec des seuils d'éviction correctement définis, les perturbations volontaires dues aux évictions peuvent violer les PDB, ce qui amène l'API d'éviction à bloquer les évictions ou le kubelet à tuer les pods quand même, entraînant des temps d'arrêt inattendus.
Comment l'éviter : Définissez des PDB pour les applications critiques et comprenez que les évictions sous pression de nœud sont involontaires et ignorent les PDB. Utilisez des classes de priorité pour protéger les pods critiques de l'éviction.
Récupération : Si les évictions ont causé des temps d'arrêt, vérifiez les PDB et les classes de priorité. Ajustez-les pour mieux protéger les charges de travail.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle avant et après avoir apporté des modifications à la configuration d'éviction. Chaque élément comprend une commande ou une étape de vérification concrète, le rôle responsable et la fréquence de révision.
| # | Action | Commande / Vérification | Propriétaire | Fréquence de révision |
|---|---|---|---|---|
| 1 | Enregistrer la capacité de référence du nœud et les seuils d'éviction actuels | kubectl describe node <node> | grep -A5 Capacity, cat /var/lib/kubelet/config.yaml | Priya Shah, ingénieure plateforme | À chaque changement, plus trimestriel |
| 2 | Vérifier les conditions de pression actuelles du nœud | kubectl describe node <node> | grep -A5 Conditions | Alex Chen, SRE | Hebdomadaire |
| 3 | Valider la syntaxe de la configuration du kubelet avant redémarrage | kubelet --config=/var/lib/kubelet/config.yaml --validate (si pris en charge) ou kubelet --help | Priya Shah | Avant chaque changement |
| 4 | Redémarrer le kubelet sur un nœud canari | sudo systemctl restart kubelet | Jordan Lee, opérateur de nœuds | Pendant la fenêtre de changement |
| 5 | Confirmer que le nœud revient Ready et qu'il n'y a pas d'évictions inattendues | kubectl get nodes, kubectl get events --all-namespaces | grep Evicted | Jordan Lee | Immédiatement après le redémarrage |
| 6 | Surveiller les métriques d'éviction pendant 24 heures | Prometheus : kube_pod_evictions_total ou graphiques de mémoire de node exporter | Maria Garcia, responsable surveillance | Après le changement, puis quotidiennement pendant une semaine |
| 7 | Revenir en arrière si les seuils provoquent des tempêtes d'éviction ou une instabilité du nœud | Restaurer le fichier de configuration précédent, sudo systemctl restart kubelet | Jordan Lee | Au besoin, documenté dans le runbook |
| 8 | Mettre à jour la documentation avec les nouveaux seuils et leur justification | Mettre à jour le runbook dans le wiki | Priya Shah | À chaque changement réussi |
Chaque propriétaire est une personne, pas un groupe. La fréquence de révision garantit que la configuration reste appropriée à mesure que les charges de travail évoluent.
Conclusion
L'éviction sous pression de nœud est un filet de sécurité, mais seulement si vous la configurez judicieusement. Les erreurs discutées ici — seuils trop agressifs, ignorance des différences de capacité des nœuds, surveillance faible et mauvaise compréhension de l'éviction dure par rapport à l'éviction douce — sont toutes évitables avec une planification et une vérification minutieuses. En inventoriant votre environnement, en apportant de petits changements, en vérifiant avec des signaux réels et en ayant un plan de retour en arrière, vous pouvez prévenir les pannes liées à l'éviction.
Commencez par une action à faible risque : vérifiez votre configuration d'éviction actuelle du kubelet sur un nœud et comparez-la à l'utilisation réelle de la mémoire et du disque du nœud. Si les seuils semblent trop agressifs ou trop laxistes, ajustez-les en suivant le chemin sûr décrit ci-dessus, et mettez en place une alerte simple pour les conditions de pression du nœud. Ensuite, étendez progressivement à tous les nœuds, en documentant chaque étape.
Une stratégie d'éviction robuste n'est pas statique. Révisez les seuils trimestriellement ou chaque fois que vous changez de taille de nœuds, ajoutez de nouveaux types de charges de travail ou observez de nouveaux modèles de pression. Avec les bonnes pratiques, vous pouvez garder votre cluster stable et vos services opérationnels même sous contrainte de ressources.