## Introduction Comprendre le réseau bridge Docker à un niveau avancé signifie aller au-delà de `docker run -p 8080:80` et explorer les mécanismes réels : comment les conteneurs obtiennent des adresses IP, comment la résolution DNS fonctionne, quelles règles iptables sont créées et comment diagnostiquer une connexion défaillante. Ce guide s'adresse aux développeurs, aux ingénieurs DevOps et aux équipes techniques de startups qui doivent exploiter des applications conteneurisées de manière fiable en production. Vous apprendrez à inspecter et à interpréter les rouages internes du réseau bridge, à concevoir une attribution d'adresses IP prévisible, à contrôler les ports exposés avec précision, à sécuriser le trafic entre conteneurs et à automatiser la vérification. Chaque section comprend des commandes réelles, des sorties attendues et des signaux d'échec afin que vous puissiez appliquer ces techniques immédiatement. La sécurité opérationnelle est le fondement : observez avant de modifier, limitez le rayon d'impact, utilisez des variables fictives au lieu de secrets, vérifiez chaque changement et documentez les étapes de récupération. À la fin, vous serez en mesure de résoudre les problèmes de réseau bridge avec confiance et de prévenir les erreurs de configuration courantes. ## Mécanismes fondamentaux du réseau bridge Le réseau bridge par défaut de Docker (nommé `bridge`) est un réseau virtuel de couche 2 qui connecte les conteneurs sur le même hôte. Chaque conteneur reçoit une interface Ethernet virtuelle (`veth`) qui est appairée avec une interface côté hôte attachée au pont `docker0`. Le pont agit comme un commutateur physique, transférant les trames entre les interfaces en fonction des adresses MAC. Faits clés sur le bridge par défaut : - Il utilise le sous-réseau `172.17.0.0/16` par défaut (configurable via daemon.json). - Les conteneurs sur le bridge par défaut ne peuvent communiquer entre eux que par adresse IP, et non par nom de conteneur. La résolution DNS n'est pas fournie sur ce réseau. - L'accès externe nécessite la publication de ports avec `-p` ou `--publish`. - Le pont lui-même possède une adresse IP sur l'hôte, généralement `172.17.0.1`, que les conteneurs utilisent comme passerelle par défaut. En revanche, les **réseaux bridge définis par l'utilisateur** (créés avec `docker network create`) offrent une résolution DNS automatique entre les conteneurs, une meilleure isolation et la possibilité de connecter/déconnecter les conteneurs à la volée. Pour tout ce qui dépasse un simple conteneur jetable, utilisez un bridge défini par l'utilisateur. Créez un bridge défini par l'utilisateur : ```bash docker network create --driver bridge --subnet 10.5.0.0/16 --gateway 10.5.0.1 my_app_net ``` Sortie attendue : l'identifiant du réseau, par exemple `3f0a9b2c8d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a`. Lancez maintenant deux conteneurs sur ce réseau : ```bash docker run -d --name web --network my_app_net --ip 10.5.0.10 nginx:alpine docker run -d --name app --network my_app_net --ip 10.5.0.20 alpine sleep 3600 ``` Depuis l'intérieur de `app`, effectuez un ping vers `web` par son nom : ```bash docker exec -it app ping -c 2 web ``` La sortie attendue montre des réponses réussies avec `10.5.0.10`. Cela fonctionne car le serveur DNS intégré de Docker (à `127.0.0.11` dans chaque conteneur) résout les noms de conteneurs sur les réseaux définis par l'utilisateur. ## Inspecter et décoder l'état du réseau Avant toute modification, rassemblez des preuves. La commande principale est `docker network inspect `, qui renvoie un objet JSON avec les conteneurs, les attributions d'adresses IP et les options réseau. Inspectez notre réseau personnalisé : ```bash docker network inspect my_app_net ``` Recherchez la section `Containers`. Vous verrez des entrées comme : ```json "Containers": { "1a2b3c...": { "Name": "web", "IPv4Address": "10.5.0.10/16", "MacAddress": "02:42:ac:11:00:02" }, "4d5e6f...": { "Name": "app", "IPv4Address": "10.5.0.20/16" } } ``` Pour voir la configuration du pont côté hôte : ```bash ip addr show docker0 brctl show docker0 ``` `docker0` affiche l'adresse IP du pont `172.17.0.1/16`. `brctl show` liste les interfaces attachées au pont (les paires veth pour les conteneurs du bridge par défaut). Pour un conteneur spécifique, inspectez ses paramètres réseau : ```bash docker inspect web --format '{{json .NetworkSettings.Networks}}' | jq ``` Cela révèle l'IP, la passerelle, l'adresse MAC et les alias. Si `jq` n'est pas installé, omettez-le ou utilisez `python -m json.tool`. Vérifiez la résolution DNS à l'intérieur d'un conteneur : ```bash docker exec web cat /etc/resolv.conf ``` Sortie attendue sur un réseau défini par l'utilisateur : ``` nameserver 127.0.0.11 options ndots:0 ``` Cela confirme que le conteneur utilise le DNS intégré de Docker. ## Inventaire de la version et de l'environnement Avant de résoudre un problème ou de modifier les paramètres du réseau bridge, documentez l'environnement. Notez la version de Docker, la configuration du démon, les réseaux existants et les conteneurs en cours d'exécution. Vérifiez la version de Docker : ```bash docker version --format '{{.Server.Version}}' ``` Exemple de sortie : `24.0.7`. Notez que des fonctionnalités comme `--ip` nécessitent un réseau défini par l'utilisateur, tandis que certains drapeaux dépendent du pilote IPAM du démon. Listez tous les réseaux : ```bash docker network ls ``` La sortie attendue inclut `bridge`, `host`, `none` et les réseaux personnalisés. La colonne `SCOPE` indique `local` pour les réseaux bridge. Pour chaque conteneur en cours d'exécution, obtenez son attachement réseau : ```bash docker ps --format 'table {{.Names}} {{.Networks}} {{.Ports}}' ``` Exemple : ``` NAMES NETWORKS PORTS web my_app_net 80/tcp app my_app_net ``` Vérifiez les règles iptables créées par Docker pour la publication de ports et la communication inter-conteneurs : ```bash sudo iptables -t nat -L -n -v | grep DOCKER sudo iptables -L -n -v | grep DOCKER ``` Ces règles sont essentielles pour acheminer le trafic vers les conteneurs. Des pare-feu mal configurés interrompent souvent la connectivité. Documentez l'état actuel dans un fichier texte ou un runbook avant tout changement. Par exemple : ``` Date : 2025-04-08 Version Docker : 24.0.7 Réseaux : bridge (172.17.0.0/16), my_app_net (10.5.0.0/16) Conteneurs : web (10.5.0.10), app (10.5.0.20) ``` ## Modifications de configuration sécurisées Lorsque vous modifiez les paramètres du réseau bridge, changez un élément à la fois et vérifiez immédiatement. Utilisez des valeurs fictives pour les secrets et évitez d'exposer des ports sensibles à toutes les interfaces. ### Création d'un bridge défini par l'utilisateur avec un sous-réseau spécifique Au lieu de vous fier au sous-réseau par défaut, définissez une plage explicite pour éviter les conflits avec les VPN d'entreprise ou d'autres réseaux. Exemple : ```bash docker network create --driver bridge \ --subnet 192.168.100.0/24 \ --gateway 192.168.100.1 \ --ip-range 192.168.100.128/25 \ --opt com.docker.network.bridge.name=br_custom \ custom_net ``` Explication : - `--subnet` définit le réseau complet. - `--gateway` définit l'IP du pont sur l'hôte. - `--ip-range` restreint l'attribution automatique d'IP à un pool plus petit (utile pour réserver des IP statiques). - `--opt com.docker.network.bridge.name` renomme l'interface du pont hôte en `br_custom` pour plus de clarté. Vérifiez la création : ```bash docker network inspect custom_net --format '{{json .IPAM.Config}}' ``` Sortie attendue : ```json [{"Subnet":"192.168.100.0/24","Gateway":"192.168.100.1"}] ``` ### Attribution d'adresses IP statiques aux conteneurs Pour les services nécessitant une adresse stable (par exemple, une base de données accessible par plusieurs applications), attribuez une IP statique. Lancez un conteneur de base de données : ```bash docker run -d --name db \ --network custom_net \ --ip 192.168.100.10 \ -e MYSQL_ROOT_PASSWORD=secret \ mysql:8 ``` Vérifiez l'IP : ```bash docker inspect db --format '{{.NetworkSettings.Networks.custom_net.IPAddress}}' ``` Attendu : `192.168.100.10`. Attention : Sur un bridge défini par l'utilisateur, vous pouvez mélanger des IP statiques et dynamiques, mais assurez-vous que l'IP statique se trouve dans le sous-réseau et ne fait pas partie du pool dynamique `--ip-range`. ### Publication sélective des ports Par défaut, `-p 8080:80` se lie à toutes les interfaces de l'hôte (`0.0.0.0:8080`). Pour restreindre l'accès au localhost uniquement : ```bash docker run -d --name local_web -p 127.0.0.1:8080:80 nginx:alpine ``` Désormais, le service n'est accessible que depuis l'hôte lui-même. Vérifiez : ```bash curl http://127.0.0.1:8080 ``` Attendu : du HTML provenant de nginx. Toute tentative d'accès depuis une autre machine échouera. Pour se lier à une IP d'hôte spécifique (par exemple, une interface de gestion privée) : ```bash docker run -d --name mgmt_web -p 192.168.1.100:8081:80 nginx:alpine ``` Rappelez-vous que la publication de ports fonctionne via des règles DNAT iptables. Si vous avez `ufw` ou `firewalld`, ils peuvent ne pas voir les règles de Docker et peuvent exposer les ports de manière incorrecte. Testez toujours depuis un hôte externe pour confirmer l'exposition. ## Vérification et diagnostics Un diagnostic efficace combine des vérifications au niveau des paquets, des tests DNS et l'analyse des journaux. Voici une approche structurée. ### Test de connectivité entre conteneurs Le ping est souvent bloqué par défaut dans de nombreuses images (ICMP désactivé), utilisez donc plutôt `curl` ou `nc`. Depuis `app`, testez la connectivité TCP vers le port 80 de `web` : ```bash docker exec app sh -c 'nc -zv web 80' ``` Sortie attendue : `web (10.5.0.10:80) open`. Si `nc` n'est pas installé dans le conteneur, utilisez un conteneur temporaire : ```bash docker run --rm --network my_app_net alpine nc -zv web 80 ``` ### Vérifications de la résolution DNS Testez la résolution de noms : ```bash docker exec app nslookup web ``` La sortie attendue inclut l'IP `10.5.0.10`. Si cela échoue, vérifiez que les deux conteneurs sont sur le même réseau défini par l'utilisateur. Le bridge par défaut ne fournit pas de DNS. ### Inspection des journaux de conteneurs pour les erreurs réseau Les journaux d'application révèlent souvent les problèmes de connexion. ```bash docker logs web --tail 20 ``` Recherchez des lignes comme `connect: connection refused` ou `no route to host`. ### Surveillance du trafic du pont Utilisez `tcpdump` sur l'interface du pont hôte pour voir le trafic circulant entre les conteneurs : ```bash sudo tcpdump -i br_custom -n port 80 ``` Générez du trafic en effectuant un curl vers `web` depuis `app` : ```bash docker exec app wget -qO- http://web ``` Vous devriez voir des paquets dans la sortie de tcpdump. ### Vérification des règles iptables pour la publication de ports Pour tracer comment une connexion externe atteint un conteneur : ```bash sudo iptables -t nat -L DOCKER -n --line-numbers ``` Trouvez la règle correspondant au port publié. Exemple : ``` 4 DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80 ``` Vérifiez ensuite la chaîne de filtrage pour la règle de transfert : ```bash sudo iptables -L DOCKER -n --line-numbers ``` Si le port n'est pas joignable depuis l'extérieur, assurez-vous que le conteneur écoute réellement sur le port attendu à l'intérieur (par exemple, `ss -tlnp` dans le conteneur). ## Modes de défaillance et récupération Le réseau bridge peut échouer de manière prévisible. Voici des scénarios courants et comment les récupérer. ### Le conteneur ne peut pas atteindre Internet Symptômes : `curl http://example.com` à l'intérieur du conteneur se bloque ou échoue avec `Could not resolve host` ou `Connection timed out`. Causes possibles : - Transfert IP désactivé sur l'hôte : `sysctl net.ipv4.ip_forward` devrait être `1`. Sinon, exécutez `sudo sysctl -w net.ipv4.ip_forward=1` et rendez-le permanent dans `/etc/sysctl.conf`. - Pare-feu bloquant le NAT sortant : assurez-vous que la règle de masquerade existe : `sudo iptables -t nat -L POSTROUTING -n -v | grep MASQUERADE`. Si elle est absente, redémarrez Docker. - Mauvaise configuration DNS : le fichier `/etc/resolv.conf` du conteneur pointe vers un serveur inexistant. Utilisez `docker run --dns 8.8.8.8` ou configurez le DNS du démon. Récupération : redémarrez le démon Docker après avoir apporté des modifications, puis recréez les conteneurs. ### La communication inter-conteneurs échoue sur le bridge par défaut Rappelez-vous que le bridge par défaut ne fournit pas de résolution de noms. Utilisez des adresses IP ou passez à un réseau défini par l'utilisateur. Pour migrer un conteneur en cours d'exécution vers un réseau défini par l'utilisateur : ```bash docker network connect my_app_net web ``` Mais notez : vous ne pouvez pas vous déconnecter du bridge par défaut sans recréer le conteneur. Pour la production, définissez les réseaux dans Compose ou Dockerfile. ### Conflit de port sur l'hôte Lors de la publication d'un port déjà utilisé, Docker renvoie une erreur : ``` Bind for 0.0.0.0:8080 failed: port is already allocated. ``` Trouvez le processus en conflit : ```bash sudo lsof -i :8080 ``` Choisissez ensuite un autre port hôte ou arrêtez le processus en conflit. ### Épuisement des adresses IP Si vous exécutez de nombreux conteneurs avec des IP statiques et épuisez le sous-réseau, les nouveaux conteneurs ne démarrent pas avec `no available IPv4 addresses on this network`. Augmentez la taille du sous-réseau ou utilisez une plage plus grande. Par exemple, au lieu de `/24`, utilisez `/22`. Pour inspecter l'utilisation actuelle des IP : ```bash docker network inspect custom_net --format '{{range .Containers}}{{.IPv4Address}}{{"\n"}}{{end}}' ``` ### Interfaces veth orphelines après la suppression d'un conteneur Normalement, Docker nettoie les paires veth lorsqu'un conteneur s'arrête. Si des interfaces orphelines subsistent (par exemple, après un plantage), supprimez-les manuellement : ```bash ip link show type veth sudo ip link delete vethXXXXXX ``` Mais assurez-vous d'abord que l'interface n'est pas utilisée. ## Pièges courants et comment les éviter 1. **Utiliser le bridge par défaut pour les applications multi-conteneurs.** Il manque de DNS et isole mal. Créez toujours un bridge défini par l'utilisateur pour les services liés. 2. **Publier des ports sur toutes les interfaces.** Exposer un port de base de données sur `0.0.0.0` est un risque de sécurité. Liez-vous à `127.0.0.1` ou à une IP privée. 3. **Ignorer la gestion des adresses IP.** Les sous-réseaux qui se chevauchent avec l'hôte ou les réseaux VPN provoquent des conflits de routage. Définissez des sous-réseaux explicites. 4. **Supposer que `ping` fonctionne.** De nombreuses images n'ont pas ping. Utilisez `curl` ou `nc` pour les vérifications TCP. 5. **Oublier de persister les règles iptables.** Les règles iptables de Docker sont perdues au redémarrage du démon si elles ne sont pas gérées correctement. Utilisez `--iptables=true` (par défaut) et évitez de vider les chaînes Docker. 6. **Ne pas vérifier la résolution DNS.** Dans les réseaux définis par l'utilisateur, le DNS est automatique, mais des options personnalisées `--dns` ou `--dns-search` peuvent le remplacer et casser la résolution de noms. 7. **Compter sur les IP des conteneurs pour un accès externe.** Les IP des conteneurs ne sont pas routables depuis l'extérieur de l'hôte. Publiez toujours les ports et accédez via l'IP de l'hôte. ## Liste de contrôle des opérations Utilisez cette liste avant et après avoir apporté des modifications au réseau bridge. - [ ] Enregistrez la version de Docker, les réseaux actuels et les attributions d'IP des conteneurs à l'aide de `docker version`, `docker network ls` et `docker network inspect`. - [ ] Identifiez le rayon d'impact : quels conteneurs, services ou clients externes sont affectés ? - [ ] Pour toute modification d'un réseau partagé, planifiez une fenêtre de maintenance et informez les parties prenantes (responsable : responsable DevOps, révisé chaque semaine). - [ ] Créez une sauvegarde de la configuration réseau si vous utilisez des outils externes (par exemple, `docker network inspect my_app_net > network_backup.json`). - [ ] Appliquez le plus petit changement : créez un nouveau réseau, reconnectez un conteneur ou ajustez une liaison de port. - [ ] Vérifiez la connectivité avec des vérifications TCP (`nc -zv`), la résolution DNS (`nslookup`) et les journaux d'application. - [ ] Testez depuis un hôte externe pour confirmer la publication des ports et les limites de sécurité. - [ ] Documentez le changement, le résultat de la vérification et les étapes de retour en arrière dans le runbook (responsable : ingénieur plateforme, mis à jour immédiatement). - [ ] Surveillez pendant 24 heures après le changement pour détecter une latence ou des erreurs inattendues. ## Conclusion Le réseau bridge Docker est simple en apparence mais cache une complexité dans l'allocation IP, le DNS, les iptables et la gestion des interfaces. En maîtrisant les commandes d'inspection, en comprenant les différences entre les bridges par défaut et définis par l'utilisateur, et en adoptant une approche de diagnostic systématique, vous pouvez résoudre la plupart des problèmes sans conjectures. Commencez par une vérification à faible risque : inspectez vos réseaux actuels, testez le DNS inter-conteneurs sur un bridge défini par l'utilisateur et confirmez que les liaisons de ports sont correctement délimitées. Enregistrez l'état, apportez une seule modification et vérifiez. À mesure que vous gagnez en confiance, intégrez ces pratiques dans les runbooks opérationnels de votre équipe. L'objectif n'est pas seulement de résoudre les problèmes lorsqu'ils surviennent, mais de concevoir des réseaux bridge prévisibles, sécurisés et faciles à dépanner. Avec les commandes et les listes de contrôle de ce guide, vous disposez d'une base solide pour exploiter des applications conteneurisées à grande échelle.