Introduction
Les problèmes de réseau liés aux Dockerfiles peuvent transformer un conteneur fonctionnel en une boîte isolée incapable d'atteindre la base de données, de résoudre les noms de services ou d'accepter le trafic entrant. Ce guide va au-delà des conseils Docker génériques et se concentre sur des étapes de dépannage pratiques pour les problèmes de réseau qui proviennent des Dockerfiles et des configurations d'exécution qu'ils produisent.
Vous apprendrez à diagnostiquer les échecs DNS sur les réseaux pont personnalisés, à vérifier les mappages de ports entre l'hôte et les conteneurs, et à tester la connectivité entre les services liés. Chaque section comprend des commandes concrètes, les résultats attendus et les signaux d'échec qui vous aident à décider s'il faut ajuster une instruction du Dockerfile, recréer un réseau ou inspecter un conteneur en cours d'exécution.
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 la récupération si l'état attendu n'est pas atteint.
Inventaire des versions et de l'environnement
Avant de modifier un Dockerfile ou de redémarrer des conteneurs, capturez un instantané précis de l'environnement actuel. Commencez par les versions du moteur Docker et du client, car le comportement réseau change entre les versions.
docker version --format '{{.Server.Version}}'
Résultat attendu : 20.10.12 ou similaire. Si les versions client et serveur diffèrent significativement, mettez-les à niveau ou alignez-les pour réduire les bogues de bord.
Ensuite, listez les réseaux définis et inspectez celui que vos conteneurs utilisent.
docker network ls
La sortie affiche les identifiants de réseau, les noms, les pilotes (bridge, overlay, host, none) et la portée. Un conteneur sur un réseau pont défini par l'utilisateur obtient la résolution DNS automatique pour les autres conteneurs du même réseau. Un conteneur sur le pont par défaut ne le fait pas ; il ne peut joindre les autres que par IP.
Pour le conteneur concerné, inspectez sa configuration réseau :
docker inspect --format '{{json .NetworkSettings.Networks}}' <nom-du-conteneur>
Cela renvoie du JSON avec les détails par réseau : adresse IP, passerelle, paramètres DNS et alias. Vérifiez que le conteneur est attaché au réseau attendu et possède une IP dans le sous-réseau.
Exemple de signal d'échec : un service nommé web ne peut pas résoudre db mais les deux sont sur le même réseau personnalisé. La sortie attendue montrerait les deux conteneurs dans l'inspection du réseau :
docker network inspect <nom-du-réseau> --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
Si db est absent, alors le conteneur de base de données n'est pas attaché à ce réseau, ou son nom de conteneur diffère.
Prérequis pour des vérifications réseau fiables :
- Docker Engine 20.10 ou ultérieur (pour les fonctionnalités DNS et réseau modernes)
- Docker CLI correspondant à la version du moteur
- Un conteneur de test jetable avec des outils réseau, tel que
nicolaka/netshoot
Utilisez un conteneur de test pour pinger les services dans le même réseau sans modifier les conteneurs de production :
docker run --rm --network <nom-du-réseau> nicolaka/netshoot ping -c 4 db
Si le DNS échoue, vous verrez ping: bad address 'db'. Si le DNS fonctionne mais que le service est injoignable, vous verrez 4 packets transmitted, 0 received, 100% packet loss, ce qui indique un service qui n'écoute pas ou une règle de pare-feu.
Lorsque vous traitez des données persistantes, confirmez où les fichiers sont stockés avant de modifier les conteneurs. Un volume nommé tel que app_data:/var/lib/app est géré par Docker et est généralement plus facile à réutiliser lors des reconstructions de conteneurs. Un montage bind tel que ./data:/var/lib/app mappe directement un répertoire hôte et est utile pour le développement local, mais il peut exposer des problèmes de permissions, de portabilité et de sauvegarde si le même chemin n'existe pas sur une autre machine. Effectuez un test de redémarrage : arrêtez le conteneur, recréez-le et confirmez que l'application voit toujours les fichiers attendus. Si les données disparaissent, le service écrivait probablement dans le système de fichiers du conteneur au lieu d'un volume ou d'un montage.
Capturez l'état actuel et les horodatages dans l'inventaire. Par exemple :
date +%Y-%m-%dT%H:%M:%S%z
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
docker logs --tail 100 <nom-du-conteneur> > /tmp/conteneur-$(date +%s).log
Cela crée un instantané horodaté et stocke les journaux récents pour comparaison ultérieure. Protégez les identifiants et les informations privées : n'imprimez jamais les variables d'environnement ou les chaînes de connexion dans les journaux. Utilisez la rédaction si nécessaire. Ne modifiez qu'un élément limité uniquement lorsque son rayon d'impact et son chemin de récupération sont compris.
Chemin de configuration sûr
Les instructions du Dockerfile influencent le réseau de manière pas toujours évidente. EXPOSE ne publie pas les ports ; il documente seulement l'intention. CMD et ENTRYPOINT déterminent le processus qui écoute, et USER peut empêcher un service de se lier à un port bas.
Les modifications de configuration sûres courantes impliquent :
- Remplacer
localhostpar un nom de service ou l'alias DNS propre du conteneur. - Passer du pont par défaut à un réseau personnalisé pour obtenir la résolution DNS.
- Ajuster
HEALTHCHECKpour utiliser un point de terminaison réseau au lieu d'une vérification de fichier local.
Avant toute modification, observez l'état actuel avec des commandes en lecture seule :
docker port <nom-du-conteneur>
Cela montre les mappages de ports réels et est plus fiable que la lecture de docker ps, qui peut être tronquée. La sortie attendue pourrait être :
80/tcp -> 0.0.0.0:8080
80/tcp -> :::8080
Si le conteneur doit exposer le port 443 mais que seul 80 apparaît, le Dockerfile peut manquer d'un EXPOSE 443 ou le processus du conteneur n'écoute pas sur 443.
Pour modifier un mappage de port, vous devez recréer le conteneur ; docker run -p et docker compose ports sont définis à la création. Une modification dangereuse consiste à éditer la configuration en direct du conteneur, car elle sera perdue au prochain redémarrage. À la place, mettez à jour le Dockerfile ou le fichier Compose, puis redéployez.
Exemple de modification sûre : une application web dans un Dockerfile utilise RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf pour éviter un avertissement, mais la résolution DNS échoue ensuite car Apache envoie désormais localhost comme hôte aux services backend. Remplacez localhost par un alias significatif comme web qui se résout via le DNS embarqué de Docker. Après la modification, reconstruisez et redémarrez, puis vérifiez avec :
docker exec <conteneur-web> curl -I http://db:8080/health
Résultat attendu : HTTP/1.1 200 OK. Si vous obtenez Could not resolve host: db, le changement DNS n'a pas pris effet, ou l'attachement réseau est incorrect.
Pour la gestion de la configuration, évitez d'intégrer des valeurs spécifiques à l'environnement dans l'image. Utilisez des variables d'environnement ou un fichier de configuration monté au moment de l'exécution. Le Dockerfile ne doit pas contenir de secrets. Un modèle sûr :
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Puis à l'exécution :
docker run -d --name app --network frontend -e DATABASE_URL=postgres://db:5432/app app:latest
DATABASE_URL utilise le nom de service db, qui se résout uniquement si un conteneur nommé db est sur le même réseau et que le serveur DNS embarqué fonctionne. Testez la connectivité avec :
docker exec app nslookup db
La sortie attendue inclut l'adresse IP du conteneur db. Sinon, vérifiez que les deux conteneurs sont sur le même réseau et que le DNS du réseau est activé (il l'est par défaut sur les réseaux définis par l'utilisateur).
Un test local de type production doit inclure un test de redémarrage : arrêtez le conteneur, recréez-le et confirmez que l'application voit toujours les fichiers et les pairs réseau attendus. Si les données disparaissent ou si les entrées DNS s'évanouissent, la configuration n'était pas persistante.
Limitez le rayon d'impact en testant d'abord les modifications sur une copie du conteneur. Utilisez docker run --rm avec la même image et les mêmes paramètres réseau, exécutez le diagnostic, puis appliquez la modification au conteneur principal uniquement après vérification.
Vérification et diagnostics
Une vérification systématique sépare les événements corrélés des causes racines. Commencez par les tests de couche 3 et 4, puis passez aux vérifications au niveau applicatif.
Ping teste si un hôte est joignable et si le DNS résout :
docker exec web ping -c 4 db
Cela envoie des paquets ICMP. Si le réseau utilise un filtrage non-ICMP, le ping peut échouer même si le service est joignable via TCP. Un meilleur test pour une base de données est la connectivité TCP :
docker exec web nc -zv db 5432
Résultat attendu : Connection to db 5432 port [tcp/postgresql] succeeded! Si cela se bloque ou affiche refusé, soit le conteneur de base de données n'écoute pas sur 5432, soit un pare-feu sur le conteneur de base de données bloque le port.
Vérifiez les ports en écoute à l'intérieur du conteneur :
docker exec db netstat -tulpn | grep LISTEN
Si rien n'écoute sur 5432, le service PostgreSQL à l'intérieur du conteneur a peut-être échoué à démarrer. Vérifiez ses journaux :
docker logs db --tail 50
Recherchez FATAL: could not create shared memory segment ou permission denied. Si le processus tourne mais n'écoute pas sur l'interface attendue, il peut se lier à 127.0.0.1 à l'intérieur du conteneur au lieu de 0.0.0.0. Dans PostgreSQL, cela est contrôlé par listen_addresses dans postgresql.conf. Le Dockerfile ou le script d'entrée doit le définir sur '*' pour le fonctionnement en conteneur.
Pour des diagnostics spécifiques au DNS, utilisez nslookup ou dig :
docker exec web nslookup db
Inspectez le serveur DNS utilisé. Le serveur DNS embarqué de Docker est à 127.0.0.11 à l'intérieur des conteneurs. Interrogez-le directement :
docker exec web nslookup db 127.0.0.11
Si cela fonctionne mais pas le résolveur par défaut, une substitution peut être définie dans /etc/resolv.conf, peut-être depuis le Dockerfile ou un montage. Vérifiez le fichier :
docker exec web cat /etc/resolv.conf
Entrée attendue : nameserver 127.0.0.11. Si elle affiche un DNS externe, le conteneur ne résoudra pas les alias du réseau Docker.
Pour tester la connectivité au niveau applicatif, utilisez curl avec une sortie verbeuse :
docker exec web curl -v http://db:8080/health
La sortie verbeuse montre la résolution DNS, la connexion TCP, la poignée de main TLS et la réponse HTTP. Une réponse réussie prouve que le chemin complet fonctionne. Si elle échoue avec Connection refused, le service n'écoute pas ou un pare-feu rejette la connexion. Si elle échoue avec Could not resolve host, le DNS est cassé.
Capturez les diagnostics de manière structurée. Par exemple, créez un fichier avec toutes les sorties :
{
echo "=== date ==="; date
echo "=== docker version ==="; docker version
echo "=== réseaux ==="; docker network ls
echo "=== inspection réseau conteneur ==="; docker inspect web --format '{{json .NetworkSettings.Networks}}'
echo "=== ping db ==="; docker exec web ping -c 4 db
echo "=== nc db 5432 ==="; docker exec web nc -zv db 5432
echo "=== journaux db tail ==="; docker logs db --tail 20
} > /tmp/débogage-réseau-$(date +%s).txt
Examinez le fichier chronologiquement pour identifier la première étape défaillante.
Modes de défaillance et récupération
Les défaillances réseau courantes des Dockerfiles se classent en catégories prévisibles. Voici les plus fréquentes, pourquoi elles surviennent et comment les récupérer.
Défaillance : la résolution DNS échoue pour un nom de service
Pourquoi : les conteneurs sont sur le pont par défaut, ou le nom de service est mal orthographié, ou le conteneur n'est pas attaché au réseau personnalisé.
Diagnostic : docker exec web nslookup db renvoie server can't find db: NXDOMAIN ou expire.
Récupération : attachez les deux conteneurs à un réseau défini par l'utilisateur. S'ils sont déjà sur un réseau personnalisé, vérifiez que le nom de service correspond au nom du conteneur ou à son alias réseau. Par exemple :
docker network create frontend
docker network connect frontend web
docker network connect frontend db --alias database
Maintenant web peut résoudre à la fois db et database. Testez avec docker exec web nslookup database.
Défaillance : le mappage de port ne fonctionne pas depuis l'hôte
Pourquoi : le conteneur tourne mais aucun port n'est publié, ou le mappage de port a été configuré pour une autre interface.
Diagnostic : docker port web ne renvoie rien, ou curl localhost:8080 échoue.
Récupération : si vous utilisez uniquement le Dockerfile, il n'y a pas de mappage de port à l'exécution ; il doit être spécifié avec docker run -p 8080:80 ou dans un fichier Compose. Recréez le conteneur avec le bon mappage. À l'intérieur du conteneur, vérifiez que le service écoute :
docker exec web netstat -tulpn | grep :80
Si rien n'écoute, le service est arrêté. Vérifiez les journaux et redémarrez le service à l'intérieur du conteneur si possible.
Défaillance : connexion refusée entre conteneurs sur le même réseau
Pourquoi : le service cible ne tourne pas ou est lié à localhost à l'intérieur de son conteneur.
Diagnostic : nc -zv db 5432 affiche connexion refusée.
Récupération : vérifiez les ports en écoute du conteneur cible. Si le service n'écoute pas, démarrez-le. S'il écoute sur 127.0.0.1, reconfigurez le service pour qu'il écoute sur 0.0.0.0 ou sur l'IP du conteneur. Par exemple, dans PostgreSQL, définissez listen_addresses = '*' dans postgresql.conf et redémarrez.
Défaillance : aucune route vers l'hôte
Pourquoi : l'IP de destination n'est pas joignable en raison d'une segmentation du réseau ou parce que la passerelle du conteneur est mal configurée.
Diagnostic : ping <ip> renvoie connect: no route to host.
Récupération : vérifiez les paramètres réseau du conteneur : docker inspect web --format '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{.Gateway}}{{"\n"}}{{end}}'. Assurez-vous que la passerelle est correcte pour le sous-réseau. Si nécessaire, recréez le réseau et reconnectez les conteneurs.
Défaillance : l'application ne peut pas se connecter à un service sur un autre réseau Docker
Pourquoi : les réseaux Docker sont isolés par conception. Les conteneurs sur des réseaux différents ne peuvent pas communiquer directement.
Diagnostic : docker exec web nc -zv db 5432 échoue même si les deux conteneurs existent.
Récupération : connectez un conteneur aux deux réseaux, ou utilisez un service qui fait le pont entre réseaux, comme un proxy inverse. Si le service cible doit être joignable depuis le conteneur web, attachez le conteneur web au réseau de base de données :
docker network connect dbnet web
Ensuite web peut joindre db via son IP sur dbnet. La résolution DNS entre réseaux n'est pas automatique.
Pièges courants
- Utiliser
localhostà l'intérieur d'un conteneur pour joindre un autre conteneur.localhostest le conteneur lui-même. Utilisez le nom de service ou l'alias DNS. - Compter sur
EXPOSEdans le Dockerfile pour publier les ports.EXPOSEdocumente seulement ; il ne mappe pas les ports. Utilisez-pou lesportsCompose. - Oublier d'attacher les conteneurs au même réseau personnalisé. La résolution DNS et la découverte automatique de services ne fonctionnent que sur les réseaux définis par l'utilisateur.
- Ignorer le mode réseau. Un conteneur avec
--network hostcontourne la pile réseau et le DNS de Docker ; un conteneur avec--network nonen'a aucune connectivité. Vérifiez le mode réseau avecdocker inspect. - Modifier les paramètres réseau d'un conteneur en cours d'exécution. Les attachements réseau et les mappages de ports sont immuables ; recréez le conteneur.
- Coder en dur les adresses IP. Les IP changent lors de la recréation des conteneurs. Utilisez des noms DNS et des alias.
- Ne pas tester après une modification. Vérifiez toujours avec un test de connectivité, ne supposez pas.
Liste de contrôle des opérations
Utilisez cette liste pour standardiser le dépannage réseau des Dockerfiles et éviter les étapes manquées.
| Étape | Action | Commande ou vérification | Résultat attendu | Responsable | Fréquence |
|---|---|---|---|---|---|
| 1 | Enregistrer la version de l'environnement | docker version | Versions client et serveur compatibles | Ingénieur DevOps, Priya Shah | Chaque session de dépannage |
| 2 | Lister les réseaux | docker network ls | Réseaux attendus présents | Ingénieur DevOps, Priya Shah | Chaque session |
| 3 | Inspecter le réseau du conteneur | docker inspect --format '{{json .NetworkSettings.Networks}}' <conteneur> | Conteneur attaché au réseau prévu, IP correcte | Ingénieur DevOps, Priya Shah | Chaque session |
| 4 | Vérifier les mappages de ports | docker port <conteneur> | Paires hôte:conteneur attendues | Ingénieur DevOps, Priya Shah | Chaque session |
| 5 | Tester la résolution DNS | docker exec <conteneur> nslookup <nom-service> | Résout vers l'IP du conteneur | Ingénieur DevOps, Priya Shah | Chaque session |
| 6 | Tester la connectivité TCP | docker exec <conteneur> nc -zv <service> <port> | Connexion réussie | Ingénieur DevOps, Priya Shah | Chaque session |
| 7 | Vérifier les ports d'écoute du service | docker exec <conteneur> netstat -tulpn | Service en écoute sur le port attendu | Développeur d'applications, Marcus Chen | Lors des changements de service |
| 8 | Examiner les journaux | docker logs <conteneur> --tail 100 | Aucune erreur fatale liée au réseau | Développeur d'applications, Marcus Chen | En cas d'échec |
| 9 | Appliquer le changement de configuration | Mettre à jour le Dockerfile ou le fichier Compose | Un seul changement limité | Développeur d'applications, Marcus Chen | Au besoin |
| 10 | Vérifier après le changement | Répéter les étapes 5 à 7 | Tous les contrôles passent | Ingénieur DevOps, Priya Shah | Après chaque changement |
| 11 | Documenter la récupération et les leçons | Mettre à jour le runbook | Étapes et commandes enregistrées | Responsable technique, Amara Okafor | Revue mensuelle |
Chaque ligne a un seul responsable imputable (une personne nommée, pas un groupe) pour garantir la responsabilité. La colonne fréquence indique quand l'action est effectuée : chaque session de dépannage, lors d'événements spécifiques ou pendant les revues périodiques. Revoyez la liste mensuellement pour ajouter de nouveaux modèles de défaillance et retirer les étapes obsolètes.
Utilisez cette liste comme modèle et adaptez-la à votre pile. Par exemple, remplacez nc par telnet si nc n'est pas disponible, et utilisez dig au lieu de nslookup si vous préférez.
Conclusion
Le dépannage réseau des Dockerfiles devient gérable lorsque vous séparez l'observation de l'intervention, vérifiez chaque couche de la pile et documentez les chemins de récupération. Les commandes et les scénarios de ce guide vous donnent un processus reproductible : inventorier l'environnement, inspecter le DNS et les ports, tester la connectivité, puis apporter des modifications minimales et vérifier.
Comme prochaine étape, choisissez une vérification à faible risque dans la liste de contrôle des opérations, comme confirmer la résolution DNS d'un nom de service. Enregistrez l'état actuel, exécutez la vérification et comparez le résultat avec le signal attendu. Si elle échoue, parcourez les modes de défaillance jusqu'à isoler la cause, puis appliquez un correctif sûr et revérifiez.
Un flux de travail technique fiable rend les défaillances visibles, protège les valeurs sensibles, limite les changements à la ressource prévue et définit la vérification de récupération avant qu'un incident ne force la décision. Tenez votre runbook à jour et partagez-le avec votre équipe.