Kubernetes • Bien configurer Requests et Limits pour une gestion optimale des ressources
Lorsqu’on déploie des applications sur Kubernetes, bien configurer les ressources CPU et mémoire est essentiel pour garantir performance et stabilité.
Les paramètres Requests et Limits jouent un rôle clé dans cette gestion, mais leur mauvaise configuration peut entraîner des pods en attente, des redémarrages intempestifs ou une utilisation inefficace des ressources.
Dans cet article, Gérald vous explique comment définir ces valeurs intelligemment pour tirer le meilleur parti de votre cluster Kubernetes.
C’est quoi les Requests ?
Les requests définissent la quantité minimale garantie de ressource utilisée par un conteneur.
Lorsqu’un pod est déployé, le composant kube-scheduler va vérifier chaque nœud pour savoir si l’un d’eux possède les ressources nécessaires pour prendre en charge les Requests de ce nouveau pod, en plus de ceux déjà déployés sur ce nœud.
Si aucun nœud n’a cette capacité, le pod restera en statuts Pending.
C’est quoi les Limits ?
Comme leur nom l’indique, cela défini la limite d’utilisation de ressources par le conteneur concerné.
Si un conteneur dépasse la limite d’utilisation mémoire, celui-ci sera arrêté et redéployé (potentiellement sur un autre nœud).
Si un conteneur dépasse la limite d’utilisation processeur, celui-ci sera freiné (throttling) en étant mis en pause par intermittence.
Pourquoi définir des Requests ?
Parce que ces ressources sont garanties par Kubernetes, aucun autre conteneur ne pourra y avoir accès.
Quelles sont les bonnes valeurs pour les Requests ?
Les bonnes valeurs pour les Requests sont généralement les valeurs minimales permettant aux conteneurs de tourner sans être freiné (CPU) et sans être redémarré.
Pourquoi définir des Limits proche des Requests pour la mémoire ?
Sur internet on voit beaucoup d’exemples où les Limits sont définies à 2*Requests, mais ce n’est pas une bonne idée.
Pourquoi ? Prenons un exemple simple : un pod avec les valeurs de Request/Limit définies à 2Gi/4Gi et un nœud avec 3Gi de mémoire disponible.
Que va-t-il se passer ?
Le pod va pouvoir démarrer puisque le nœud a assez de mémoire pour satisfaire la valeur Request. Mais ensuite ? Quand le pod va monter en mémoire ?
Réponse : la mémoire du nœud va saturer. Kubernetes va alors essayer d’éjecter des pods pour les redémarrer sur d’autres nœuds. Mais si la montée est trop rapide, c’est le processus de gestion OOM du noyau Linux qui va se déclencher pour tuer des processus qu’il juge inutile. Pendant ce temps, le nœud peut être complètement freezé et inaccessible pendant plusieurs minutes.
Maintenant, imaginez la situation si tous les pods sont configurés avec Limit=2*Requests.
La recommandation est donc de mettre des valeurs de Limit très proche des valeurs de Request, voire identiques.
Et pourquoi pas de Limits pour le processeur ?
La mémoire et le CPU sont 2 types de ressources complètement différentes. Contrairement à la mémoire, le CPU n’est pas une ressource que l’on peut remplir ou vider. Ce n’est pas non plus une ressource que l’on peut découper.Lorsque l’on parle d’utilisation processeur, y compris dans le gestionnaire de ressources de votre OS préféré, on parle en réalité de temps.
Lorsque l’on dit que le CPU est utilisé à 100%, c’est que la somme des temps d’exécution de tous les processus sur la dernière tranche de temps donnée (3s par défaut pour top sur linux) est égale au temps d’exécution disponible du CPU sur cette même tranche de temps.
Si top indique 10% d’utilisation processeur pour un processus, cela signifie qu’il a utilisé 10% du temps d’exécution disponible du processeur sur les 3 dernières secondes, soit 300ms.
Dans Kubernetes, lorsque l’on définit une limite CPU pour un container, on ne définit pas un nombre ou une fraction de processeur utilisable, mais un quota maximum de temps d’utilisation par rapport au temps total d’exécution mis à disposition par l’ensemble des processeurs, et pas plus.
Ainsi, en définissant une limite CPU à 500m pour un conteneur, celui-ci n’utilisera pas un demi-processeur, mais aura accès, au maximum, à la moitié du temps d’exécution d’un processeur, et passera l’autre moitié de son temps à attendre d’être de nouveau autorisé à utiliser du temps processeur. Et cela, même si ceux-ci ne font rien à côté.
À noter que ce temps peut être réparti entre plusieurs processeurs, y compris lorsque la valeur est inférieure à 1.
Mais, s’il n’y a pas de Limits pour le processeur, il y a un risque qu’un conteneur prenne toutes les ressources ?
Non, car ce conteneur sera naturellement limité par les Requests des autres conteneurs.
Natan Yellin de robusta.dev a utilisé une analogie basée sur le rationnement de bouteille magique dans le désert que je conseille de lire pour bien comprendre pourquoi utiliser des Limits pour le processeur est généralement une mauvaise chose : https://home.robusta.dev/blog/stop-using-cpu-limits
Ces recommandations sont valables toujours, tout le temps ?
Non. Comme beaucoup de recommandations, elles seront valables la très grande majorité des cas.
Cependant, la présence ou non des Limits et Requests et leurs valeurs les unes par rapport aux autres ont une incidence directe sur la QoS qui sera assignée aux pods, et ainsi, quels pods seront évincés en priorité si un nœud est surchargé.
Pour conclure…
Bien paramétrer Requests et Limits est une étape cruciale pour optimiser la gestion des ressources dans Kubernetes. En appliquant ces bonnes pratiques—des Limits proches des Requests pour la mémoire et l’absence de Limits pour le CPU—vous évitez des blocages et optimisez l’efficacité de votre cluster.
Bien sûr, chaque cas d’usage peut avoir ses spécificités, mais ces recommandations s’appliquent dans la grande majorité des scénarios. Une configuration réfléchie vous permettra d’assurer la stabilité et la performance de vos applications Kubernetes.
Pour plus d’informations sur ce sujet : https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/