E-NO
DevOps 8 min de lecture

Limites CPU et mémoire Docker pour des charges de travail proches de la production : guide pratique d'implémentation

calendar_today Publié : 2026-07-08
update Dernière mise à jour : 2026-07-08
analytics Efficacité SEO : 97%
Illustration du guide technique pour « Limites CPU et mémoire Docker pour des charges de travail proches de la production : guide pratique d'implémentation ».

Introduction

Si vos conteneurs fonctionnent bien en local mais plantent ou sont limités sous une vraie charge, vous avez probablement besoin de limites explicites CPU et mémoire ainsi que d'une méthode reproductible pour les valider. Ce guide montre comment :

  • Définir des limites CPU et mémoire avec docker run et Docker Compose
  • Détecter et diagnostiquer les conteneurs OOMKilled
  • Lire docker stats pour comparer l'utilisation réelle aux limites configurées
  • Exécuter un pilote local sûr avant un déploiement plus large
  • Éviter les pièges courants en production et les erreurs de planification de capacité

L'objectif est un flux de travail simple qui donne un comportement proche de la production sur un portable ou un hôte unique, sans surprises ultérieures.

Aperçu du workflow

Utilisez cette boucle pour chaque service.

  1. Définir un budget modeste et des critères de réussite
  • Choisissez une limite mémoire initiale (par exemple 256 Mio) et un budget CPU (par exemple 0,5 CPU).
  • Définissez succès/échec : le service reste sain pendant N minutes sous une charge définie, latence p95 dans la cible, aucun OOMKilled, throttling CPU acceptable pour votre SLO.
  1. Appliquer des limites pour des tests rapides (docker run)

Limite mémoire dure et politique de swap :

   docker run --rm \
    --name web \
    --memory=256m \
    --memory-swap=256m \
    -p 8080:80 nginx:alpine

Notes :

  • --memory définit la limite RAM dure.
  • --memory-swap définit RAM + swap. Mettez-le égal à --memory pour désactiver le swap pour le conteneur. Mettez -1 pour autoriser un swap illimité (si le swap hôte est activé).

Limite CPU et pinning :

   # Limiter à 0,5 CPU (part d'un cœur dans le temps)
   docker run --rm --cpus=0.5 nginx:alpine

   # Optionnellement épingler aux cœurs 0 et 1
   docker run --rm --cpuset-cpus="0,1" --cpus=1.0 nginx:alpine
  1. Observer l'utilisation avec docker stats
   docker stats

Champs clés :

  • CPU % : relatif aux CPUs assignés. Un conteneur avec --cpus=1.0 à 100 % est saturé.
  • MEM USAGE / LIMIT et MEM % : mémoire courante et le plafond configuré.
  • PIDS : nombre de processus/threads dans le conteneur.
  1. Détecter et diagnostiquer OOMKilled

Symptômes :

  • Le conteneur s'arrête inopinément, souvent avec le code de sortie 137 (tué par SIGKILL).
  • docker ps ou docker inspect affiche OOMKilled=true.

Commandes :

   # Était-ce un OOMKilled ?
   docker inspect -f '{{.State.OOMKilled}}' <container_id>

   # Quel était le code de sortie ?
   docker inspect -f '{{.State.ExitCode}}' <container_id>

   # Événements récents du démon (chercher oom)
   docker events --since 10m | grep -i oom || true

Pour reproduire intentionnellement à des fins d'apprentissage :

   # Allouer trop de mémoire pour déclencher OOMKilled
   docker run --rm --memory=128m --memory-swap=128m python:3.11-alpine \
    python -c "a=['x'*10_000_000 for _ in range(100)]; import time; time.sleep(60)"

Si vous voyez OOMKilled, augmentez la limite, réduisez la charge ou plafonnez le tas du runtime (voir exemples Dockerfile ci-dessous).

  1. Encoder les limites dans Docker Compose

Deux approches courantes selon la cible.

docker compose local (non Swarm) : Privilégiez les clés de ressources de la version 2.x du fichier Compose pour une application locale.

   version: "2.4"
   services:
     api:
       image: myorg/api:1.0.0
       mem_limit: 512m
       mem_reservation: 128m
       cpus: "1.0"
       cpu_shares: 512
       ports:
         - "8080:8080"

Notes :

  • mem_limit et cpus sont respectés localement avec les fichiers v2.x.
  • cpu_shares définit le poids relatif quand l'hôte est occupé ; ce n'est pas un plafond dur par lui-même.

Déploiements Swarm : Utilisez deploy.resources (Compose v3+). Ceux-ci sont pour le mode Swarm.

   version: "3.8"
   services:
     api:
       image: myorg/api:1.0.0
       deploy:
         resources:
           limits:
             cpus: '1.0'
             memory: 512M
           reservations:
             cpus: '0.25'
             memory: 128M
       ports:
         - "8080:8080"

Notes :

  • deploy.resources sont appliqués par Swarm. Ne comptez pas sur eux pour du docker compose local pur sauf si vous validez aussi l'application effective.
  1. Ajouter des plafonds mémoire au niveau du runtime dans votre Dockerfile

Les limites du conteneur plafonnent l'ensemble de l'arbre de processus. De nombreux runtimes ont aussi besoin de plafonds au niveau application pour ne pas allouer au-delà de votre budget.

Exemples :

Node.js : plafonner le tas V8

   FROM node:20-alpine
   ENV NODE_OPTIONS=--max-old-space-size=256

Java : définir le tas et l'ergonomie consciente du conteneur

   FROM eclipse-temurin:21-jre
   ENV JAVA_TOOL_OPTIONS="-XX:+UseContainerSupport -Xms128m -Xmx256m"

