Introduction
Les conteneurs Docker partagent le noyau et les ressources de l'hôte. Sans limites explicites, un seul conteneur peut épuiser la mémoire ou le CPU, provoquant une instabilité pour les autres services ou l'hôte lui-même. Les limites de ressources sont un élément essentiel pour exécuter des conteneurs en toute sécurité, que ce soit en développement ou en production.
Cependant, définir ces limites conduit souvent à des erreurs déroutantes : conteneurs tués avec le code de sortie 137, processus incapables d'allouer de la mémoire, ou limitation du CPU entraînant des performances médiocres. Des limites mal configurées peuvent également empêcher les conteneurs de démarrer ou les faire se comporter de manière imprévisible.
Ce guide couvre les erreurs les plus courantes liées aux limites de ressources Docker, comment les diagnostiquer et les corriger. Nous nous concentrerons sur les limites de mémoire et de CPU, avec des notes sur les limites PID. Vous apprendrez à inspecter les paramètres actuels, à appliquer des configurations sûres, à les vérifier et à récupérer après un échec.
Inventaire des versions et de l'environnement
Avant de modifier les limites de ressources, confirmez votre environnement.
Vérifier la version de Docker
Les fonctionnalités de limites de ressources dépendent de la version de Docker et du système d'initialisation sous-jacent. Utilisez :
docker version
La sortie attendue inclut les versions client et serveur. Par exemple :
Client: Docker Engine - Community
Version: 24.0.7
API version: 1.43
Go version: go1.20.10
Git commit: 311b9ff
Built: Thu Oct 26 09:08:15 2023
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 24.0.7
API version: 1.43 (minimum version 1.12)
Go version: go1.20.10
Git commit: 311b9ff
Built: Thu Oct 26 09:08:15 2023
OS/Arch: linux/amd64
Experimental: false
Vérifier la prise en charge du noyau
Les limites de mémoire reposent sur les cgroups. Vérifiez que cgroup v2 ou v1 est actif :
docker info | grep -i cgroup
Sur les systèmes modernes, vous pourriez voir :
Cgroup Driver: systemd
Cgroup Version: 2
Si cgroup v2 n'est pas pris en charge, les limites de mémoire risquent de ne pas être appliquées correctement.
Identifier les conteneurs en cours d'exécution
Listez les conteneurs et leur utilisation actuelle des ressources :
docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Status}}"
Cela donne un aperçu rapide.
Prérequis
Vous aurez besoin de :
- Docker Engine 20.10 ou plus récent (pour une prise en charge fiable de cgroup v2)
- Des autorisations appropriées pour exécuter les commandes Docker (généralement root ou le groupe docker)
- Une image de conteneur de test (par exemple,
alpineouubuntu)
Chemin de configuration sécurisé
Les limites de ressources peuvent être définies au moment de l'exécution du conteneur, dans les fichiers Docker Compose, ou en mettant à jour des conteneurs existants. Nous couvrirons chaque cas avec des exemples.
Options d'exécution
La commande docker run accepte --memory (ou -m) pour les limites de mémoire et --cpus pour les limites CPU.
Exemple de limite de mémoire :
docker run -d --name limited-nginx --memory 256m nginx
Cela limite le conteneur à 256 Mo de RAM. Si le conteneur dépasse cette limite, le noyau peut tuer le processus à l'intérieur du conteneur, et le conteneur se terminera avec le code 137.
Exemple de limite CPU :
docker run -d --name limited-cpu --cpus 1.5 nginx
Cela permet au conteneur d'utiliser jusqu'à 1,5 cœur CPU. Si le conteneur tente d'utiliser davantage, il sera limité.
Limites combinées :
docker run -d --name limited-app --memory 512m --memory-swap 1g --cpus 2 myapp
L'option --memory-swap définit le total mémoire+swap. Si vous définissez --memory et --memory-swap à la même valeur, le swap est désactivé.
Limite de PID :
docker run -d --name limited-pids --pids-limit 100 nginx
Cela limite le nombre de processus à l'intérieur du conteneur.
Configuration Docker Compose
Dans un fichier docker-compose.yml, utilisez la section deploy.resources pour Docker Swarm ou les options de niveau supérieur resources (dans Compose v2.x avec docker compose et le mode non-swarm) ou les anciens champs mem_limit, cpus.
Exemple moderne Compose (v2.x) :
services:
web:
image: nginx
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Remarque : La section deploy est utilisée par Docker Swarm et par docker compose avec le drapeau --compatibility ou dans les versions récentes avec docker compose up. Pour le développement local, vous pouvez utiliser les anciennes options plus simples :
services:
web:
image: nginx
mem_limit: 512m
cpus: 0.5
Mise à jour des conteneurs existants
Vous ne pouvez pas modifier les limites de ressources d'un conteneur en cours d'exécution directement. Vous devez arrêter le conteneur, le recréer avec de nouvelles limites, ou utiliser docker update pour certains paramètres.
Utilisation de docker update :
docker update peut modifier la mémoire, le CPU et d'autres limites sur un conteneur en cours d'exécution sans redémarrage. Par exemple :
docker update --memory 512m --cpus 1 mycontainer
docker update --memory-swap -1 mycontainer
Le -1 pour memory-swap le réinitialise à un swap illimité (mais la limite de mémoire reste).
Cependant, certains changements peuvent nécessiter un redémarrage si le processus d'initialisation du conteneur doit voir les nouvelles limites.
Vérification et diagnostic
Après avoir défini les limites, vérifiez qu'elles sont appliquées et testez leur comportement.
Inspecter les limites du conteneur
Utilisez docker inspect pour voir les limites effectives :
docker inspect limited-nginx --format '{{.HostConfig.Memory}}'
La sortie sera le nombre d'octets, par exemple 268435456 pour 256 Mo.
Pour le CPU :
docker inspect limited-cpu --format '{{.HostConfig.NanoCpus}}'
La sortie sera 1500000000 pour 1,5 CPU (puisque 1 CPU = 1e9 NanoCpus).
Vérifier les statistiques d'exécution
Utilisez docker stats pour observer l'utilisation en temps réel :
docker stats limited-nginx --no-stream
Sortie :
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
abc123def456 limited-nginx 0.00% 2.5MiB / 256MiB 0.98% 0B / 0B 0B / 0B 5
La colonne MEM USAGE / LIMIT montre l'utilisation actuelle par rapport à la limite.
Tester les limites de mémoire
Pour voir comment un conteneur se comporte lorsqu'il dépasse les limites de mémoire, exécutez un conteneur avec une limite de mémoire, puis exécutez une commande consommatrice de mémoire à l'intérieur.
docker run -d --name mem-test --memory 128m alpine sleep 300
docker exec mem-test sh -c "tail /dev/zero"
La commande tail /dev/zero consommera rapidement de la mémoire. Après quelques secondes, vérifiez l'état du conteneur :
docker ps -a | grep mem-test
Vous devriez voir que le conteneur s'est terminé avec le code 137.
Vérifiez les journaux :
docker logs mem-test
Il se peut qu'ils ne montrent rien car le processus a été tué par le tueur OOM. Sur l'hôte, vous pouvez vérifier dmesg pour les messages OOM.
Tester les limites CPU
Pour observer la limitation du CPU, exécutez une tâche intensive en CPU avec une limite CPU basse :
docker run -d --name cpu-test --cpus 0.5 alpine sh -c "while true; do :; done"
Ensuite, surveillez l'utilisation du CPU :
docker stats cpu-test --no-stream
Vous devriez voir un pourcentage CPU autour de 50 % (0,5 CPU). Si vous vérifiez les statistiques CPU du cgroup du conteneur, vous verrez des compteurs de limitation.
Modes d'échec et récupération
Comprendre les erreurs courantes et comment récupérer est essentiel.
Le conteneur se termine avec le code 137
Le code de sortie 137 indique que le conteneur a été tué, généralement à cause d'un OOM (mémoire insuffisante) ou parfois d'une suppression manuelle. Si votre conteneur est tué de manière inattendue, vérifiez les limites de mémoire et l'utilisation.
Diagnostic :
- Vérifiez le code de sortie :
docker inspect <container> --format '{{.State.ExitCode}}' - Vérifiez les journaux pour les messages OOM :
docker logs <container>peut ne pas montrer grand-chose ; vérifiezdmesgoujournalctlsur l'hôte pour les événements OOM. - Inspectez la limite de mémoire :
docker inspect <container> --format '{{.HostConfig.Memory}}' - Vérifiez l'utilisation maximale de la mémoire avant la mort si disponible.
Correctif :
- Augmentez la limite de mémoire si l'application a légitimement besoin de plus de mémoire :
docker update --memory 512m <container>ou recréez avec une limite plus élevée. - Optimisez l'utilisation de la mémoire de l'application (par exemple, corrigez les fuites de mémoire).
- Ajoutez de l'espace swap si approprié : autorisez le swap en définissant
--memory-swapsupérieur à--memory.
Retour en arrière :
Si la limite augmentée cause des problèmes sur l'hôte, revenez à la limite précédente et réduisez l'application ou ajoutez plus de mémoire hôte.
Le conteneur ne démarre pas en raison de limites invalides
Parfois, vous pouvez spécifier des limites invalides, comme --memory 10g sur un hôte avec moins de mémoire. Docker démarrera quand même le conteneur, mais la limite peut ne pas être réalisable. Plus fréquemment, vous pourriez obtenir une erreur comme :
docker: Error response from daemon: invalid memory limit: 0.
Ou si vous spécifiez une limite CPU trop basse, par exemple --cpus 0.001, Docker peut l'accepter mais le conteneur risque d'être affamé.
Diagnostic :
- Vérifiez le message d'erreur exact de
docker run. - Assurez-vous que la limite est un nombre positif et dans les capacités de l'hôte.
Correctif :
- Corrigez la valeur de la limite.
- Sur cgroup v2, évitez de définir des limites de swap sans limites de mémoire, car cela peut causer des erreurs.
Limitation CPU et problèmes de performances
Si votre conteneur n'a pas les performances attendues, il se peut qu'il atteigne les limites CPU. Les symptômes incluent des temps de réponse lents et des métriques de limitation CPU élevées.
Diagnostic :
- Vérifiez les statistiques CPU du conteneur :
docker stats <container>montre le % CPU ; comparez avec la limite. - Inspectez la limitation CPU du cgroup : sur cgroup v2, vérifiez
cat /sys/fs/cgroup/system.slice/docker-<container-id>.scope/cpu.statet regardeznr_throttledetthrottled_time.
Correctif :
- Augmentez la limite CPU si la charge de travail en a besoin.
- Optimisez l'application pour utiliser moins de cycles CPU.
- Ajustez la planification ou la parallélisation.
Limite de PID dépassée
Si un conteneur tente de créer plus de processus que permis, il obtiendra des erreurs comme fork: retry: Resource temporarily unavailable.
Diagnostic :
- Vérifiez les journaux du conteneur pour les erreurs de fork.
- Inspectez la limite PID :
docker inspect <container> --format '{{.HostConfig.PidsLimit}}'(la valeur par défaut est 0, signifiant illimité, mais vous avez peut-être défini une limite). - Vérifiez le nombre actuel de PID :
docker exec <container> ps -e | wc -l
Correctif :
- Augmentez
--pids-limitsi l'application a légitimement besoin de plus de processus. - Réduisez le nombre de processus en optimisant l'application (par exemple, utilisez des threads plutôt que des processus).
Pièges courants et comment les éviter
Les limites de ressources sont puissantes mais faciles à mal configurer. Voici les erreurs fréquentes et comment les éviter ou les récupérer.
Définir des limites trop basses pour la charge de travail
De nombreuses équipes commencent avec des limites basses arbitraires sans profiler l'utilisation réelle, ce qui conduit à des tueries OOM fréquentes ou à une famine CPU.
Pourquoi cela arrive : Manque de compréhension des besoins en ressources de l'application.
Comment éviter : Établissez une base de référence de votre application dans un environnement de staging en utilisant des outils comme docker stats ou la surveillance des performances des applications. Définissez des limites avec au moins 20 à 30 % de marge au-dessus de l'utilisation maximale.
Exemple : Si votre application web atteint un pic à 700 Mo, définissez --memory 1g au lieu de --memory 512m.
Récupération : Augmentez les limites progressivement et surveillez la stabilité.
Ignorer la configuration du swap
Par défaut, Docker peut ne pas autoriser le swap à moins que vous ne le définissiez explicitement. Si votre conteneur a occasionnellement besoin de plus de mémoire que la limite mais que vous voulez éviter les tueries OOM, activer le swap peut aider, mais cela peut ralentir le conteneur.
Pourquoi cela arrive : Mauvaise compréhension de la sémantique de --memory-swap.
Comment éviter : Comprenez que --memory-swap est le total mémoire+swap. Si vous définissez --memory 512m --memory-swap 1g, le conteneur a 512 Mo de RAM et 512 Mo de swap. Si vous définissez les deux à la même valeur, le swap est désactivé.
Récupération : Ajustez --memory-swap avec précaution et testez les performances.
S'appuyer uniquement sur les options d'exécution
Lors de l'utilisation de l'orchestration ou de Compose, définir des limites uniquement via les drapeaux docker run peut conduire à des incohérences lorsque les conteneurs sont recréés.
Pourquoi cela arrive : Les équipes peuvent ne pas avoir de méthode standard pour définir les limites.
Comment éviter : Définissez les limites dans votre fichier Docker Compose ou vos manifestes d'orchestration (Kubernetes, Swarm). Cela garantit que les limites sont versionnées et appliquées de manière cohérente.
Récupération : Refactorisez pour utiliser les configurations Compose ou d'orchestration.
Ne pas surveiller après le déploiement
Définir des limites n'est pas une tâche ponctuelle. Les applications changent et les limites doivent être ajustées.
Pourquoi cela arrive : Manque de surveillance et d'alertes continues.
Comment éviter : Mettez en place une surveillance avec des outils comme Prometheus, Datadog ou cAdvisor. Alertez sur des métriques comme l'utilisation de la mémoire du conteneur approchant de la limite ou la limitation CPU.
Récupération : Examinez et ajustez les limites en fonction des tendances d'utilisation, au moins trimestriellement ou après des changements significatifs de l'application.
Mal comprendre les limites CPU
L'option --cpus de Docker est une limite stricte sur le temps CPU, pas une réservation. Si vous définissez --cpus 1 pour une charge de travail en rafale qui a occasionnellement besoin de 2 CPU, elle sera limitée même si l'hôte a des CPU inactifs.
Pourquoi cela arrive : Confusion entre les limites CPU et les réservations CPU. Docker n'a pas de drapeau de réservation CPU séparé, mais vous pouvez utiliser les parts CPU (--cpu-shares) pour influencer la planification en cas de contention.
Comment éviter : Comprenez le modèle CPU de votre charge de travail. Pour les charges en rafale, envisagez de définir une limite CPU plus élevée ou d'utiliser --cpu-shares pour lui donner plus de poids.
Récupération : Ajustez --cpus ou --cpu-shares en fonction des observations de performances.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle lorsque vous travaillez avec les limites de ressources Docker.
| Étape | Action | Commande / Notes | Responsable | Fréquence |
|---|---|---|---|---|
| 1 | Vérifier la version de Docker et la prise en charge des cgroups | docker version, docker info | grep -i cgroup | Ingénieur DevOps | Mensuellement ou après des changements d'infrastructure |
| 2 | Identifier les conteneurs et l'utilisation actuelle des ressources | docker ps, docker stats | Ingénieur DevOps | Hebdomadaire |
| 3 | Décider des limites en fonction des besoins de la charge de travail | Basé sur le profilage et les tests de l'application | Propriétaire de l'application (par exemple, Priya Shah, responsable d'ingénierie) | À chaque version ou trimestriellement |
| 4 | Appliquer les limites à l'aide des drapeaux docker run ou de Compose | Utiliser --memory, --cpus, --pids-limit | Ingénieur DevOps | À chaque déploiement |
| 5 | Vérifier que les limites sont appliquées | docker inspect pour vérifier HostConfig | Ingénieur DevOps | Immédiatement après le déploiement |
| 6 | Surveiller l'utilisation des ressources et la limitation | docker stats, vérifier les statistiques cgroup | Équipe de surveillance | Continu |
| 7 | Tester les scénarios d'échec (OOM, limitation CPU) | Exécuter des tests de stress dans le staging | Responsable QA | Avant chaque version majeure |
| 8 | Documenter les limites et le raisonnement | Mettre à jour les procédures opérationnelles | Ingénieur DevOps | Lors des changements |
| 9 | Configurer des alertes pour l'utilisation des ressources | Utiliser des outils de surveillance | Équipe de surveillance | Configuration initiale, puis maintenance |
| 10 | Examiner et ajuster les limites périodiquement | Comparer les tendances d'utilisation | Propriétaire de l'application et responsable DevOps | Trimestriel |
Conclusion
Les limites de ressources Docker sont essentielles pour des opérations de conteneurs stables et prévisibles. Les erreurs courantes telles que le code de sortie 137, la limitation CPU et l'épuisement des PID peuvent être diagnostiquées et corrigées avec les bonnes commandes et configurations. Commencez par un pilote restreint pour tester les limites avant un déploiement à grande échelle, et vérifiez toujours avec docker inspect et docker stats. Documentez vos limites et révisez-les à mesure que les besoins des applications évoluent.
En suivant le chemin de configuration sécurisé et en ayant un plan de retour en arrière, vous pouvez éviter la plupart des problèmes liés aux ressources et maintenir vos applications conteneurisées en bon état de fonctionnement.
Ressources supplémentaires :
- Documentation Docker : Options d'exécution avec mémoire, CPU et GPU
- Contraintes de ressources Docker Compose : Référence du fichier Compose