Introduction
En production, les conteneurs se disputent souvent des ressources finies : CPU, mémoire et E/S. Sans limites explicites, un conteneur peut dégrader tout l'hôte, provoquant des pics de latence ou des pannes. Les limites de ressources Docker permettent aux opérateurs d'imposer des frontières, de protéger les services critiques et de maintenir des performances prévisibles.
Cet article fournit une liste de contrôle pratique pour planifier, configurer, vérifier et maintenir les limites de ressources Docker en production. Vous y trouverez des commandes concrètes, des exemples de configuration et les résultats attendus pour vous guider.
Inventaire de version et d'environnement
Avant de définir des limites de ressources, documentez votre environnement. Confirmez la version du démon Docker, le pilote de stockage et la couche d'orchestration (le cas échéant). Le support des limites de ressources varie selon la version de Docker et le runtime. Par exemple, les limites CPU nécessitent Docker 1.13+, tandis que le support cgroup v2 est arrivé dans Docker 20.10.
Exécutez la commande suivante pour vérifier la version de Docker et la version de cgroup :
docker info --format '{{.ServerVersion}} | cgroup driver: {{.CgroupDriver}} | cgroup version: {{.CgroupVersion}}'
Résultat attendu (exemple) :
20.10.12 | cgroup driver: cgroupfs | cgroup version: 1
Vérifiez également le noyau hôte et le système d'exploitation : uname -r. Assurez-vous d'avoir la permission de modifier les paramètres cgroup (root ou sudo). Consignez ces détails dans votre runbook.
Si vous utilisez des orchestrateurs comme Kubernetes, sachez que les limites de ressources Docker peuvent être gérées par le planificateur. Ici, nous nous concentrons sur Docker Engine directement, mais de nombreux principes s'appliquent.
Ensuite, identifiez les charges de travail. Classez les conteneurs comme critiques, normaux ou best-effort. Les services critiques ont besoin de ressources garanties, tandis que les best-effort peuvent être fortement limités. Inventoriez l'utilisation actuelle avec docker stats --no-stream pour établir des références.
Exemple de sortie de docker stats --no-stream :
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
f1e2d3c4b5a6 web 5.00% 150MiB / 512MiB 29.30% 1.2MB / 800kB 4.1MB / 0B 12
b7c8d9e0f1a2 db 12.00% 800MiB / 1GiB 78.13% 2.5MB / 1.8MB 10MB / 20MB 25
Utilisez ces données pour définir des limites initiales et identifier les conteneurs qui nécessitent une attention.
Chemin de configuration sécurisé
Appliquez les limites progressivement. Commencez par des conteneurs non critiques dans un environnement de préproduction, puis étendez. Définissez les limites au lancement du conteneur avec la commande docker run ou dans des fichiers Compose avec la section deploy.resources (pour Swarm) ou resources (pour les versions plus récentes de Compose).
Étape 1 : Définir des limites CPU
Les limites CPU sont des poids relatifs lors de l'utilisation du planificateur par défaut. Pour des limites strictes, utilisez --cpus (par exemple, --cpus=1.5 autorise 1,5 cœur). Si vous avez besoin d'un poids proportionnel, --cpu-shares existe, mais la documentation Docker actuelle se concentre sur --cpus et ne couvre pas --cpu-shares.
Exemple de limite CPU stricte :
docker run -d --name web --cpus=1.5 --memory=512m nginx
Ce conteneur peut utiliser au maximum 1,5 cœur CPU. Pour confirmer, exécutez docker inspect web --format '{{.HostConfig.NanoCpus}}' et vous verrez 1500000000 (1,5 * 1e9 nanocpus).
Exemple de limite CPU pour plusieurs conteneurs :
docker run -d --name app1 --cpus=0.5 monimage
docker run -d --name app2 --cpus=1.0 monimage
App1 est limité à 0,5 CPU et app2 à 1,0 CPU. En cas de contention CPU, app2 peut utiliser jusqu'à deux fois plus de temps CPU que app1. Si vous avez besoin d'un poids relatif, --cpu-shares existe toujours, mais il n'est pas couvert dans la documentation Docker actuelle.
Quota et période CPU (avancé) :
Pour définir directement un quota CPU, utilisez --cpu-quota=50000 --cpu-period=100000 pour 50 % d'un cœur.
docker run -d --name batch --cpu-quota=50000 --cpu-period=100000 montravailbatch
Cela équivaut à 0,5 CPU.
Étape 2 : Définir des limites mémoire
Définissez toujours des limites mémoire pour prévenir les incidents OOM (Out-Of-Memory). Utilisez --memory (pour la RAM) et --memory-swap (pour la mémoire totale + swap). Si --memory-swap est omis, il prend par défaut le double de la limite mémoire.
Limite mémoire avec swap désactivé :
docker run -d --name api --memory=1g --memory-swap=1g monapp
Cela empêche le conteneur d'utiliser le swap, c'est-à-dire que la mémoire totale est plafonnée à 1 Go. Si le conteneur tente de dépasser cela, il sera tué par OOM.
Limite mémoire avec swap autorisé :
docker run -d --name cache --memory=512m --memory-swap=1g redis
Cela autorise 512 Mo de RAM plus 512 Mo de swap. L'utilisation du swap peut dégrader les performances ; à utiliser avec prudence.
Réservation mémoire (limite douce) :
docker run -d --name web --memory=1g --memory-reservation=750m nginx
Sous pression mémoire, le noyau tente de récupérer de la mémoire jusqu'à la réservation, mais ne tuera pas par OOM au-dessus de cette réservation à moins que la limite stricte ne soit dépassée.
Étape 3 : Définir des limites Blkio (facultatif)
Pour les E/S disque, utilisez --device-read-bps, --device-write-bps, --device-read-iops, --device-write-iops.
Limiter le débit d'écriture sur un périphérique spécifique :
docker run -d --name db --device-write-bps /dev/sda:10mb postgres
Cela limite les écritures à 10 Mo/s sur /dev/sda. Utile pour empêcher un voisin bruyant de saturer les E/S disque.
Limiter les IOPS en lecture :
docker run -d --name logger --device-read-iops /dev/sda:100 monappdelog
Cela limite les lectures à 100 IOPS.
Étape 4 : Utiliser Compose pour la répétabilité
Pour Compose v3 (Swarm), utilisez la section deploy :
version: "3.8"
services:
web:
image: nginx
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M
Remarque : les reservations ne sont honorées qu'en mode Swarm.
Pour Compose v2 (non-Swarm), utilisez les clés de service de niveau supérieur :
version: "2.4"
services:
web:
image: nginx
mem_limit: 256m
memswap_limit: 512m
cpus: 0.5
cpu_shares: 512
Pour Compose v1 avec l'outil autonome docker-compose, vous pouvez également utiliser mem_limit et cpus.
Exemple complet de docker-compose.yml pour une pile de type production :
version: "3.8"
services:
frontend:
image: nginx:1.21
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
restart: always
backend:
image: monapp:2.0
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
environment:
- DB_HOST=db
restart: always
db:
image: postgres:13
deploy:
resources:
limits:
memory: 2G
cpus: '2.0'
volumes:
- pgdata:/var/lib/postgresql/data
restart: always
volumes:
pgdata:
Lors du déploiement sur Swarm, utilisez docker stack deploy -c docker-compose.yml mapile. Pour non-Swarm, utilisez docker compose up -d avec une version qui prend en charge la clé deploy (ignorée) ou utilisez directement mem_limit/cpus.
Étape 5 : Mettre à jour les conteneurs existants
Les conteneurs existants nécessitent une recréation pour appliquer de nouvelles limites. Utilisez docker update pour certains paramètres (parts CPU, limites mémoire) si le conteneur est en cours d'exécution, mais pas pour tous (par exemple, --cpus). Pour des résultats cohérents, recréez le conteneur avec les drapeaux souhaités.
Mettre à jour la limite mémoire sur un conteneur en cours d'exécution :
docker update -m 512m monconteneur
Mettre à jour les parts CPU :
docker update --cpu-shares 512 monconteneur
Remarque : docker update ne peut pas modifier --cpus, --memory-swap ou les paramètres blkio après la création. Pour ceux-ci, vous devez arrêter, supprimer et recréer le conteneur. Utilisez toujours une configuration versionnée pour la recréation.
Vérification et diagnostics
Après avoir appliqué les limites, vérifiez qu'elles sont actives et observez le comportement sous charge.
Inspecter les limites du conteneur
Utilisez docker inspect pour confirmer les paramètres :
docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}' nom_conteneur
Exemple de sortie :
524288000 1500000000
Cela indique 512 Mo de mémoire (en octets) et 1,5 CPU (en nanocpus, 1,5 * 1e9).
Inspecter tous les paramètres liés aux ressources :
docker inspect --format 'Memory: {{.HostConfig.Memory}}, MemorySwap: {{.HostConfig.MemorySwap}}, CpuShares: {{.HostConfig.CpuShares}}, NanoCpus: {{.HostConfig.NanoCpus}}' web
Exemple de sortie :
Memory: 536870912, MemorySwap: 1073741824, CpuShares: 1024, NanoCpus: 500000000
Surveiller l'utilisation à l'exécution
Utilisez docker stats pour voir la consommation actuelle et les limites :
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"
Résultat attendu (hypothétique) :
NAME CPU % MEM USAGE / LIMIT MEM %
web 15.00% 120MiB / 512MiB 23.44%
api 0.50% 300MiB / 1GiB 29.30%
db 45.00% 1.2GiB / 2GiB 60.00%
Notez que la colonne "MEM USAGE / LIMIT" montre la limite appliquée. Le % CPU est relatif au nombre de cœurs disponibles sur l'hôte. Avec une limite de 1,5 CPU, le % CPU maximum affiché serait de 150 %.
Simuler une charge pour vérifier l'application
Pour la mémoire, exécutez une commande de stress à l'intérieur du conteneur :
docker exec -it web stress --vm 1 --vm-bytes 600M
Si la limite mémoire est de 512 Mo, le conteneur devrait être tué par OOM. Observez que le conteneur est tué ou limité lorsqu'il dépasse la limite. Vérifiez le code de sortie et les journaux : docker inspect web --format '{{.State.OOMKilled}}' devrait afficher true après un kill OOM.
Pour le CPU, exécutez une tâche intensive en CPU et vérifiez que l'utilisation ne dépasse pas la limite avec docker stats. Avec --cpus=1.5, le % CPU devrait plafonner à 150 % sur un hôte multicœur.
Exemple de test de stress CPU :
docker exec -it web bash -c "yes > /dev/null &"
Ensuite, observez docker stats web :
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
f1e2d3c4b5a6 web 150.00% 150MiB / 512MiB 29.30% 1.2MB / 800kB 4.1MB / 0B 3
Le % CPU sera limité exactement à 150 %.
Vérifier les paramètres cgroup
Inspectez les fichiers cgroup de l'hôte (nécessite root) :
cat /sys/fs/cgroup/memory/docker/<container_id>/memory.limit_in_bytes
Devrait afficher 536870912 pour 512 Mo.
Sur les systèmes cgroup v2, le chemin peut différer : /sys/fs/cgroup/system.slice/docker-<container_id>.scope/memory.max.
Modes de défaillance et récupération
Comprendre les modes de défaillance aide à la planification de la récupération. Les problèmes courants incluent :
- Kills OOM : Le conteneur dépasse la limite mémoire, le noyau tue un processus. Le conteneur peut redémarrer (si la politique de redémarrage le permet) ou rester arrêté. Atténuation : définissez des limites mémoire avec une marge, surveillez l'utilisation de la mémoire et mettez à l'échelle horizontalement.
- Famine CPU : Le conteneur est excessivement limité, provoquant une réponse lente. Atténuation : ajustez
--cpusou supprimez les limites CPU pour les services critiques. - Goulot d'étranglement E/S : Des limites Blkio trop basses ralentissent les écritures de la base de données. Atténuation : augmentez les limites ou séparez les volumes de données.
- Limites incorrectes dues à une mauvaise configuration : Des fautes de frappe dans les drapeaux ou les fichiers Compose peuvent conduire à aucune limite ou à des restrictions involontaires. Atténuation : utilisez
docker inspectpour vérifier après la création.
Exemple de kill OOM
Supposons qu'un conteneur web ait --memory=512m et --memory-swap=512m. Un pic soudain amène l'application à allouer plus de 512 Mo. Le tueur OOM du noyau met fin au processus à l'intérieur du conteneur. Docker enregistre cela dans l'état du conteneur :
docker inspect web --format '{{.State.OOMKilled}} {{.State.ExitCode}}'
Sortie :
true 137
Le code de sortie 137 indique que le processus a été tué par SIGKILL (OOM). Le conteneur peut redémarrer si --restart=always est défini. Pour diagnostiquer, vérifiez docker logs web et les journaux dmesg de l'hôte pour les événements OOM.
Étapes de retour en arrière
Pour annuler les modifications :
- Supprimez ou ajustez les limites en recréant le conteneur sans les drapeaux de limite.
- Si le conteneur a été tué, redémarrez-le avec la configuration précédente.
- Dans Compose, revenez à la section ressources précédente et exécutez à nouveau
docker compose up -d.
Exemple : Supprimer la limite mémoire d'un conteneur en cours d'exécution
Vous pouvez utiliser docker update -m 0 nom_conteneur mais notez que tous les paramètres ne peuvent pas être mis à jour. Plus sûr : recréez à partir de l'image avec les drapeaux d'origine. Voici une recette :
# Arrêter et supprimer le conteneur
docker stop web && docker rm web
# Recréer sans limite mémoire
docker run -d --name web --cpus=1.5 nginx
Vérifications de récupération
Après le retour en arrière, vérifiez :
- Le conteneur est en cours d'exécution :
docker psmontre le conteneur. - L'utilisation des ressources est normale :
docker statsmontre des valeurs attendues. - Les points de terminaison de santé de l'application répondent (par exemple,
curl http://localhost/healthrenvoie 200). - Les métriques au niveau de l'hôte (CPU, mémoire, E/S disque) sont dans la plage normale.
De plus, surveillez les métriques au niveau de l'hôte pour vous assurer qu'aucun autre conteneur n'est affecté.
Liste de contrôle des opérations
Utilisez cette liste de contrôle pour les opérations de routine et les revues :
- [ ] Confirmer la version Docker et le support cgroup (
docker info). - [ ] Inventorier tous les conteneurs de production et leurs utilisations de ressources de référence.
- [ ] Définir des niveaux de ressources (garanties, burst, best-effort) et les limites correspondantes.
- [ ] Appliquer les limites en utilisant les drapeaux
docker rundocumentés ou les fichiers Compose. - [ ] Vérifier les limites avec
docker inspectetdocker stats. - [ ] Tester les limites sous charge pour assurer aucune terminaison inattendue.
- [ ] Mettre en place la surveillance et les alertes pour l'utilisation des ressources (par exemple, mémoire > 80 % de la limite).
- [ ] Documenter les procédures de récupération pour les kills OOM et la limitation.
- [ ] Planifier des revues périodiques pour ajuster les limites en fonction des charges de travail changeantes.
- [ ] Maintenir les images de conteneurs et Docker Engine à jour pour bénéficier des améliorations de gestion des ressources.
- [ ] Former les membres de l'équipe sur les configurations des limites de ressources et le dépannage.
Table de définition des niveaux de ressources
| Niveau | Services d'exemple | Limite CPU (--cpus) | Limite mémoire | Parts CPU | Notes |
|---|---|---|---|---|---|
| Critique | PostgreSQL, Redis | 2.0 | 2 Go | 2048 | Ressources garanties ; aucun compromis |
| Normal | Frontend web, API | 0.5 | 512 Mo | 1024 | Adéquat pour une charge typique |
| Best-effort | Tâches batch, analytique | 0.25 | 256 Mo | 512 | Peut être limité ; peut être tué sous pression |
Configuration de la surveillance et des alertes
Utilisez un outil de surveillance comme Prometheus, Datadog ou cAdvisor pour collecter les métriques des conteneurs. Définissez des alertes basées sur des seuils :
- Mémoire : Alerter lorsque l'utilisation de la mémoire du conteneur > 80 % de la limite pendant plus de 5 minutes.
- CPU : Alerter lorsque le nombre de limitations CPU augmente (temps limité dans
cpu.statdu cgroup) ou que l'utilisation CPU est proche de la limite pendant une période prolongée. - OOM : Alerter sur tout événement de kill OOM (code de sortie 137 du conteneur ou compteur
oom_killdansmemory.eventsdu cgroup). - E/S disque : Alerter lorsque le temps d'attente blkio est élevé ou que le débit approche de la limite.
Exemple de règle d'alerte Prometheus pour la mémoire :
groups:
- name: docker
rules:
- alert: ContainerMemoryHigh
expr: (container_memory_usage_bytes / container_spec_memory_limit_bytes) > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "Conteneur {{ $labels.name }} utilisation mémoire supérieure à 80 %"
Tâches de maintenance
- Revoir régulièrement
docker statspour identifier les conteneurs approchant des limites. - Mettre à jour les fichiers Compose dans un contrôle de version, pas seulement en production.
- Après les déploiements, confirmer que les nouveaux conteneurs ont des limites appliquées automatiquement.
- Tester la restauration de la configuration depuis le contrôle de version.
- Tenir un journal d'audit des changements de limites de ressources.
Sauvegardes
Les configurations de ressources doivent être stockées en tant qu'infrastructure-as-code (fichiers Compose ou scripts) et versionnées. Sauvegardez ces fichiers avec les autres configurations.
Exemple de flux de travail Git :
# Valider les modifications du fichier Compose
git add docker-compose.yml
git commit -m "Mettre à jour les limites de ressources pour le service db : mémoire 2g, cpus 2.0"
git push origin main
Stockez les sauvegardes dans un dépôt distant et envisagez des sauvegardes automatisées de tout le répertoire de configuration.
Conclusion
Les limites de ressources Docker sont essentielles pour la stabilité en production. En suivant cette liste de contrôle, vous pouvez systématiquement appliquer, vérifier et maintenir des limites pour prévenir les conflits de ressources et les pannes. Commencez par un pilote sur un petit ensemble de conteneurs, mesurez l'impact et étendez progressivement. Documentez vos configurations et gardez-les sous contrôle de version. Avec ces pratiques, vos applications conteneurisées fonctionneront de manière prévisible et efficace.
Rappelez-vous : les limites de ressources ne sont pas à configurer et à oublier. Surveillez, examinez et ajustez en continu en fonction des modèles de charge de travail réels. Une stratégie solide de gestion des ressources est un élément clé d'une plateforme de conteneurs fiable.