Introduction
Les limites de ressources Docker sont un contrôle opérationnel essentiel pour exécuter des conteneurs de manière fiable et économique. Lorsque vous définissez une limite de mémoire de 512 Mo, Docker l'applique via les cgroups du noyau Linux, mais le comportement de cette application dépend du type de ressource, du runtime de conteneur et de la configuration de l'hôte. Cet article explique l'architecture derrière les limites de ressources Docker et vous fournit des commandes pratiques pour les observer, les configurer et les vérifier.
Nous aborderons :
- Comment Docker interagit avec les cgroups v1 et v2 pour appliquer les limites.
- Les limites CPU, y compris les parts, les quotas et les ensembles de CPU.
- Les limites de mémoire, y compris le comportement OOM et le swap.
- Les limites d'I/O disque et de PID.
- Comment inspecter les limites actuelles et les configurer via la CLI Docker et Compose.
- Les modes de défaillance, les étapes de récupération et une liste de contrôle opérationnelle.
Tout au long, nous utilisons des commandes concrètes et des sorties attendues pour que vous puissiez suivre sur votre propre hôte.
Prérequis et inventaire de l'environnement
Avant de modifier toute limite de ressource, connaissez votre environnement.
Version de Docker et pilote cgroup
Le comportement des limites de ressources dépend de la version de Docker et du pilote cgroup configuré sur l'hôte. Exécutez :
docker version --format '{{.Server.Version}}'
Exemple de sortie attendue : 24.0.5
Vérifiez le pilote cgroup :
docker info --format '{{.CgroupDriver}} {{.CgroupVersion}}'
Exemple de sortie attendue : systemd 2
Si vous voyez cgroupfs et la version 1 des cgroups, certaines fonctionnalités plus récentes comme memory.high peuvent ne pas être disponibles. La plupart des distributions modernes utilisent par défaut la version 2 des cgroups avec le pilote systemd.
Noyau hôte et distribution
Confirmez que le noyau prend en charge les cgroups v2 :
grep cgroup /proc/filesystems
La sortie attendue inclut nodev cgroup2.
Identifier les conteneurs en cours d'exécution et leurs limites actuelles
Listez les conteneurs en cours d'exécution :
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
Inspectez ensuite les limites d'un conteneur spécifique, par exemple web :
docker inspect web --format '{{json .HostConfig.Resources}}'
Exemple de sortie attendue :
{"CpusetCpus":"","CpuShares":1024,"Memory":0,"NanoCpus":0,"MemorySwap":0,"PidsLimit":0}
Ici, Memory: 0 signifie aucune limite de mémoire, et NanoCpus: 0 signifie aucun quota CPU.
Vérifier les fichiers cgroup d'un conteneur
Trouvez le chemin cgroup du conteneur :
docker inspect web --format '{{.HostConfig.CgroupParent}} {{.Id}}'
Ensuite, en utilisant l'ID du conteneur, consultez son répertoire cgroup (exemple pour cgroup v2) :
cat /sys/fs/cgroup/system.slice/docker-<container-id>.scope/memory.max
Si aucune limite n'est définie, ce fichier contient max.
Chemin de configuration sûr
Étape 1 : Capturer les métriques de base
Avant de modifier les limites, mesurez l'utilisation actuelle. Utilisez docker stats :
docker stats --no-stream web
Exemple de sortie attendue :
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
c2f2... web 0.15% 120MiB / 1.945GiB 6.03% 1.2kB / 0B 0B / 0B 7
Notez la colonne MEM USAGE / LIMIT. Si LIMIT indique la mémoire totale de l'hôte, le conteneur est illimité.
Étape 2 : Choisir le bon type de limite
- CPU : Utilisez les parts CPU pour une pondération relative lorsque plusieurs conteneurs sont en concurrence. Utilisez un quota (NanoCpus) pour une limite absolue. Utilisez cpuset pour épingler à des cœurs spécifiques.
- Mémoire : Définissez une limite stricte (
--memory) et éventuellement une limite de swap (--memory-swap). Comprenez le comportement OOM. - I/O disque : Utilisez les limites blkio sur cgroup v1 ; sur cgroup v2, utilisez
--device-read-bpset--device-write-bpspour les périphériques bloc. - PID : Limitez le nombre de processus pour prévenir les bombes à fork avec
--pids-limit.
Étape 3 : Appliquer les limites via Docker Run
Exemple : exécutez un conteneur avec 512 Mo de mémoire, un quota CPU de 0,5 et 100 PID :
docker run -d --name web \
--memory 512m \
--memory-swap 1g \
--cpus 0.5 \
--pids-limit 100 \
nginx:alpine
Vérifiez :
docker inspect web --format '{{json .HostConfig.Resources}}'
La sortie attendue inclut "Memory":536870912, "NanoCpus":500000000, "PidsLimit":100.
Étape 4 : Appliquer les limites via Docker Compose
Dans docker-compose.yml :
services:
web:
image: nginx:alpine
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
memory: 128M
pids_limit: 100
Remarque : la clé deploy n'est honorée que par Docker Swarm. Pour docker compose (hors Swarm), utilisez la clé resources au niveau du service (Compose v2.24+ prend en charge limits et reservations sous resources). Alternativement, utilisez mem_limit et cpus pour une compatibilité plus ancienne :
services:
web:
image: nginx:alpine
mem_limit: 512m
cpus: 0.5
pids_limit: 100
Appliquez avec docker compose up -d.
Vérification et diagnostics
Après avoir défini les limites, vérifiez leur application depuis l'intérieur et l'extérieur du conteneur.
À l'intérieur du conteneur : vérifier la mémoire visible
Le conteneur voit la mémoire de l'hôte, pas la limite, sauf si vous définissez une réservation de mémoire. Cela perturbe beaucoup de monde. Exécutez :
docker exec web cat /proc/meminfo | head -3
La sortie attendue montre la mémoire totale de l'hôte, pas 512 Mo. Pour voir la limite cgroup, lisez :
docker exec web cat /sys/fs/cgroup/memory.max
Sortie attendue : 536870912 (512 Mo en octets).
Test de stress de la limite de mémoire
Utilisez un outil comme stress à l'intérieur du conteneur :
docker exec web sh -c "apk add --no-cache stress-ng && stress-ng --vm 1 --vm-bytes 600M --timeout 30s"
Après quelques secondes, vérifiez si le processus a été tué par OOM :
docker inspect web --format '{{.State.OOMKilled}} {{.State.ExitCode}}'
Sortie attendue : true 137 (137 = 128 + 9, SIGKILL).
Inspecter les événements cgroup
Sur cgroup v2, surveillez memory.events :
docker exec web cat /sys/fs/cgroup/memory.events
Recherchez les compteurs oom et oom_kill qui augmentent.
Vérification de la limitation CPU
Pour tester le quota CPU, exécutez une tâche gourmande en CPU et observez la limitation :
docker run --rm --cpus 0.5 alpine sh -c "time crunch 100000000000"
Vérifiez que le pourcentage CPU de docker stats reste proche de 50 %.
Pour cgroup v2, lisez cpu.stat :
docker exec web cat /sys/fs/cgroup/cpu.stat | grep throttled
La sortie attendue inclut nr_throttled et throttled_time.
Modes de défaillance et récupération
OOM kill à l'intérieur du conteneur
Si l'application à l'intérieur du conteneur est tuée, le conteneur peut se terminer ou redémarrer. Vérifiez :
docker inspect web --format '{{.State.OOMKilled}}'
Si vrai, augmentez la limite de mémoire ou réduisez l'empreinte de l'application. Note : si --memory-swap n'est pas défini, le swap est désactivé et la limite est égale à --memory. Pour autoriser le swap, définissez --memory-swap > --memory.
OOM au niveau de l'hôte
Si l'hôte manque de mémoire, le noyau peut tuer d'autres processus. Utilisez dmesg pour voir les OOM kills :
dmesg | grep -i oom
Récupération : ajoutez de la mémoire à l'hôte, réduisez les limites des conteneurs ou ajustez les paramètres d'overcommit.
Famine CPU
Si un conteneur est excessivement limité, les performances de l'application se dégradent. Vérifiez nr_throttled dans cpu.stat ; s'il est élevé, envisagez d'augmenter --cpus ou de passer à cpuset.
Limites d'I/O disque non appliquées
Sur certains pilotes de stockage, les limites blkio peuvent ne pas fonctionner. Vérifiez avec docker info | grep Storage Driver. Pour overlay2, les limites blkio peuvent être ignorées ; utilisez des limites spécifiques au périphérique ou passez à cgroup v2.
Limite de PID dépassée
Si le conteneur atteint la limite de PID, le fork échoue : « Ressource temporairement indisponible ». Augmentez --pids-limit si c'est légitime, sinon corrigez l'application.
Pièges courants et comment les éviter
- Supposer que le conteneur voit sa limite de mémoire : De nombreuses applications lisent
/proc/meminfoet pensent avoir la mémoire de l'hôte, provoquant des OOM. Solution : définissez des variables d'environnement ou utilisez des bibliothèques conscientes des cgroups. - Définir des parts CPU au lieu d'un quota : Les parts CPU sont relatives et ne plafonnent pas l'utilisation lorsque le CPU est inactif. Utilisez
--cpuspour des limites strictes. - Ne pas définir
--memory-swap: Si vous définissez--memorysans--memory-swap, le swap est désactivé et le conteneur ne peut pas utiliser de swap. Cela peut surprendre les applications qui s'attendent à du swap. - Ignorer les limites de PID : Une bombe à fork peut épuiser l'espace PID de l'hôte. Définissez toujours
--pids-limitpour les conteneurs non fiables. - Ne pas utiliser cgroup v2 : cgroup v2 offre une meilleure isolation et des fonctionnalités comme memory.high pour les limites souples. Migrez si possible.
- Surallouer les ressources de l'hôte : La somme des limites des conteneurs peut dépasser la capacité de l'hôte. Surveillez l'utilisation globale et définissez des réservations appropriées.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle avant et après avoir modifié les limites de ressources Docker.
| Étape | Action | Propriétaire | Fréquence |
|---|---|---|---|
| 1 | Mesurer les métriques de base avec docker stats | Ingénieur DevOps (ex. Alex Johnson) | Avant chaque changement de limite |
| 2 | Examiner le profil de l'application et déterminer les limites requises | Propriétaire de l'application (ex. Priya Shah) | Pré-déploiement |
| 3 | Appliquer les limites dans l'environnement de préproduction d'abord | Ingénieur DevOps | À chaque changement |
| 4 | Exécuter des tests de charge pour vérifier les performances sous limites | Responsable QA | À chaque version |
| 5 | Surveiller les OOM kills et les métriques de limitation | Équipe SRE | En continu (alertes) |
| 6 | Documenter les limites dans le contrôle de version | Ingénieur DevOps | À chaque changement |
| 7 | Réviser les limites trimestriellement et ajuster selon les tendances | Responsable ingénierie | Trimestriel |
Conclusion
Les limites de ressources Docker sont essentielles pour des environnements multi-locataires stables. En comprenant les mécanismes cgroup sous-jacents et en utilisant les commandes pratiques fournies ici, vous pouvez définir des limites CPU, mémoire, I/O et PID appropriées, diagnostiquer les problèmes d'application et récupérer après des défaillances. Commencez toujours par l'observation, apportez des modifications incrémentales et vérifiez avec des métriques concrètes.
Prochaine étape : sélectionnez un conteneur de votre environnement, suivez le chemin de configuration sûr pour définir une limite de mémoire, vérifiez avec un test de stress et documentez les résultats. Adoptez ensuite la liste de contrôle opérationnelle pour maintenir les limites au fil du temps.