Kubernetes
4 min de lecture

Ressources Kubernetes : deux règles, zéro prise de tête

OOMKilled, throttling invisible, pods qui redémarrent. Deux règles simples pour configurer CPU et mémoire sans se planter.

Arthur Zinck
Arthur Zinck
Expert DevOps Kubernetes & Cloud

Un pod qui redémarre en boucle. OOMKilled dans les events. Le développeur m’explique que son application ne consomme “que 512 Mo en local”. C’est un scénario classique, et il a raison de se fier à ses observations — le problème, c’est que Kubernetes ne fonctionne pas tout à fait comme un environnement local.

L’allocation des ressources Kubernetes, c’est un sujet qu’on survole souvent. On s’inspire d’exemples trouvés en ligne, on adapte un peu, et ça marche. Jusqu’au jour où ça ne marche plus. J’ai moi-même mis du temps à vraiment comprendre les subtilités. Ce n’est pas un sujet intuitif.

Il y a deux règles à retenir. Deux. Le reste, c’est du contexte pour comprendre pourquoi.

La mémoire, c’est binaire. Un conteneur qui dépasse sa limit se fait tuer. Pas de négociation. Le kernel Linux ne sait pas “reprendre” de la mémoire à un processus en cours d’exécution. Impossible. Le processus stocke ses données, ses caches, ses résultats intermédiaires dans cet espace. Lui retirer une partie reviendrait à corrompre son état.

Donc quand kubelet détecte une pression mémoire sur un node, il cherche des victimes. Les premiers sur la liste : les pods qui consomment plus que leur request mais moins que leur limit. Ces pods-là vivent en sursis. Un pic de charge sur le node et boom, OOMKilled.

La semaine dernière, un client me montre ses logs. Son API gateway redémarrait 3 à 4 fois par jour. Toujours aux heures de pointe. Request mémoire à 256 Mi, limit à 1 Gi. Le conteneur tournait autour de 400 Mi en moyenne. Largement sous la limit. Mais au-dessus de la request. À chaque pic de trafic sur le cluster, c’était lui qui sautait.

La configuration était logique sur le papier — laisser de la marge avec une limit haute, c’est prudent. Sauf que Kubernetes interprète ça différemment.

La fix : request et limit à 512 Mi. Alignées. Plus de redémarrages depuis deux semaines.

Règle numéro un : mémoire, requests égales aux limits. Toujours.

Le CPU, c’est une autre histoire. Plus subtile. Et franchement, celle-là m’a pris du temps à intégrer.

Le CPU est une ressource temporelle. Le scheduler Linux (CFS) découpe le temps en périodes de 100 ms. Chaque conteneur reçoit un quota de millisecondes dans cette période. Un conteneur avec une limit de 1 CPU peut utiliser 100 ms de temps CPU par période de 100 ms.

Là où ça se complique : sur une machine multi-core, ces 100 ms peuvent se répartir sur plusieurs cœurs. Ton conteneur peut consommer son quota en 20 ms réelles sur 5 cœurs, puis attendre 80 ms. C’est le throttling.

Et le throttling, il ne crie pas. Il ne génère pas d’alerte. Ton conteneur continue de tourner. Juste… plus lentement. Les requêtes prennent 200 ms au lieu de 50 ms. Personne ne comprend pourquoi. J’ai perdu pas mal de temps sur ce genre de problèmes avant de développer le réflexe de regarder les métriques CFS.

J’ai passé deux jours sur un cluster où une application Java “ramait” de façon aléatoire. CPU usage à 60% selon Prometheus. Tout semblait normal. J’ai creusé les métriques CFS :

  • container_cpu_cfs_throttled_seconds_total : 847 secondes de throttling cumulées sur 24h
  • container_cpu_cfs_periods_total : 40% des périodes impactées

L’application était étranglée en silence. Limit CPU à 2, mais des pics à 4-5 cœurs pendant le garbage collection. La JVM se faisait throttler à chaque GC.

La solution : supprimer la limit CPU. Garder uniquement la request pour le scheduling. Le scheduler place le pod sur un node avec suffisamment de CPU disponible, mais le conteneur peut burst au-delà si le node a de la capacité.

Règle numéro deux : CPU, uniquement des requests, pas de limits.

Une question revient souvent quand je propose ça : “Et si un conteneur consomme tout le CPU du node ?” C’est une inquiétude légitime, et elle montre qu’on a bien compris que les ressources sont partagées. La réponse : c’est pour ça que les requests existent. Kubernetes garantit que chaque pod obtient au minimum sa request. Le surplus est partagé proportionnellement.

Un pod avec une request de 2 CPU sur un node chargé obtiendra toujours ses 2 CPU. Il pourra en avoir plus si d’autres pods n’utilisent pas leur quota. C’est du partage intelligent, pas du chaos.

Pour résumer :

  • Mémoire : requests: 512Mi et limits: 512Mi — identiques
  • CPU : requests: "1" — pas de limits

Deux lignes de YAML. Des heures de debugging en moins.

Meme Kubernetes Optimization

Points clés à retenir

  • Mémoire : requests = limits, toujours (évite les OOMKilled surprise)
  • CPU : uniquement des requests, pas de limits (évite le throttling invisible)
  • Le throttling CPU ne génère pas d'alerte mais tue les perfs
  • Métriques à surveiller : container_cpu_cfs_throttled_seconds_total
kubernetes ressources cpu memory throttling oomkilled

Partager cet article

Twitter LinkedIn