Python : réduire les arènes de l'allocateur glibc pour limiter la croissance du RSS

   FROM python:3.11-alpine
   ENV MALLOC_ARENA_MAX=2

Gardez ces réglages alignés avec votre --memory conteneur. Si le runtime peut dépasser le plafond, vous verrez OOMKilled sous charge.

  1. Tester la charge localement de manière contrôlée

Vous pouvez générer une pression CPU ou mémoire simple sans outils supplémentaires :

   # Brûler CPU (le plafond 0,5 CPU limitera ce processus)
   docker run --rm --cpus=0.5 alpine sh -c "yes > /dev/null"

   # Pic mémoire (attendre OOMKilled à 128m)
   docker run --rm --memory=128m --memory-swap=128m alpine \
    sh -c "python3 - <<'PY'
a=['x'*10_000_000 for _ in range(100)]
import time; time.sleep(60)
PY"

Surveillez docker stats et vos métriques de service pendant le test. Ajustez les limites et les plafonds runtime jusqu'à ce que vos objectifs tiennent.

  1. Documenter les budgets et les compromis

Enregistrez pour chaque service :

  • Mémoire en état stable, mémoire de pointe et limite
  • Budget CPU et saturation observée sous charge
  • Quand le throttling est acceptable vs quand passer à l'échelle horizontale
  • Modes de défaillance connus (par exemple, OOMKilled quand la taille de lot > N)

Plan de pilote local

Rendez le premier pilote étroit, mesurable et facile à inspecter localement.

Périmètre

  • Un service important mais simple (par exemple, une API publique ou un worker).

Configuration initiale

# Compose pour tests locaux (v2.4)
version: "2.4"
services:
  api:
    image: myorg/api:1.0.0
    mem_limit: 256m
    mem_reservation: 128m
    cpus: "0.5"
    ports: ["8080:8080" ]

Étapes de test

  1. Démarrer le service et confirmer sa disponibilité.
  2. Appliquer une charge faible et constante (par exemple, une boucle curl à 5 req/s pendant 10 minutes).
  3. Observer avec docker stats. Sauvegarder un instantané aux minutes 1, 5 et 10.
  4. Vérifier les redémarrages ou OOMKilled avec docker ps et docker inspect.
  5. Augmenter la charge d'un petit pas (par exemple, +2 req/s) et répéter.

Critères de réussite

  • Aucun événement OOMKilled.
  • Latence p95 dans la cible.
  • MEM % reste sous 80 % en état stable ; de courts pics sont tolérés s'ils ne provoquent pas d'OOMKilled.
  • CPU % sous le plafond produit une latence acceptable. Sinon, augmenter cpus ou ajouter des répliques.

Itération suivante

  • Si la mémoire est serrée, baisser le tas du runtime ou augmenter mem_limit modestement (par exemple, +64m).
  • Si le CPU est saturé et la latence souffre, augmenter cpus (par exemple, 0,5 -> 1,0) ou ajouter une deuxième réplique.
  • Reporter les changements dans Compose et relancer les mêmes étapes pour comparer.

Référence rapide de dépannage

  • Le conteneur redémarre avec le code 137 et OOMKilled=true : réduire l'utilisation mémoire ou relever la limite ; aligner le tas du runtime avec la limite du conteneur ; envisager de désactiver le swap pour le conteneur en mettant --memory-swap égal à --memory.
  • CPU bloqué à 100 % avec throttling : augmenter --cpus, réduire le travail par requête, ou ajouter des répliques. Épingler à des cœurs spécifiques seulement si vous avez une raison (isolation, test NUMA).
  • docker stats montre MEM proche de LIMIT alors que le tas de l'appli semble petit : tenir compte des allocations natives, caches, JIT, threads et page cache ; réduire les arènes d'allocateur ou le cache de code JIT si applicable.
  • Les ressources Compose ne s'appliquent pas localement : utiliser mem_limit/cpus v2.x pour les tests locaux ; traiter deploy.resources comme Swarm-only sauf vérification contraire.
  • Sur macOS/Windows : Docker s'exécute dans une VM. Assurez-vous que la VM a assez de mémoire et de CPUs, sinon tous les conteneurs peuvent se contendre ou être tués au niveau de la VM.

Mises en garde pour la planification de capacité

  • Laissez de la marge. Ne fixez pas les limites à l'utilisation de pointe observée ; prévoyez les pics, GC, JIT et tampons noyau.
  • Séparez comportement stable et comportement en pointe. Si les pics causent OOMKilled, ajustez l'application (taille de lot, tas, mise en cache) avant de simplement relever les limites.
  • Observez dans le temps. Des tests courts peuvent cacher des fuites et de la fragmentation qui n'apparaissent qu'après des heures ou des jours.
  • Équilibrez plafonds CPU et objectifs de latence. Les plafonds durs protègent les voisins mais peuvent augmenter la latence de queue sous charge bursty ; choisissez selon vos SLO.
  • Documentez les budgets par service pour que de futurs changements n'effacent pas silencieusement vos garanties.

Conclusion

Commencez petit et délibérément : fixez des limites CPU et mémoire conservatrices, vérifiez avec docker stats et l'état de sortie du conteneur, et encodez les réglages dans Compose adaptés à votre environnement. Ajoutez des plafonds mémoire au niveau du runtime pour garder votre application dans le budget du conteneur. Utilisez le plan de pilote local pour étendre la couverture service par service. Avec le temps, vos limites deviennent des valeurs par défaut fiables, proches de la production, que vous pouvez faire évoluer en toute confiance.

Recherches connexes

Score de qualité de l’article

Utilité pour le lecteur 97%
  • check_circle Guide prêt à lire
  • check_circle Exemples pratiques inclus
  • check_circle URL d’article optimisée pour le SEO