Requests et Limits Kubernetes : Évitez les pièges !
Mauvaise config des requests et limits ? Ralentissements, OOMKilled, throttling… Découvrez comment optimiser vos pods Kubernetes et éviter ces erreurs !
L’issue s’ouvre déjà remplie avec le titre et l’adresse de cette page.
Vos Pods Kubernetes se font tuer avec un mystérieux message OOMKilled ? Ou ils semblent tourner au ralenti malgré un serveur bien dimensionné ? Le problème vient probablement d'une mauvaise configuration des requests et limits, les paramètres qui définissent combien de CPU et de mémoire chaque Pod peut utiliser.
Ce guide vous explique comment configurer ces paramètres pour éviter les ralentissements, les crashs et la surconsommation de ressources.
Les requests définissent la quantité minimale de ressources (CPU/mémoire) dont un conteneur a besoin pour fonctionner. Kubernetes réserve ces ressources sur le nœud où le Pod est déployé.
Exemple : si un Pod demande 500m CPU (soit 50% d'un CPU), Kubernetes s'assure qu'un nœud ayant au moins cette capacité disponible l'accueille.
Les limits définissent la quantité maximale de ressources qu'un conteneur peut consommer. Si un Pod dépasse cette limite, Kubernetes applique des restrictions :
CPU : throttling (ralentissement forcé)
Mémoire : OOMKilled, le conteneur est tué et redémarré selon la restartPolicy
Exemple : si un Pod a une limit de 1 CPU, il ne pourra jamais dépasser cette valeur, même en cas de forte demande.
Deux choses différentes se vérifient, et les confondre fait tourner en rond : ce que le Pod déclare, qui se lit dans son manifeste, et ce qu'il consomme réellement, qui demande le serveur de métriques. Le premier explique un placement, le second explique une lenteur.
Fenêtre de terminal
kubectldescribepodmon-pod|grep-A10"Limits:"
Sortie attendue :
Limits:
cpu: 500m
memory: 256Mi
Requests:
cpu: 250m
memory: 128Mi
Une surprise attend ceux qui n'ont déclaré que des limits : Kubernetes recopie
la limit dans la request au moment d'enregistrer le Pod. Un manifeste qui ne
porte que limits: {cpu: 300m, memory: 128Mi} se relit ainsi :
Le scheduler réserve donc bien 300m sur le nœud, et le Pod se retrouve en classe
Guaranteed sans que personne l'ait demandé. Relisez toujours le Pod après
création plutôt que votre manifeste :
Le schéma se lit de gauche à droite. Le request, mis en avant, est l'espace garanti par le scheduler. La zone burst est disponible si le nœud a de la capacité. La limit est le plafond absolu, au-delà, c'est le throttling CPU ou l'OOMKilled mémoire.
Le throttling CPU se produit lorsque votre conteneur atteint sa limit CPU. Kubernetes utilise le planificateur CFS (Completely Fair Scheduler) de Linux pour le ralentir.
Symptômes :
L'application devient lente et peu réactive
Des timeouts apparaissent sur les requêtes
Une baisse de performance inattendue en période de charge
Comment détecter le throttling :
Fenêtre de terminal
kubectltoppod
Deux précisions avant de la taper. D'abord, cette commande ne marche pas sur
un cluster neuf : elle interroge l'API de métriques, fournie par
metrics-server, qui n'est installé ni sur kind ni sur la plupart des
distributions minimales.
Sortie sans metrics-server
error: Metrics API not available
Ensuite, kubectl top rend des valeurs absolues, en millicores et en
octets, jamais un pourcentage. Comparez vous-même la valeur affichée à la
limits.cpu du conteneur : un Pod qui plafonne à 500m avec une limite à
500m est en throttling.
La mesure qui ne se discute pas vient de Prometheus, car elle compte le
temps réellement volé au conteneur plutôt que de l'inférer :
Contrairement au CPU, la mémoire ne se ralentit pas : on ne peut pas rendre un octet déjà alloué. Le CPU se bride, la mémoire se reprend, et la seule façon de la reprendre est de tuer le processus. C'est le noyau qui s'en charge quand le cgroup atteint sa limite, avec le message OOMKilled.
Deux précisions que les schémas simplifiés escamotent. C'est le conteneur qui est tué, pas le Pod : ce que vous observez ensuite dépend de la restartPolicy et du contrôleur. Et le déclenchement est réactif, lié à la pression mémoire du cgroup : une charge peut brièvement frôler sa limite avant que le noyau n'intervienne.
Symptômes :
Redémarrages en boucle (CrashLoopBackOff)
Pertes de données si l'application ne gère pas bien les interruptions
Instabilité du service si plusieurs Pods critiques sont affectés
Comment détecter un OOMKilled :
Le plus simple d'abord, la colonne STATUS le dit directement :
Fenêtre de terminal
kubectlgetpodmon-pod
Sortie
NAME READY STATUS RESTARTS AGE
oom 0/1 OOMKilled 0 3s
Attention au champ que vous interrogez si vous scriptez. Beaucoup d'exemples
lisent lastState, qui ne contient la raison qu'après un redémarrage. Sur
un Pod en restartPolicy: Never, il est vide et la raison vit dans state :
Fenêtre de terminal
# Le conteneur tourne encore après avoir été tué et relancé
Interrogez les deux, ou lisez simplement kubectl describe pod, qui affiche la
Reason quel que soit le cas.
Le code de sortie lève la dernière ambiguïté, et c'est lui qu'on retrouve
dans les journaux d'une chaîne de déploiement qui n'a plus accès au cluster :
Code
Raison
Ce qui s'est passé
137
OOMKilled
Le noyau a tué le processus par SIGKILL, 128 + 9
1
Error
L'application s'est arrêtée en erreur, la mémoire n'y est pour rien
Un 137 oriente vers limits.memory ou vers une fuite mémoire ; un
1 oriente vers les journaux de l'application. Confondre les deux fait
augmenter une limite mémoire qui n'était pas en cause.
Solutions :
Augmentez la limite mémoire
Surveillez la consommation mémoire pour détecter des fuites éventuelles
Utilisez Prometheus pour monitorer container_memory_working_set_bytes
Pod Pending : des requests que personne ne peut servir
Le troisième symptôme ne fait ni crasher ni ralentir : le Pod ne démarre
jamais. Des requests supérieures à ce qu'un nœud peut offrir laissent le
scheduler sans candidat, et le Pod reste en Pending indéfiniment.
Fenêtre de terminal
kubectldescribepodmon-pod
Le message d'échec nomme la ressource qui manque, nœud par nœud :
Sortie
0/2 nodes are available: 1 Insufficient cpu, 1 Insufficient memory,
1 node(s) had untolerated taint(s).
Lisez-le comme un décompte : sur 2 nœuds, aucun ne convient, et le motif
diffère d'un nœud à l'autre. Le mot Insufficient désigne toujours les
requests, jamais les limits : c'est la preuve que le placement se joue sur
ce que le Pod demande, pas sur son plafond.
Si un nœud manque de ressources, Kubernetes doit faire de la place pour les Pods les plus importants. Il utilise un mécanisme d'éviction pour supprimer les Pods non prioritaires.
L'ordre d'éviction dépend de la classe de QoS (Quality of Service) de chaque Pod.
Kubernetes attribue automatiquement une classe de QoS à chaque Pod en fonction de sa configuration de ressources.
Classe
Condition
Priorité à l'éviction
Guaranteed
requests = limits, CPU et mémoire, sur tous les conteneurs
Derniers évincés
Burstable
au moins un conteneur avec des requests, sans remplir la condition ci-dessus
Évincés après BestEffort
BestEffort
aucun conteneur ne déclare ni requests ni limits
Premiers évincés
Glissez pour voir
Les mots « tous les conteneurs » ne sont pas une précaution de rédaction, c'est
le piège de cette classification. Un Pod dont le conteneur principal est
parfaitement réglé et dont le sidecar ne déclare rien n'est pas
Guaranteed :
Il n'existe pas de valeur universellement bonne : le réglage dépend de ce que vous protégez. Un service critique veut des garanties, un traitement par lots veut de l'élasticité, un environnement de test veut surtout ne pas gaspiller.
Ces quatre lignes suffisent à décider dans la plupart des cas. Retenez surtout la colonne QoS : c'est le risque encouru (l'éviction) qui doit guider le réglage, pas une valeur recopiée d'un exemple.
Les trois symptômes ci-dessous se distinguent par ce que fait le Pod. Un Pod qui ne démarre pas renvoie aux requests et au placement ; un Pod qui redémarre renvoie à la limite mémoire ; un Pod qui ralentit renvoie à la limite CPU. Identifier lequel des trois vous avez sous les yeux fait l'essentiel du diagnostic.
Symptôme
Cause probable
Solution
Pod OOMKilled
Limit mémoire trop basse
Augmenter limits.memory
Application lente
Throttling CPU
Augmenter limits.cpu ou supprimer la limit
Pod Pending, Insufficient cpu ou Insufficient memory
Sept questions sur ce qui coûte le plus cher : la différence entre
garantie et plafond, la condition exacte de la classe Guaranteed, et ce
qui distingue le bridage CPU de l'arrêt pour dépassement mémoire.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
7 questions
5 min.
80% requis
Informations
Le chronomètre démarre au clic sur Démarrer
Questions à choix multiples, vrai/faux et réponses courtes
Vous pouvez naviguer entre les questions
Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
⏱️--:--
0 / 7
Vérification
(0/0)
--:--
0%
0Correctes
0À revoir
0:00Temps
-Niveau
Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Une limite ne se vérifie que là où le noyau l'applique. Le premier lab écrit à la main un Pod à deux conteneurs, chacun avec ses requests et ses limits, et fait lire depuis l'intérieur la mémoire réellement imposée. Le second augmente CPU et mémoire d'un Pod qui tourne par la sous-ressource resize, sans le recréer ni redémarrer son conteneur.