## Introduction L'optimisation des performances de Docker Swarm consiste à passer de symptômes observés à des améliorations vérifiées. Au lieu de deviner les réglages, les opérateurs doivent identifier le composant exact qui cause la latence, mesurer son comportement actuel, effectuer un seul changement ciblé et confirmer le résultat avec des données avant/après. Ce guide s'adresse aux développeurs, ingénieurs DevOps et équipes techniques qui exécutent des charges de travail conteneurisées en production. Il couvre des commandes pratiques, des exemples réalistes et des étapes de récupération pour les goulots d'étranglement courants dans Docker Swarm. Vous apprendrez à inspecter votre cluster, à régler les limites de ressources, à corriger les problèmes de réseau overlay et à diagnostiquer les services lents sans perturber le reste de l'infrastructure. Le principe sous-jacent est la sécurité opérationnelle : observer d'abord, changer une chose à la fois, vérifier avec des données et toujours avoir un plan de retour en arrière. ## Inventaire des versions et de l'environnement Avant d'effectuer des modifications, vous devez avoir une vision claire de ce que vous exécutez. Vérifiez les versions de Docker Engine et de Swarm sur chaque nœud. Exécutez la commande suivante sur chaque manager et worker : ```bash docker version --format '{{.Server.Version}}' docker node ls ``` Sortie attendue sur un cluster sain à trois nœuds : ``` ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION q3n9x... * manager1 Ready Active Leader 24.0.7 k8m2p... worker1 Ready Active 24.0.7 j7l1o... worker2 Ready Active 24.0.7 ``` Si un nœud apparaît comme `Down` ou `Unknown`, investigatez avec `docker node inspect --format '{{.Status}}'` et consultez les journaux du démon Docker sur cet hôte avec `journalctl -u docker.service --since "10 minutes ago"`. Ensuite, inventoriez les services et leur utilisation des ressources : ```bash docker service ls docker service ps --no-trunc docker stats --no-stream ``` La sortie de `docker stats` donne en temps réel l'utilisation CPU, mémoire et réseau par conteneur. Notez tout conteneur proche de sa limite mémoire ou utilisant excessivement le CPU. Pour un aperçu rapide sur l'ensemble du cluster, exécutez `docker node ps $(docker node ls -q)` pour voir toutes les tâches. Confirmez également où se trouvent les données persistantes. Un service peut sembler lent parce que son volume est sur un disque lent ou qu'un montage bind est mal configuré. ```bash docker volume ls docker volume inspect docker service inspect --format '{{json .Spec.TaskTemplate.ContainerSpec.Mounts}}' ``` Si vous voyez un montage bind pointant vers un répertoire qui n'existe pas sur tous les nœuds, les tâches échoueront à se planifier ou s'exécuteront avec des données manquantes. Utilisez des volumes nommés pour le stockage partagé à l'échelle du cluster ou assurez-vous que le chemin du montage bind existe sur chaque nœud éligible. **Exemple pratique :** Dans un Swarm à trois nœuds, un worker exécute un conteneur de base de données. L'application signale des requêtes lentes. `docker stats` montre que le conteneur utilise 95 % de sa limite mémoire de 512 Mo alors que l'hôte dispose de 16 Go libres. La pression mémoire force la base de données à utiliser le swap dans le conteneur. La solution consiste à augmenter la limite mémoire à 2 Go et à ajouter une réservation pour garantir la capacité. Cette observation concrète (95 % d'utilisation) justifie le changement. Avant de modifier les limites de ressources, capturez toujours la spécification actuelle : ```bash docker service inspect myapp_db --pretty ``` Ensuite, mettez à jour uniquement la limite mémoire : ```bash docker service update --limit-memory 2g --reserve-memory 1g myapp_db ``` Après la mise à jour, surveillez `docker stats` pendant quelques minutes pour confirmer que l'utilisation tombe en dessous de 70 % et que la latence des requêtes s'améliore. Sinon, revenez en arrière avec `docker service update --limit-memory 512m --reserve-memory 0b myapp_db` ou `docker service rollback myapp_db` si la spécification précédente a été déployée via `docker stack deploy`. ## Chemin de configuration sécurisé Le chemin de configuration sécurisé signifie effectuer des modifications de manière contrôlée et réversible. Pour Docker Swarm, cela implique souvent d'ajuster les paramètres au niveau du service, comme les limites CPU et mémoire, les politiques de mise à jour et les pilotes de journalisation. Chaque changement doit être justifié par une métrique observée. Commencez par la configuration actuelle du service : ```bash docker service inspect myapp_frontend --pretty ``` Recherchez les limites CPU et mémoire. Si le service atteint fréquemment sa limite CPU, inspectez la limitation du conteneur avec `docker stats` et la valeur `nr_throttled` dans le cgroup : ```bash docker inspect --format '{{.HostConfig.NanoCpus}}' cat /sys/fs/cgroup/cpu/cpu.stat # sur les hôtes Linux ``` Une valeur `nr_throttled` non nulle indique que le conteneur est limité. Supposons que vous observiez 1000 événements de limitation en 60 secondes. Vous pouvez augmenter la limite CPU de 0,5 à 1,0 CPU puis surveiller à nouveau. Utilisez une valeur fractionnaire pour les CPU partiels : ```bash docker service update --limit-cpu 1.0 myapp_frontend ``` Pour vérifier, exécutez `docker stats --no-stream` et regardez la colonne `CPU %`. Si le service utilise maintenant 80 % d'un CPU sans limitation, le changement a été efficace. Un autre problème courant est la journalisation. Le pilote de journalisation par défaut `json-file` peut provoquer une forte utilisation d'E/S disque si les services journalisent excessivement. Vérifiez la taille du fichier journal du conteneur : ```bash docker inspect --format '{{.LogPath}}' ls -lh $(docker inspect --format '{{.LogPath}}') ``` Si le fichier journal fait plusieurs centaines de Mo, configurez la rotation des journaux dans la définition du service : ```yaml # docker-compose.yml (ou fichier stack) services: myapp: logging: driver: json-file options: max-size: "10m" max-file: "3" ``` Déployez le changement avec `docker stack deploy -c docker-compose.yml myapp`. Cela nécessite de redéployer la stack, ce qui met à jour le service avec une perturbation minimale si vous utilisez des mises à jour progressives. Définissez toujours des politiques de mise à jour pour contrôler le déploiement des changements : ```bash docker service update --update-parallelism 2 --update-delay 10s myapp_frontend ``` Cela met à jour deux tâches à la fois avec un délai de 10 secondes entre les lots. Si quelque chose tourne mal, utilisez immédiatement `docker service rollback myapp_frontend`. **Exemple pratique :** Un service de paiement dans le Swarm subit des latences chaque fois que son fichier journal dépasse 20 Go. Le disque se remplit, provoquant de l'attente d'E/S. En définissant la rotation des journaux à 10 Mo avec trois fichiers, l'utilisation du disque se stabilise et la latence disparaît. Ce changement est à faible risque et facilement réversible. ## Vérification et diagnostics Un diagnostic efficace nécessite d'observer le système sans le modifier d'abord. Utilisez des commandes en lecture seule pour recueillir des données avant tout changement. ### Diagnostics réseau La latence réseau est souvent la cause des problèmes de performances dans Swarm. Vérifiez l'état du réseau overlay : ```bash docker network ls docker network inspect ``` Inspectez le réseau pour les conteneurs attachés et vérifiez la perte de paquets entre les nœuds. Depuis l'hôte, vous pouvez utiliser `ping` entre les adresses IP des nœuds pour tester la connectivité sous-jacente : ```bash ping -c 10 ``` Si la perte de paquets est élevée, examinez l'infrastructure sous-jacente, comme les règles de pare-feu ou les paramètres MTU. Les réseaux overlay ajoutent une surcharge due à l'encapsulation VXLAN ; le MTU doit être abaissé à 1450 sur les interfaces réseau de l'hôte si les trames jumbo ne sont pas prises en charge. Pour tester la latence entre deux conteneurs sur le même réseau overlay : ```bash docker exec -it ping ``` Si les pings sont lents, examinez la résolution DNS du service. Docker Swarm utilise un DNS intégré ; des services mal configurés peuvent réessayer les recherches. Vérifiez le fichier `/etc/resolv.conf` à l'intérieur du conteneur : ```bash docker exec cat /etc/resolv.conf ``` Assurez-vous que le serveur de noms est `127.0.0.11`. Sinon, le conteneur utilise peut-être un DNS externe, ce qui cause des retards. ### Diagnostics CPU et mémoire Utilisez `docker stats` pour identifier les conteneurs avec une utilisation élevée du CPU ou de la mémoire. Pour un instantané unique : ```bash docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" ``` Pour des métriques cgroup plus détaillées sur Linux, lisez les fichiers cgroup : ```bash cat /sys/fs/cgroup/cpu/cpuacct.usage # Temps CPU en nanosecondes cat /sys/fs/cgroup/memory/memory.usage_in_bytes ``` Ces valeurs vous aident à définir des limites appropriées. Si un conteneur utilise 2 Go en pic, définissez la limite mémoire à 2,5 Go pour laisser de la marge. ### Journaux de service Les journaux révèlent souvent des problèmes de performances. Utilisez `docker service logs` pour voir la sortie récente : ```bash docker service logs --tail 100 myapp_frontend ``` Si vous utilisez un pilote de journalisation qui stocke les journaux localement, vous pouvez également tracer les requêtes lentes en ajoutant des journaux de durée au niveau de l'application. Par exemple, un service web peut journaliser la durée de chaque requête. Filtrez les plus lentes : ```bash docker service logs myapp_frontend --since 1h | grep "duration" | sort -k3 -n | tail -10 ``` Cela montre les requêtes les plus lentes. Corrélez les horodatages avec d'autres métriques. **Exemple pratique :** Un nœud worker présente une charge moyenne élevée. `docker stats` montre qu'un conteneur utilise constamment 3,2 CPU. Le service n'a pas de limite CPU, il consomme donc tous les cœurs disponibles. Vous voyez que `nr_throttled` est à zéro car aucune limite n'est définie, mais l'hôte est surchargé. La solution consiste à définir une limite CPU permettant au service d'utiliser au maximum 2 CPU, laissant de la capacité pour les autres tâches. Après avoir défini `--limit-cpu 2`, la charge de l'hôte baisse et tous les services fonctionnent mieux. Vérifiez en exécutant `docker stats` et en observant que l'utilisation CPU du conteneur limité reste autour de 200 %. ## Modes de défaillance et récupération L'optimisation des performances peut introduire des pannes si elle n'est pas effectuée avec soin. Voici les modes de défaillance courants et comment s'en remettre. ### 1. Arrêts par manque de mémoire (OOM) Si vous définissez une limite mémoire trop basse, le conteneur peut être tué par le tueur OOM. Les symptômes incluent des redémarrages fréquents du conteneur et un code de sortie 137. Vérifiez l'historique des tâches du service : ```bash docker service ps myapp_backend --no-trunc ``` Recherchez `OOMKilled` dans la colonne `ERROR`. Si présent, augmentez la limite mémoire ou réduisez l'utilisation mémoire de l'application. Vous pouvez également consulter les journaux du démon Docker pour les événements OOM : ```bash journalctl -u docker.service | grep -i oom ``` Récupération : augmentez la limite mémoire à une valeur sûre, par exemple de 256 Mo à 512 Mo, et redéployez. Surveillez `docker stats` pour vous assurer que le conteneur reste dans les limites. ### 2. Pannes du réseau overlay Si vous configurez mal le réseau overlay, les conteneurs peuvent ne pas communiquer. Par exemple, changer le sous-réseau sans recréer le réseau peut entraîner des conflits d'adresses IP. Récupération : inspectez le réseau et recréez-le si nécessaire : ```bash docker network inspect my_overlay # Si cassé, supprimez le réseau (cela nécessite de déconnecter tous les conteneurs) docker network rm my_overlay # Puis recréez avec les bons paramètres et reconnectez les services docker network create --driver overlay --subnet 10.0.9.0/24 my_overlay docker service update --network-add my_overlay myapp_backend ``` ### 3. Mise à jour progressive bloquée Une mise à jour progressive peut échouer si une nouvelle tâche ne devient pas saine. Si le service a un healthcheck, la mise à jour peut attendre indéfiniment. Vérifiez l'état de la mise à jour : ```bash docker service inspect myapp_frontend --format '{{.UpdateStatus}}' ``` S'il affiche `rollback completed`, la mise à jour a déjà été annulée. Si elle est bloquée, vous pouvez forcer une annulation : ```bash docker service rollback myapp_frontend ``` Ou mettez à jour avec `--force` pour continuer malgré les échecs. Définissez toujours un healthcheck dans la définition du service pour éviter cela : ```yaml healthcheck: test: ["CMD", "curl", "-f", "http://localhost/health"] interval: 10s timeout: 5s retries: 3 ``` ### 4. Épuisement de l'espace disque Les journaux ou les couches de conteneurs peuvent remplir le disque. Cela entraîne un comportement erratique de tous les conteneurs. Vérifiez l'utilisation du disque : ```bash df -h docker system df ``` Si `docker system df` montre des volumes ou images volumineux, nettoyez avec `docker system prune -f`. Pour les journaux, assurez-vous que la rotation est en place comme décrit précédemment. ### 5. Problèmes de résolution DNS Les pannes du DNS intégré peuvent ralentir la découverte de services. Les symptômes incluent des délais d'attente lorsque les conteneurs tentent de se connecter par nom de service. Testez la résolution depuis l'intérieur d'un conteneur : ```bash docker exec nslookup myapp_backend ``` Si cela échoue, vérifiez que le conteneur est attaché au bon réseau. Redémarrer le conteneur peut corriger les entrées DNS obsolètes : ```bash docker service update --force myapp_backend ``` **Exemple pratique :** Après avoir défini une limite mémoire de 128 Mo sur un service Java, il redémarre constamment avec le code de sortie 137. `docker service ps` affiche `OOMKilled`. L'application a besoin d'au moins 500 Mo pour le tas JVM plus la surcharge. Vous récupérez en augmentant la limite à 1 Go et en ajoutant `-Xmx512m` à la commande Java pour plafonner le tas. Le service se stabilise. ## Liste de contrôle des opérations Utilisez cette liste avant et après tout changement d'optimisation des performances. - [ ] **Identifier le goulot d'étranglement** : Est-ce le CPU, la mémoire, les E/S disque ou le réseau ? Utilisez `docker stats`, `iostat` et `ping` pour localiser. - [ ] **Enregistrer les métriques de référence** : Notez le pourcentage CPU actuel, l'utilisation mémoire, la latence des requêtes et le débit. Par exemple, avant d'ajuster, enregistrez que le service web a un temps de réponse moyen de 800 ms sous 100 requêtes par seconde. - [ ] **Faire un changement à la fois** : Si vous changez plusieurs choses, vous ne saurez pas laquelle a aidé ou nui. - [ ] **Définir l'amélioration attendue** : Fixez un objectif mesurable, par exemple « Réduire le temps de réponse à moins de 400 ms ». - [ ] **Appliquer le changement** : Utilisez `docker service update` ou `docker stack deploy`. - [ ] **Surveiller après le changement** : Observez `docker stats` et les journaux de l'application pendant au moins 15 minutes. Comparez à la référence. - [ ] **Vérifier l'objectif** : Si le temps de réponse passe à 350 ms, succès. Sinon, investigatez ou annulez. - [ ] **Documenter le changement** : Mettez à jour les procédures avec la nouvelle configuration et le raisonnement. - [ ] **Planifier le retour en arrière** : Sachez toujours comment revenir : `docker service rollback ` si vous utilisez l'annulation intégrée de Swarm, ou redéployez le fichier stack précédent. **Responsabilisation** : Attribuez un propriétaire unique à chaque effort d'optimisation. Par exemple, l'ingénieur DevOps de garde doit être propriétaire du changement, et le chef d'équipe le révise chaque semaine jusqu'à stabilisation. Le propriétaire est responsable de la surveillance et de l'annulation si nécessaire. ## Pièges courants De nombreux problèmes de performances proviennent d'erreurs évitables. Voici les plus fréquentes et comment les prévenir. ### 1. Ignorer les limites de ressources **Pourquoi cela arrive** : Les développeurs exécutent des conteneurs localement sans limites, ils pensent donc que la production est acceptable. **Comment éviter** : Définissez toujours des limites CPU et mémoire sur chaque service. Utilisez des réservations de ressources pour garantir une capacité minimale. Révisez les limites tous les trimestres. ### 2. Surcharger les nœuds du Swarm **Pourquoi cela arrive** : Les équipes planifient trop de services sur un seul nœud sans tenir compte de l'utilisation globale des ressources. **Comment éviter** : Utilisez des contraintes de placement pour répartir les charges de travail, ou utilisez `docker node update --availability drain` pour déplacer les tâches. Surveillez l'utilisation des ressources des nœuds avec `docker node inspect` et `docker stats`. ### 3. Mal configurer les réseaux overlay **Pourquoi cela arrive** : Copier les paramètres réseau d'un autre Swarm sans comprendre l'environnement, par exemple une inadéquation de MTU. **Comment éviter** : Testez les performances réseau avant la production. Utilisez `docker network create` avec un MTU approprié. Documentez la topologie réseau. ### 4. Ne pas utiliser de healthchecks **Pourquoi cela arrive** : Les healthchecks nécessitent une configuration supplémentaire, donc les équipes les ignorent. **Comment éviter** : Implémentez des healthchecks pour tous les services critiques. Ils empêchent le routage du trafic vers des conteneurs non sains et améliorent les mises à jour progressives. ### 5. Journalisation excessive **Pourquoi cela arrive** : Les applications journalisent au niveau DEBUG en production. **Comment éviter** : Définissez le niveau de journalisation sur INFO ou WARN en production. Configurez la rotation des journaux et utilisez un système de journalisation centralisé pour analyser les journaux sans remplir le disque. ### 6. Copier aveuglément des valeurs de réglage depuis Internet **Pourquoi cela arrive** : Les opérateurs trouvent un article de blog recommandant un paramètre spécifique du noyau ou de Docker et l'appliquent sans test. **Comment éviter** : Chaque paramètre de réglage doit être justifié par vos métriques. Testez d'abord dans un environnement de staging. Gardez un enregistrement des changements et de leurs effets. ## Conclusion L'optimisation des performances de Docker Swarm est un processus systématique de mesure, de changement ciblé et de vérification. En maintenant un inventaire clair des versions et des ressources, en suivant des pratiques de configuration sécuritaires et en utilisant des commandes de diagnostic, vous pouvez résoudre les goulots d'étranglement sans risquer la stabilité. Les exemples de ce guide fournissent un point de départ. Adaptez-les à votre environnement : confirmez toujours les prérequis, observez le comportement actuel, effectuez un ajustement ciblé et vérifiez le résultat avec des chiffres concrets. Plus important encore, planifiez votre chemin de récupération avant de faire le changement. Commencez par une amélioration à faible risque, comme définir la rotation des journaux ou ajouter un healthcheck. Enregistrez l'état actuel, appliquez le changement et comparez les métriques. Au fil du temps, ces pratiques disciplinées garderont votre cluster Swarm rapide et fiable.