E-NO Logo
EN FR
Docker resource limits 8 Min Read

Limites CPU et mémoire Docker pour des charges proches de la production : guide pratique

calendar_today Published: 2026-07-08
update Last Updated: 2026-07-08
analytics SEO Efficiency: 97%
Technical guide illustration for Limites CPU et mémoire Docker pour des charges proches de la production : guide pratique.

Introduction

Si vos conteneurs tournent correctement en local mais échouent sous trafic, vous avez sans doute besoin de limites CPU et mémoire explicites, et d'un moyen reproductible de les tester. Ce guide montre comment :

  • définir des CPU limits et un Docker memory limit avec docker run et Docker Compose
  • détecter et dépanner des OOMKilled containers
  • lire docker stats pour comparer l'usage réel aux limites
  • mener un pilote local sûr avant un déploiement plus large
  • éviter les production pitfalls et erreurs de capacity planning

Objectif : une méthode simple qui reproduit un comportement "proche de la prod" sur un laptop ou un hôte unique, sans mauvaises surprises plus tard.

Vue d'ensemble de la boucle de travail

Utilisez cette boucle pour chaque service.

  1. Définir un petit budget et des critères de succès
  • Choisissez une limite mémoire initiale (exemple : 256 MiB) et un budget CPU (exemple : 0,5 CPUs).
  • Définissez le succès/échec : le service reste sain N minutes sous une charge définie, la latence p95 respecte l'objectif, pas d'OOMKilled, et le CPU throttling est acceptable pour vos SLO.
  1. Poser 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 fixe la limite dure de RAM.
  • --memory-swap fixe RAM+swap. Égale à --memory pour désactiver le swap du conteneur. Mettez -1 pour autoriser un swap illimité (si le swap hôte est activé).
  • Limite CPU et pinning :
# Limiter à 0,5 CPU (parts d'un cœur dans le temps)
docker run --rm --cpus=0.5 nginx: alpine

# Optionnel : pinner sur les cœurs 0 et 1
docker run --rm --cpuset-cpus="0,1" --cpus=1.0 nginx: alpine
  1. Observer l'usage avec docker stats
docker stats

Champs clés :

  • CPU % : relatif aux CPUs attribués. Un conteneur avec --cpus=1.0 à 100 % est pleinement saturé.
  • MEM USAGE / LIMIT et MEM % : mémoire actuelle et plafond configuré.
  • PIDS : nombre de processus/threads du conteneur.

Symptômes :

  1. Détecter et diagnostiquer OOMKilled
  • Le conteneur se termine de façon inattendue, souvent avec le code 137 (SIGKILL).
  • docker ps ou docker inspect indique OOMKilled=true.

Commandes :

# OOMKilled ?
docker inspect -f '{{.State.OOMKilled}}' <container_id>

# Quel 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 volontairement (à 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 observez OOMKilled, augmentez la limite, réduisez la charge, ou fixez un plafond de heap au runtime (voir Dockerfile examples ci-dessous).

Deux voies fréquentes selon la cible.

  1. Encoder les limites dans Docker Compose
  • Docker compose local (hors Swarm) : privilégiez les clés de ressources du fichier Compose version 2.x pour une application locale effective.
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 honorés localement avec les fichiers v2.x.
  • cpu_shares définit un poids relatif quand l'hôte est chargé ; ce n'est pas un plafond dur en soi.
  • Déploiements Swarm : utilisez deploy.resources (Compose v3+). Ces paramètres 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 est appliqué par Swarm. Ne vous y fiez pas pour un docker compose local simple sans vérifier l'application effective.

Les limites du conteneur couvrent tout l'arbre de processus. Beaucoup de runtimes nécessitent aussi un cap au niveau applicatif pour ne pas dépasser votre budget.

  1. Ajouter des plafonds mémoire au niveau du runtime dans votre Dockerfile

Exemples :

# Node.js : plafonner le heap V8
FROM node:20-alpine
ENV NODE_OPTIONS=--max-old-space-size=256

# Java : définir le heap et les réglages "container-aware"
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 RSS
FROM python:3.11-alpine
ENV MALLOC_ARENA_MAX=2

Alignez ces valeurs avec votre --memory de conteneur. Si le runtime peut croître au-delà du cap, vous verrez des OOMKilled sous charge.

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

  1. Tester la charge en local de façon contrôlée
# Burn CPU (un cap à 0,5 CPU va brider ceci)
docker run --rm --cpus=0.5 alpine sh -c "yes > /dev/null"

