Introduction
Lorsqu'un service conteneurisé consomme plus de CPU ou de mémoire que prévu, il peut ralentir toutes les autres charges de travail sur le même hôte. Docker fournit des contrôles intégrés pour ces situations : les limites de ressources. Pourtant, de nombreux développeurs et ingénieurs DevOps ne s'exercent jamais à définir et vérifier ces limites jusqu'à ce qu'un incident les y oblige. Cet article comble cette lacune avec un laboratoire local que vous pouvez exécuter sur votre propre machine. Vous créerez un environnement sûr, appliquerez des limites de CPU et de mémoire, et confirmerez le comportement avec des commandes concrètes et des sorties attendues.
Le laboratoire s'adresse aux développeurs, aux consultants DevOps et aux équipes techniques de startups qui ont besoin de comprendre la gestion des ressources Docker de manière pratique. Il relie la configuration, les tests, les exemples et le développement avec des commandes pratiques, des signaux d'échec et des décisions de récupération. L'objectif est la sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, utiliser des espaces réservés au lieu de secrets, vérifier le résultat et documenter comment récupérer si l'état attendu n'est pas atteint.
Inventaire de la version et de l'environnement
Avant d'appliquer une limite de ressources, sachez exactement quelle version de Docker vous utilisez et quels conteneurs sont déjà actifs. Cela évite de modifier le mauvais conteneur ou d'appliquer des options non prises en charge par votre version de Docker.
Commencez par une observation en lecture seule de votre environnement :
docker version
La sortie attendue comprend une section Client et Serveur avec les numéros de version. Pour les limites de ressources, vous avez besoin de Docker Engine 19.03 ou version ultérieure sous Linux, ou de Docker Desktop 2.0 ou version ultérieure sous Mac et Windows. Les versions antérieures ne prennent pas en charge toutes les options telles que --cpus ou --memory-swap.
Ensuite, voyez ce qui est en cours d'exécution :
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
Cela affiche les noms des conteneurs, leur statut et les mappages de ports. Notez les noms car vous les utiliserez dans les commandes ultérieures.
Vérifiez les journaux récents pour détecter des signes de contention de ressources :
docker logs <nom-du-conteneur> --tail 100
Recherchez des messages tels que OOMKilled, cannot allocate memory ou des redémarrages répétés. Cela indique que des limites de mémoire peuvent être nécessaires.
Pour une inspection plus approfondie, utilisez :
docker inspect <nom-du-conteneur>
Cela renvoie un grand objet JSON. Concentrez-vous sur la section HostConfig, qui montre les limites de ressources actuelles.
Une erreur courante consiste à sauter cet inventaire et à appliquer directement une limite à un conteneur qui n'en a pas besoin. Passez deux minutes ici ; vous éviterez de modifier la mauvaise charge de travail.
Chemin de configuration sûr
Modifiez toujours un conteneur à la fois. Avant de changer quoi que ce soit, enregistrez l'état actuel pour pouvoir revenir en arrière. Docker ne conserve pas l'historique des commandes docker run, alors sauvegardez votre commande d'origine ou votre fichier Compose.
Par exemple, si vous exécutez un conteneur de serveur web nommé web, capturez d'abord ses limites actuelles :
docker inspect web | jq '.[0].HostConfig | {CpuPeriod, CpuQuota, Memory, MemorySwap}'
Si vous n'avez pas jq, utilisez docker inspect web et recherchez ces champs manuellement.
Ensuite, appliquez un petit changement et vérifiez-le. Pour un conteneur qui nécessite une limite de CPU, utilisez :
docker run -d --name test-app --cpus="1.5" nginx
Cette commande exécute un conteneur Nginx avec accès à 1,5 cœur de CPU. L'option --cpus est une commodité qui définit à la fois CpuPeriod et CpuQuota de manière appropriée.
Pour limiter la mémoire, utilisez :
docker run -d --name test-app --memory="512m" --memory-swap="512m" nginx
Définir memory-swap égal à memory désactive complètement l'espace d'échange. Si vous omettez memory-swap, le conteneur peut utiliser deux fois la limite de mémoire en espace d'échange.
Vérifiez maintenant que les limites ont pris effet :
docker inspect test-app | grep -E '"Memory"|"CpuQuota"'
Vous devriez voir les valeurs que vous avez définies.
Vérification et diagnostics
Après avoir défini les limites, confirmez qu'elles sont appliquées, pas seulement configurées. Exécutez une charge de travail à l'intérieur du conteneur qui tente de dépasser la limite et observez ce qui se passe.
Pour les limites de CPU, utilisez un outil qui sollicite tous les cœurs disponibles. À l'intérieur du conteneur, exécutez :
docker exec -it test-app bash
apt-get update && apt-get install -y stress
stress --cpu 4 --timeout 10
Si le conteneur est limité à 1,5 CPU, le processus de stress sera limité. Vous pouvez vérifier l'utilisation du CPU du conteneur depuis l'extérieur :
docker stats test-app
Pendant que le test de stress s'exécute, la colonne CPU % devrait afficher environ 150 % (1,5 cœur), pas 400 %.
Pour les limites de mémoire, exécutez un processus qui alloue plus que la limite. À l'intérieur du conteneur :
stress --vm 1 --vm-bytes 600M --timeout 10
Comme la limite est de 512 Mo, le tueur OOM (Out-Of-Memory) devrait terminer le processus. Vous pouvez voir le résultat en vérifiant les journaux du conteneur :
docker logs test-app
Recherchez les messages oom-kill.
Un autre diagnostic consiste à inspecter l'état du conteneur après le test :
docker inspect test-app --format='{{.State.OOMKilled}}'
Cela devrait afficher true.
Modes de défaillance et récupération
Les limites de ressources peuvent provoquer des défaillances de plusieurs manières. Voici les modes de défaillance courants et comment récupérer de chacun.
Conteneur tué par OOM
Si un conteneur dépasse sa limite de mémoire, le tueur OOM du noyau peut arrêter le processus. Le conteneur reste en cours d'exécution mais le processus principal se termine. Vous pourriez voir des compteurs de redémarrage élevés dans docker ps.
Récupération :
- Augmentez la limite de mémoire si l'application a légitimement besoin de plus.
- Optimisez l'application pour qu'elle utilise moins de mémoire.
- Assurez-vous que le conteneur dispose de suffisamment d'espace d'échange si nécessaire.
Exemple :
docker run -d --name web --memory="1g" --memory-swap="2g" nginx
Cela permet 1 Go de RAM et 1 Go d'espace d'échange.
Limitation du CPU
Un conteneur limité à moins de CPU que sa charge de travail n'exige sera limité. Cela provoque une latence élevée et de mauvaises performances, mais le conteneur ne plante pas.
Récupération :
- Augmentez la limite de CPU.
- Ajustez l'application pour qu'elle soit moins gourmande en CPU.
- Envisagez une mise à l'échelle horizontale avec plus de conteneurs derrière un équilibreur de charge.
Exemple :
docker run -d --name api --cpus="0.5" my-api
Si l'API est lente, passez à --cpus="2" et testez à nouveau.
Limites d'E/S disque insuffisantes
Docker peut également limiter les E/S de bloc avec --blkio-weight et --device-read-bps, mais ceux-ci sont moins couramment utilisés. Une limite trop basse peut provoquer des délais d'attente.
Récupération :
- Ajustez les limites d'E/S en fonction des exigences de l'application.
- Surveillez les E/S disque avec
docker statsou des outils de l'hôte.
Limites mal configurées
Parfois, une limite est définie si basse que le conteneur ne peut pas démarrer ou devient inutilisable. Par exemple, --memory="4m" peut être trop petit pour l'image de base.
Récupération :
- Supprimez le conteneur et recréez-le avec des limites corrigées.
- Utilisez la commande d'origine ou le fichier Compose que vous avez sauvegardé plus tôt.
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle pour garantir une gestion sûre et efficace des limites de ressources dans votre laboratoire local et vos environnements de type production.
- [ ] Enregistrer l'état actuel : Avant de modifier une limite, sauvegardez la configuration actuelle du conteneur avec
docker inspectet enregistrez la commandedocker rund'origine ou le fichier Compose. - [ ] Identifier le responsable : Attribuez une personne responsable des décisions concernant les limites de ressources. Par exemple, le responsable DevOps possède la politique de limites par défaut.
- [ ] Définir les limites progressivement : Commencez par une limite généreuse, observez les performances, puis resserrez si nécessaire.
- [ ] Tester après le changement : Exécutez un test de stress à l'intérieur du conteneur et confirmez la limitation ou le comportement OOM comme prévu.
- [ ] Documenter les limites : Ajoutez les limites de ressources à vos fichiers de configuration de conteneurs ou fichiers Compose, pas seulement comme indicateurs de ligne de commande.
- [ ] Surveiller régulièrement : Utilisez
docker statsou un outil de surveillance pour suivre l'utilisation des ressources au fil du temps. Ajustez les limites mensuellement en fonction des données. - [ ] Examiner et ajuster : Planifiez un examen trimestriel de toutes les limites de conteneurs pour vous assurer qu'elles correspondent toujours aux besoins des applications.
Pièges courants
Même les ingénieurs expérimentés trébuchent avec les limites de ressources Docker. Voici les erreurs fréquentes et comment les éviter.
Oublier de définir des limites
Par défaut, les conteneurs Docker n'ont aucune contrainte de ressources. Un processus incontrôlable peut affamer d'autres conteneurs et même l'hôte. Pourquoi cela arrive : les limites de ressources ne sont pas obligatoires, et les développeurs se concentrent souvent d'abord sur la fonctionnalité.
Comment éviter : En production, définissez toujours des limites de mémoire et de CPU. Pour les tests locaux, définissez des limites pour imiter les contraintes de production.
Confondre mémoire et mémoire-échange
Beaucoup pensent que --memory seul inclut l'espace d'échange. En réalité, il ne définit que la RAM. Si vous ne spécifiez pas --memory-swap, Docker définit l'échange à deux fois la mémoire. Cela peut conduire à des tues inattendues par OOM lorsque l'échange est épuisé.
Comment éviter : Soyez explicite. Si vous ne voulez pas d'échange, définissez --memory-swap égal à --memory. Si vous voulez un certain échange, définissez-le exactement.
Ne pas tester avec des charges de travail réalistes
Définir des limites puis exécuter une commande triviale ne prouve pas que le conteneur survivra sous charge. Pourquoi : les tests légers n'atteignent pas les limites.
Comment éviter : Utilisez des outils de stress ou reproduisez les modèles de trafic réels avant de déclarer le succès.
Ignorer les limites du noyau de l'hôte
Les limites Docker fonctionnent au-dessus des capacités du noyau de l'hôte. Si l'hôte a un vm.max_map_count bas ou d'autres paramètres, les conteneurs peuvent échouer malgré des limites adéquates. Pourquoi : les paramètres du noyau Linux affectent tous les processus.
Comment éviter : Vérifiez les paramètres sysctl de l'hôte et ajustez si nécessaire. Par exemple, vm.max_map_count devrait être au moins 262144 pour certaines bases de données.
Limiter excessivement et provoquer de mauvaises performances
Définir des limites trop basses est aussi mauvais que de ne pas en définir du tout. Le conteneur peut être limité en permanence, provoquant des temps de réponse lents. Pourquoi : les équipes réagissent souvent de manière excessive à un incident passé et resserrent trop.
Comment éviter : Évaluez l'utilisation réelle des ressources de votre application et définissez des limites avec une marge de 20 à 30 % au-dessus du pic.
Conclusion
Les limites de ressources Docker sont un outil essentiel pour des opérations de conteneurs stables. Mais elles ne sont utiles que si chaque limite est versionnée, observable et réversible. Copier une commande sans vérifier les prérequis et la sortie attendue n'est pas une procédure d'exploitation.
Comme prochaine étape, créez votre propre laboratoire local. Utilisez les commandes et les exemples de cet article pour définir une limite de mémoire sur un conteneur de test, exécutez un test de stress et observez le comportement OOM. Ensuite, expérimentez avec différentes valeurs de limites et voyez comment le système réagit. Enfin, appliquez ce que vous avez appris à un conteneur réel dans un environnement de développement, et documentez les nouvelles limites dans vos fichiers de configuration.
Un flux de travail technique fiable rend les défaillances visibles, protège les valeurs sensibles, limite les modifications à la ressource prévue et définit la vérification de la récupération avant qu'un incident ne force la décision.