## Introduction L'optimisation des performances Docker est un processus systématique d'observation, de mesure et d'ajustement de votre environnement de conteneurs afin d'éliminer les goulots d'étranglement, de réduire la latence et de maximiser l'efficacité des ressources. Plutôt que de deviner les correctifs, ce guide présente des étapes concrètes et pratiques pour diagnostiquer et résoudre les problèmes de performance dans les conteneurs Docker et les déploiements Docker Compose. Que vous soyez un développeur exécutant une pile locale, un ingénieur DevOps gérant des pipelines CI/CD ou un fondateur technique faisant évoluer une startup, comprendre comment optimiser les performances Docker peut prévenir les pannes et améliorer l'expérience de l'utilisateur final. Cet article fournit des exemples clairs et exploitables que vous pouvez exécuter en toute sécurité dans votre propre environnement. Nous couvrirons les domaines essentiels : l'inventaire de votre environnement, la modification sécuritaire de la configuration, la vérification des changements à l'aide de commandes de diagnostic, la gestion des modes de défaillance et la mise en œuvre d'une liste de contrôle opérationnelle. Chaque recommandation est assortie de prérequis, de sorties attendues et d'étapes de récupération, afin que vous puissiez l'appliquer en toute confiance. ## Étape 1 : Inventaire des versions et de l'environnement Avant de commencer à optimiser, sachez exactement ce que vous exécutez. Commencez par collecter les informations de version et d'environnement. Cela vous donne une base de référence à comparer après avoir apporté des modifications, et vous aide à identifier les problèmes de compatibilité. Exécutez les commandes en lecture seule suivantes pour capturer votre configuration Docker : # Afficher les versions du client et du serveur Docker docker version # Afficher des informations détaillées à l'échelle du système docker info # Concentrez-vous sur Storage Driver, Cgroup Driver, Kernel Version et Total Memory Exemple de sortie de docker version (tronquée pour plus de clarté) : Client: Docker Engine - Community Version: 24.0.5 API version: 1.43 Go version: go1.20.6 Git commit: ced0996 Built: Fri Jul 21 20:35:18 2023 OS/Arch: linux/amd64 Context: default Server: Docker Engine - Community Engine: Version: 24.0.5 API version: 1.43 (minimum version 1.12) Go version: go1.20.6 Git commit: a61e2b4 Built: Fri Jul 21 20:35:18 2023 OS/Arch: linux/amd64 Experimental: false À partir de docker info , vérifiez ces champs : Storage Driver: overlay2 Cgroup Driver: cgroupfs Kernel Version: 5.15.0-79-generic Total Memory: 15.62GiB Si vous voyez Storage Driver: vfs ou Cgroup Driver: none , vous pourriez rencontrer des problèmes de performance. overlay2 est recommandé sur les noyaux Linux modernes. Le pilote cgroup doit correspondre à votre système d'initialisation (par exemple, systemd pour les distributions basées sur systemd). Listez tous les conteneurs en cours d'exécution avec un format de tableau concis : docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" Exemple de sortie : NAMES IMAGE STATUS PORTS web nginx:1.25 Up 2 hours 0.0.0.0:80->80/tcp db postgres:15 Up 2 hours 5432/tcp cache redis:7 Up 2 hours 6379/tcp Pour chaque conteneur, capturez les détails pertinents avec docker inspect : docker inspect Cela renvoie un objet JSON avec les montages, les paramètres réseau, les variables d'environnement, les limites de ressources et l'état de santé. Enregistrez cette sortie dans un fichier avant d'apporter des modifications : docker inspect web > web-inspect-avant.json Si vous utilisez Docker Compose, exécutez les commandes équivalentes : docker compose version docker compose ps docker compose images La première affiche la version de Compose (par exemple, Docker Compose version v2.20.2 ). La seconde liste les services et leurs états. Avec cet inventaire, vous pouvez identifier quel conteneur ou service nécessite une attention particulière. ## Étape 2 : Identifier les goulots d'étranglement de performance Les problèmes de performance se manifestent souvent par une utilisation élevée du CPU, une pression mémoire, des E/S lentes ou une latence réseau. Utilisez les statistiques intégrées de Docker pour voir la consommation de ressources en direct : docker stats --no-stream Exemple de sortie : CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 9d1f2c3e4b5a web 95.20% 420MiB / 1GiB 41.02% 1.2MB / 850kB 12MB / 0B 5 7e2a1b3c4d5e db 35.10% 1.1GiB / 2GiB 55.00% 400kB / 250kB 150MB / 30MB 7 Recherchez les conteneurs constamment proches de 100% de CPU ou avec un pourcentage de mémoire élevé. Utilisez docker top pour voir les processus à l'intérieur d'un conteneur spécifique : docker top web Cela affiche le PID, l'utilisateur, le CPU et la mémoire par processus, vous aidant à identifier quel processus consomme des ressources. Vérifiez les journaux pour détecter des modèles d'erreur ou des requêtes lentes : docker logs web --tail 100 --timestamps Pour les applications web, inspectez les temps de réponse depuis l'intérieur du conteneur. Si vous n'avez pas de shell dans l'image, utilisez docker exec pour exécuter des vérifications simples : docker exec web sh -c "time wget -q -O /dev/null http://localhost/health" Si la commande n'est pas disponible, installez ou utilisez des outils alternatifs. La sortie montre le temps réel, utilisateur et système, vous donnant une idée de la latence interne. Pour diagnostiquer les performances du système de fichiers, attachez-vous au conteneur et effectuez un test d'écriture : docker exec web sh -c "dd if=/dev/zero of=/tmp/fichier_test bs=1M count=100 oflag=direct && rm /tmp/fichier_test" Cela écrit 100 Mo directement dans le système de fichiers du conteneur. Comparez la vitesse rapportée avec la vitesse du disque de l'hôte. Si elle est significativement plus lente, vous pourriez avoir un problème de pilote de stockage ou le conteneur écrit sur un montage bind lent. ## Étape 3 : Ajuster les limites de ressources De nombreux problèmes de performance proviennent de limites de ressources manquantes ou incorrectes. Par défaut, un conteneur peut utiliser toutes les ressources disponibles de l'hôte, ce qui peut entraîner une contention. Définissez des limites explicites de CPU et de mémoire pour assurer un partage équitable et empêcher un seul conteneur d'affamer les autres. Dans Docker Compose, ajoutez la section deploy pour Docker Swarm ou la section resources (pour le mode non-swarm, incluse dans les versions plus récentes de Compose) sous votre service : services: web: image: nginx:1.25 ports: - "80:80" deploy: resources: limits: cpus: '0.50' memory: 512M reservations: cpus: '0.25' memory: 128M Si vous utilisez Docker simple, passez les drapeaux directement : docker run -d --name web --cpus="0.50" --memory="512m" nginx:1.25 Après avoir défini les limites, vérifiez avec docker stats ou docker inspect : docker inspect web --format '{{.HostConfig.NanoCpus}} {{.HostConfig.Memory}}' Exemple de sortie : 500000000 536870912 NanoCpus de 500 millions équivaut à 0,5 cœur de CPU ; une mémoire de 536870912 octets équivaut à 512 Mio. Si l'application dépasse la limite, Docker limite le CPU et peut tuer le conteneur s'il dépasse la mémoire. Surveillez les événements OOM (out of memory) dans docker events : docker events --filter 'event=oom' --since 24h Si des événements OOM apparaissent, ajustez la limite avec précaution. Envisagez également de définir memswap_limit si vous souhaitez contrôler l'utilisation de la mémoire d'échange. Par exemple, --memory="512m" --memory-swap="1g" autorise 512 Mio de RAM plus 512 Mio de swap. ## Étape 4 : Optimiser l'image et les couches Les images volumineuses ralentissent les téléchargements, les déploiements et les E/S disque. Inspectez l'historique et la taille de votre image : docker images # Exemple de sortie REPOSITORY TAG IMAGE ID CREATED SIZE nginx 1.25 a6bd71f48f68 2 weeks ago 187MB myapp latest 123456789abc 1 hour ago 1.2GB Une image de 1,2 Go pour une application simple indique un gonflement inutile. Utilisez docker history pour voir la taille des couches : docker history myapp:latest Exemple de sortie : IMAGE CREATED CREATED BY SIZE COMMENT 123456789abc 1 hour ago /bin/sh -c #(nop) COPY . /app 400MB 987654321def 1 hour ago /bin/sh -c apt-get update && apt-get install … 600MB Réduisez la taille de l'image en : - Utilisant une image de base plus petite (par exemple, alpine au lieu de ubuntu ). - Combinant les commandes RUN pour réduire les couches (par exemple, apt-get update && apt-get install -y package && rm -rf /var/lib/apt/lists/* ). - Excluant les fichiers inutiles avec un fichier .dockerignore (par exemple, node_modules , .git , les journaux). Après la reconstruction, vérifiez la nouvelle taille : docker build -t myapp:optimized . docker images myapp Si la taille diminue considérablement, le temps de téléchargement et la vitesse de déploiement s'améliorent. ## Étape 5 : Surveiller et vérifier La surveillance continue est essentielle pour détecter les régressions de performance. Utilisez le point de terminaison de métriques intégré de Docker si vous exécutez avec des fonctionnalités expérimentales ou fiez-vous à docker stats dans un script de sondage. Voici une simple boucle bash pour journaliser le CPU et la mémoire toutes les 5 secondes : while true; do docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" >> docker-stats.log sleep 5 done Examinez le journal périodiquement. Pour une solution plus robuste, intégrez Prometheus et cAdvisor. cAdvisor collecte les métriques des conteneurs et les expose sur le port 8080. Exécutez cAdvisor : docker run -d --name cadvisor -p 8080:8080 \ -v /:/rootfs:ro \ -v /var/run:/var/run:ro \ -v /sys:/sys:ro \ -v /var/lib/docker/:/var/lib/docker:ro \ google/cadvisor:latest Configurez ensuite Prometheus pour scraper cAdvisor. Cela vous donne des données historiques et des alertes. ## Étape 6 : Modes de défaillance et récupération Même avec une optimisation minutieuse, les choses peuvent mal tourner. Préparez-vous aux modes de défaillance courants et sachez comment récupérer. ### Le conteneur ne démarre pas après les limites de ressources Si vous définissez une limite de mémoire trop basse, l'application peut planter immédiatement. Vérifiez le code de sortie et les journaux : docker ps -a --filter "name=web" # Recherchez le code de sortie 137 (indiquant un kill OOM) docker logs web Si OOM l'a tué, augmentez la limite de mémoire. Mettez à jour le fichier Compose ou la commande d'exécution, puis redémarrez : docker compose up -d --force-recreate web ### Dégradation des performances après la mise à jour de l'image Si une nouvelle version d'image provoque une lenteur, revenez à l'image de travail précédente. Gardez des versions étiquetées dans votre registre. Pour revenir en arrière : docker service update --image myapp:1.0.3 myapp # Ou dans Compose : # changez la balise d'image dans docker-compose.yml pour la version précédente, puis : docker compose up -d Si l'image n'est pas disponible localement, téléchargez-la d'abord : docker pull myapp:1.0.3 ### Perte de données lors de la suppression du conteneur Si votre conteneur stocke des données importantes dans sa couche inscriptible, la suppression du conteneur supprime ces données. Utilisez toujours des volumes ou des montages bind pour les données persistantes. Pour identifier les volumes utilisés par un conteneur : docker inspect -f '{{range .Mounts}}{{.Name}} {{.Destination}}{{"\n"}}{{end}}' db Exemple de sortie : postgres_data /var/lib/postgresql/data Si aucun volume n'est répertorié pour le répertoire de données, vous risquez une perte de données. Créez un volume et montez-le : services: db: image: postgres:15 volumes: - postgres_data:/var/lib/postgresql/data volumes: postgres_data: Recréez ensuite le conteneur et vérifiez que les données persistent après le redémarrage : docker compose down docker compose up -d docker compose exec db ls /var/lib/postgresql/data ## Étape 7 : Liste de contrôle opérationnelle Utilisez cette liste de contrôle pour garantir une optimisation des performances sûre et efficace : - Base de référence : Enregistrez docker version , docker info , l'utilisation des ressources des conteneurs ( docker stats --no-stream ) et les fichiers de configuration pertinents avant les modifications. - Changez une chose à la fois : Modifiez un seul paramètre (par exemple, la limite de CPU) et observez l'effet. - Documentez : Notez ce qui a été changé, pourquoi et l'impact attendu. - Vérifiez : Après avoir appliqué le changement, exécutez les mêmes commandes de diagnostic et comparez les résultats. - Surveillez : Configurez des alertes pour les métriques critiques (par exemple, CPU > 85 %, mémoire > 90 %). - Plan de retour en arrière : Ayez la configuration ou l'image précédente prête à revenir si nécessaire. - Testez la récupération : Simulez une panne en arrêtant un conteneur et en vous assurant qu'il redémarre avec les données intactes. ## Conclusion L'optimisation des performances Docker est une pratique continue, pas une tâche ponctuelle. En suivant les étapes de ce guide, vous pouvez systématiquement identifier et résoudre les goulots d'étranglement, définir des limites de ressources appropriées, optimiser les images et établir des procédures fiables de surveillance et de récupération. Commencez par un changement à faible risque, comme définir une limite de CPU sur un conteneur non critique. Observez les résultats, documentez le résultat et appliquez progressivement d'autres optimisations. Avec une approche disciplinée, vous pouvez maintenir un environnement Docker haute performance qui soutient vos applications et votre équipe. Pour approfondir, explorez la documentation officielle de Docker sur les contraintes de ressources, les pilotes de stockage et les meilleures pratiques pour la construction d'images. N'oubliez pas que l'objectif n'est pas seulement la vitesse, mais la stabilité, l'observabilité et la récupérabilité.