# Pic mémoire (attendez-vous à OOMKilled à 128m)
docker run --rm --memory=128m --memory-swap=128m python:3.11-alpine \
  sh -c "python - <<'PY'\na=['x'*10_000_000 for _ in range(100)]\nimport time; time.sleep(60)\nPY"

Surveillez docker stats et vos métriques de service pendant le test. Ajustez limites et caps runtime jusqu'à atteindre vos objectifs.

Consignez pour chaque service :

  1. Documenter budgets et compromis
  • mémoire en régime établi, mémoire de pointe et limite
  • budget CPU et saturation observée sous charge
  • quand le throttling est acceptable vs quand il faut scaler
  • modes de panne connus (par exemple, OOMKilled quand batch size > N)

Plan de pilote local

Faites un premier pilote restreint, mesurable et facile à inspecter en local.

Périmètre

  • Un service important mais simple (exemple : API publique ou worker).

Configuration initiale

# Compose pour test local (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émarrez le service et vérifiez son readiness.
  2. Appliquez une petite charge constante (par exemple, une boucle curl à 5 rps pendant 10 minutes).
  3. Observez avec docker stats. Conservez un snapshot aux minutes 1, 5 et 10.
  4. Vérifiez redémarrages ou OOMKilled avec docker ps et docker inspect.
  5. Augmentez la charge par paliers (par exemple, +2 rps) et répétez.

Critères de réussite

  • Aucun événement OOMKilled.
  • Latence p95 dans la cible.
  • MEM % en dessous de 80 % en régime établi ; des pics courts sont acceptables s'ils ne mènent pas à OOMKilled.
  • La latence reste acceptable sous le cap CPU. Sinon, augmentez cpus ou multipliez les réplicas.

Itération suivante

  • Si la mémoire est juste, baissez le heap runtime ou augmentez modérément mem_limit (ex. +64m).
  • Si le CPU est saturé et la latence souffre, augmentez cpus (ex. 0.5 -> 1.0) ou ajoutez une seconde réplique.
  • Encodez les changements dans Compose et rejouez les mêmes étapes pour comparer.

Dépannage rapide

  • Le conteneur redémarre avec le code 137 et OOMKilled=true : réduisez l'utilisation mémoire ou relevez la limite ; alignez le heap runtime avec la limite conteneur ; envisagez de désactiver le swap du conteneur en mettant --memory-swap égal à --memory.
  • CPU bloqué à 100 % avec throttling : augmentez --cpus, réduisez le travail par requête, ou ajoutez des réplicas. Pinner sur des cœurs précis uniquement pour de bonnes raisons (isolation, tests NUMA).
  • docker stats montre MEM proche de LIMIT alors que le heap de l'app paraît petit : tenez compte des allocations natives, caches, JIT, threads et du page cache ; réduisez les arènes de l'allocateur ou le code cache JIT si applicable.
  • Ressources Compose non appliquées en local : utilisez les clés v2.x mem_limit/cpus pour les tests locaux ; considérez deploy.resources comme réservé à Swarm sauf validation explicite.
  • Sur macOS/Windows : Docker tourne dans une VM. Assurez-vous que la VM a suffisamment de mémoire et de CPUs, sinon tous les conteneurs se disputent les ressources ou sont tués au niveau de la VM.

Avertissements de capacity planning

  • Gardez de la marge. Ne fixez pas les limites à l'égal de l'usage de pointe observé ; prévoyez des bursts, le GC, le JIT et les buffers noyau.
  • Distinguez régime établi et comportement en pic. Si les pics causent des OOMKilled, optimisez l'app (taille de batch, heap, cache) avant d'augmenter simplement les limites.
  • Observez dans la durée. Des tests courts masquent des fuites et de la fragmentation qui n'apparaissent qu'après des heures ou des jours.
  • Équilibrez les CPU limits avec vos objectifs de latence. Des caps durs protègent les voisins mais peuvent augmenter la latence de queue sous charges bursty ; choisissez en fonction de vos SLO.
  • Documentez les budgets par service pour que les changements futurs ne détricotent pas vos garanties.

Conclusion

Commencez petit et méthodique : fixez des limites CPU et mémoire conservatrices, vérifiez-les avec docker stats et l'état de sortie des conteneurs, et encodez les réglages dans Docker Compose selon votre environnement. Ajoutez des caps mémoire au niveau du runtime pour garder l'application dans le budget du conteneur. Utilisez le plan de pilote local pour étendre la couverture service par service. Avec le temps, vos réglages deviennent des défauts fiables et « production-like » que vous pourrez faire évoluer en confiance.

Article Quality Score

Reader usefulness 97%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL