Publié le 24 mai 2022.
Il y a quelque temps, j'avais des Pods dans l'état « evicted » (expulsé) dans mon cluster Kubernetes sans vraiment comprendre pourquoi.
J'ai exploré le sujet, et aujourd'hui je voulais partager mon expérience avec vous, et surtout vous expliquer ce qu'était l'état « evicted » d'un Pod.
Dans ce court article, j'expliquerai pourquoi vos pods peuvent être expulsés et quel est exactement le processus d'expulsion. Nous n’allons pas rentrer dans trop de détails, mais nous couvrirons les bases.
Avant d'entrer dans l'éviction (expulsion), il est crucial de comprendre comment Kubernetes gère la priorité des pods, également appelée la qualité de service des pods (QoS Class).
Vocabulaire
Un peu de vocabulaire pour commencer ! Bon bien comprendre cet article, nous devons parler le même langage, les concepts essentiels sont présentés ci-dessous :
Demandes (Requests) et Limites
- Requests : paramètre utilisé pour le scheduling du pod. Ce paramètre est la quantité minimale de ressources dont un conteneur a besoin pour démarrer. Les requêtes ne signifient pas que la ressource est dédiée au pod.
- Limites : il s'agit de la quantité maximale d'une ressource que le nœud accordera aux conteneurs à utiliser.
Les classes de qualité de service des pods (QoS Classes).
- "Guaranteed" : pods dont les requêtes et les limites sont configurées à la fois pour les ressources mémoire et CPU.
- Chaque conteneur du pod doit avoir une limite de CPU et une request de CPU, ces valeurs doivent être identiques
- Chaque conteneur du pod doit avoir une limite de mémoire et une request de mémoire, ces valeurs doivent être identiques
- "Burstable" : pods avec au moins une request sur le CPU ou la mémoire pour au moins un de leurs conteneurs.
- "Best Effort" : Pods sans aucune request ni limite.
Qu’est-ce qu’un pod expulsé (evicted)
Maintenant que nous savons ce que sont les requests/limites et que celles-ci définissent la QoS Class des pods, nous allons étudier le processus d'expulsion.
Lorsqu'un nœud atteint sa limite de disque ou de mémoire, un flag est mis sur le nœud Kubernetes pour indiquer qu'il est sous pression. Ce flag bloque également une nouvelle allocation de pod sur ce nœud, et suite à cela, un processus d'éviction est lancé pour libérer certaines ressources afin d’alléger la charge sur le nœud.
C'est le Kubelet du nœud sous pression qui s'occupera du processus d'expulsion. Celui-ci commencera à expulser des pods jusqu'à ce que les ressources utilisées du nœud soient inférieures au seuil d'éviction. Lorsque le Kubelet expulse un pod, il met fin à tous les conteneurs du pod et passe son statut en “Failed”.
Si le pod expulsé est géré par un deployment, le deployment crée un autre pod à déployer (schedule) par Kubernetes.
Comment les ressources sont-elles libérées ?
La première chose que le Kubelet fera sera de libérer du disque en supprimant les pods qui ne sont pas en cours d'exécution et leurs images (quick win). Ensuite, si le nettoyage du disque ne suffit pas, le Kubelet lancera une éviction des pods dans un ordre défini par leur QoS Class :
- Les pods en “Best Effort” ;
- Les pods “Burstable” qui utilisent plus de ressources que la request définie sur la ressource qui met le nœud sous pression ;
- Les pods “Burstable” qui utilisent moins de ressources que la request définie sur la ressource qui met le nœud sous pression.
Quant aux pods ”Guaranteed”, ils sont (en théorie) à l’abri d’une expulsion.
Par exemple, prenons un nœud qui a des problèmes de CPU. Si un Pod a une request sur la ressource CPU et utilise la moitié de sa request CPU, il sera expulsé après un pod avec une request sur la ressource CPU mais qui utilise plus que sa requête.
Le plus important :
Vous l'aurez compris, il est impératif de définir correctement les requests et les limites sur vos pods.
Pour commencer, vous pouvez utiliser les QoS classes suivantes en fonction de la criticité de vos applications :
- Applications critiques : Guaranteed (requests = limits pour CPU + mémoire)
- Applications non-critiques qui ne tolèrent pas de pannes : Burstable (au moins une request sur le CPU ou la mémoire)
- Applications non-critiques qui tolèrent des pannes : Best Effort (ni requests ni limites)
Cas d’usage : les pods Prometheus expulsés
Il y a quelques mois, mon pod de serveur Prometheus s’est fait expulser. En jetant un coup d'œil aux événements du Pod, j’ai vu le message suivant :
Message: The node was low on resource: memory. Container prometheus-server was using 2890108Ki, which exceeds its request of 2000Mi.
Voici les requests configurées sur ce pod :
$ k describe pods prometheus-server-5c949c44f7-rc9sv | grep -iA2 Requests
Requests
cpu: 500m
memory: 2000Mi
Eh bien, il n'est pas choquant que le Pod consomme plus que sa demande de mémoire. Le problème survient quand, dans le cas où le nœud sur lequel tourne le Pod est en difficulté avec sa mémoire (ce qui est mon cas ici), alors le Pod se fait expulser assez rapidement, juste après ceux en Best Effort.
Pour aller plus loin
Si vous voulez en savoir plus sur le processus d'expulsion, je vous encourage à lire la documentation officielle de Kubernetes, qui explique plus en détail le comportement des nœuds pression.
Cette documentation couvre les signaux d'expulsion, le seuil d'expulsion, etc.