Certaines pannes ne commencent pas par un crash. Elles commencent par « Nous n’avons changé que le réseau. » Puis, soudainement, vos conteneurs n’atteignent plus la base de données, votre supervision devient aveugle, et l’équipe sécurité découvre que votre application écoute maintenant sur toutes les interfaces comme en 2009.
Docker vous propose trois leviers séduisants pour le comportement L2/L3 local — bridge, host et macvlan. Les trois peuvent fonctionner. Les trois peuvent ruiner votre week-end si vous les choisissez pour la mauvaise raison. Choisissons celui qui vieillira bien en production.
Decision first: what to pick in 60 seconds
Si vous exploitez des systèmes en production, la valeur par défaut devrait être ennuyeuse. Les réseaux ennuyeux déclenchent moins d’alertes.
Choisissez bridge quand
- Vous avez besoin de publication de ports (
-p) et d’une isolation raisonnable. - Vous voulez que l’hôte reste l’hôte, et non « une soupe de conteneurs avec root au sommet ».
- Vous avez plusieurs services par hôte et souhaitez qu’ils cohabitent sans conflits de ports.
- Vous voulez une voie vers des architectures plus avancées plus tard (réseaux multiples, réseaux internes, contrôles de politique).
Choisissez host quand
- Vous avez besoin d’un très faible overhead et pouvez accepter le rayon d’impact (filtres de paquets, ports et namespaces partagés).
- Vous exécutez un service réseau majeur par hôte, qui lie déjà les ports nécessaires.
- Vous avez une bonne stratégie pour le filtrage et l’observabilité sur l’hôte.
Choisissez macvlan quand
- Vous avez besoin que les conteneurs apparaissent comme des citoyens L2 de première classe avec leur propre MAC/IP sur votre LAN physique.
- Vous intégrez des systèmes qui attendent des IP uniques par workload (licences legacy, listes d’accès IP, routeurs en amont, multicast, supervision contraignante, « appliances de sécurité »).
- Vous pouvez gérer les réalités L2 : ARP, tables CAM, sécurité des ports du switch et discipline IPAM.
Mon choix par défaut opinionné : commencez par des réseaux bridge définis par l’utilisateur. N’utilisez host que si vous pouvez défendre le risque par écrit. N’utilisez macvlan que lorsque vous devez interfacer le LAN comme un véritable hôte et que vous avez validé que le switch ne vous punira pas pour cela.
The mental model that prevents dumb outages
Le réseau Docker n’est pas magique. C’est des namespaces réseau Linux plus un peu de colle : paires Ethernet virtuelles (veth), un périphérique bridge, des règles de routage, et des règles NAT/iptables/nftables. Le « driver » que vous choisissez décide principalement où ces paquets voyagent et qui possède les ports.
Trois questions qui décident de tout
- Où vivent les ports ? Mappez-vous les ports du conteneur vers l’hôte (bridge), le conteneur partage-t-il l’espace de ports de l’hôte (host), ou le conteneur obtient-il sa propre IP (macvlan) ?
- Qui applique la politique L3/L4 ? Pare-feu de l’hôte + règles gérées par Docker, ou ACL réseau en amont, ou les deux ?
- Quel est votre domaine de défaillance ? Voulez-vous que « un conteneur devient bizarre » devienne « l’hôte devient bizarre » ?
Aussi : la performance est rarement votre premier problème. La capacité de debug et la prévisibilité le sont. Un driver réseau 3% plus rapide mais 30% plus difficile à diagnostiquer n’est pas une optimisation. C’est un incident futur avec une invitation de calendrier.
Une citation que j’ai vue se vérifier dans plus de postmortems que je ne veux compter : idée paraphrasée : « L’espoir n’est pas une stratégie. »
Bridge networking: the default for a reason
Le mode bridge est l’histoire de Docker « je veux que les conteneurs aient leur propre petit monde, mais restent joignables ». Le conteneur reçoit une IP sur un sous-réseau privé. Docker crée un bridge Linux (comme docker0 ou un bridge défini par l’utilisateur), puis relie eth0 du conteneur via une paire veth. Le trafic sortant est routé/NATé vers l’interface de l’hôte. Le trafic entrant utilise typiquement des ports publiés.
Pourquoi le bridge est adapté à la production
- La publication de ports est explicite. Vous ouvrez ce que vous avez l’intention d’ouvrir. Cela compte quand l’image du conteneur change et se met à binder des ports supplémentaires.
- Les noms comptent. Les réseaux bridge définis par l’utilisateur offrent une découverte de service intégrée basée sur le DNS. Les conteneurs peuvent communiquer par nom sans coller d’IP dans les configs.
- L’isolation est réelle-en-quelque-sorte. Ce n’est pas une frontière VM complète, mais c’est une ligne de confinement significative contre les collisions de ports accidentelles et certaines catégories de mauvaise config.
- Le debug est gérable. Vous pouvez raisonner sur les flux : conteneur → veth → bridge → hôte → uplink ; et les règles NAT sont visibles.
Quand le bridge mord
- Incompatibilités MTU. Les réseaux overlay, VPN ou les jumbo frames peuvent tromper PMTUD et faire disparaître des paquets.
- Surprises du NAT. Le changement d’IP source peut casser des ACL en amont ou embrouiller les logs.
- Comportement hairpin. Le trafic conteneur→hôte→conteneur via un port publié peut se comporter différemment du trafic conteneur→conteneur direct.
- Dérive du pare-feu. Docker manipule iptables/nftables. Si votre pare-feu de base suppose le contrôle total, vous aurez une guerre de territoire.
Le réseau bridge ressemble à une berline fiable. Pas sexy, mais elle démarre en hiver, les pièces sont bon marché, et tout le monde sait la réparer.
Host networking: fast, sharp, and unsafe by default
--network host abolit les précautions : le conteneur partage le namespace réseau de l’hôte. Pas de NAT. Pas d’IP conteneur. Pas de publication de ports. Si le processus bind 0.0.0.0:443, il bind le port 443 de l’hôte. C’est le but.
À quoi le mode host est vraiment adapté
- Workloads à fort débit de paquets où NAT et conntrack sont mesurables et douloureux.
- Appliances réseau (démons de routage, annonce BGP, serveurs DHCP) où vous voulez des sémantiques d’interface directes.
- Hôtes mono-locataires simples où un workload possède la machine.
Ce que le mode host casse (silencieusement)
- Les collisions de ports deviennent des échecs « aléatoires ». Déployez deux services voulant le même port 8125/udp et vous le découvrirez à l’exécution.
- Les frontières de sécurité deviennent floues. Le conteneur peut voir les interfaces de l’hôte, parfois des services host-local, et votre hypothèse « c’est juste dans Docker » meurt.
- L’observabilité devient étrange. Les outils qui s’attendent à des IP de conteneur perdent un point d’ancrage ; l’attribution du trafic peut nécessiter des outils sensibles aux cgroups.
Blague courte n°1 : Le networking host, c’est comme donner les clés de la maison à votre conteneur parce qu’il a promis qu’il allait juste déplacer la voiture pour la passer au lavage.
Gouvernance du mode host pour le rendre viable
- Utilisez systemd ou un orchestrateur pour empêcher deux conteneurs de binder le même port.
- Faites respecter explicitement la politique de pare-feu de l’hôte ; ne comptez pas sur les « bons paramètres » de Docker.
- Documentez la propriété des ports par hôte comme un contrat. Parce que c’en est un.
- Privilégiez le mode host uniquement pour les workloads qui le justifient : capture de paquets, agents de métriques, proxies edge ou véritables démons réseau.
Macvlan: the “looks like a real host” option (and its traps)
Macvlan assigne à chaque conteneur sa propre adresse MAC et IP sur votre segment réseau physique. Depuis le reste du LAN, le conteneur est un pair. Pas de mapping de ports, pas de NAT. Juste « voici une autre machine ». C’est séduisant parce que cela élimine une catégorie de situations embarrassantes : les systèmes en amont peuvent parler directement aux conteneurs sans jongler avec les ports.
Quand macvlan est la bonne réponse
- Listes d’accès legacy basées sur IP que vous ne pouvez ou ne voulez pas réécrire autour du NAT.
- Conteneurs de type appliance qui ont besoin d’une identité IP propre pour le routage, la supervision, ou la segmentation réseau.
- Logiciels dépendant du multicast/broadcast où les sémantiques NAT/bridge deviennent pénibles (avec des réserves ; tout ne devient pas plus simple).
Le gros piège macvlan : le trafic hôte→conteneur
Par défaut, avec macvlan, l’hôte ne peut pas parler à ses propres enfants macvlan sur la même interface physique. Cela surprend les gens à chaque fois. Les paquets ne font pas de hairpin comme vous l’imaginez.
La solution consiste généralement à créer une sous-interface macvlan sur l’hôte (une interface « shim ») dans le même réseau macvlan et à router via elle. Ce n’est pas difficile, mais c’est un élément supplémentaire à maintenir au redémarrage et dans la gestion de configuration.
Où macvlan mord plus tard
- Sécurité des ports de switch et limites de table CAM. Certains switches n’aiment pas qu’un port physique émette soudainement des dizaines de MACs. Vous l’apprendrez à 2h du matin, pendant un incident, si vous ne demandez pas avant.
- Tempêtes ARP et churn de la table de voisins. Les conteneurs apparaissent et disparaissent ; les caches ARP ne suivent pas toujours.
- L’IPAM devient votre problème. L’IPAM de Docker peut allouer depuis une plage, mais il ne négociera pas avec votre serveur DHCP ou le tableau réseau de votre équipe à moins que vous ne fassiez le travail.
- Tests « sur une seule machine » plus difficiles. Vous ne pouvez pas tout faire tourner sur un seul laptop et attendre que le LAN se comporte identiquement, surtout dès que le Wi‑Fi entre en jeu.
Blague courte n°2 : Macvlan, c’est super jusqu’à ce que votre switch voie trente nouvelles adresses MAC et décide de pratiquer la pleine conscience en laissant tomber le trafic.
Interesting facts and a little history (useful, not trivia night)
- Les network namespaces Linux (la base du réseau des conteneurs) sont apparus dans le noyau à la fin des années 2000, initialement pour isoler les piles réseau des processus sans virtualisation complète.
- Le réseau Docker initial s’appuyait fortement sur des règles NAT
iptables; pendant des années, « Docker a cassé mon pare-feu » était un rite de passage parce qu’il insérait automatiquement des chaînes. - Les réseaux bridge définis par l’utilisateur ont ajouté une découverte de service intégrée basée sur le DNS, ce qui a été une grande amélioration par rapport à l’ancien mécanisme
--linkqui introduisait des entrées hôtes fragiles dans les conteneurs. - Macvlan en tant que fonctionnalité du noyau précède l’adoption par Docker ; c’est un driver Linux qui permet à plusieurs MAC virtuelles de partager une interface physique, historiquement utilisé pour la séparation réseau et les environnements de laboratoire.
- Conntrack (connection tracking) est une taxe cachée dans les configurations lourdes en NAT ; des taux de connexion élevés peuvent épuiser les tables conntrack et ressembler à des pertes aléatoires de paquets.
- Les problèmes MTU se sont aggravés avec la généralisation des overlays/VPN ; « ça marche sur un hôte, ça casse entre sites » revient souvent à la MTU de chemin et au comportement de fragmentation.
- Le networking host revient à renoncer à l’une des isolations pratiques des conteneurs : la séparation des namespaces de ports. C’est pourquoi beaucoup d’orchestrateurs le traitent comme un choix privilégié.
- Bridge vs macvlan est souvent un débat sur l’endroit où vit l’identité : dans l’hôte (bridge/NAT) ou dans le LAN (macvlan). Aucun n’est gratuit.
Practical tasks: commands, outputs, and the decision you make
Ce sont les tâches que j’exécute quand quelqu’un dit : « Le réseau est bizarre. » Chacune a une commande, une sortie d’exemple, ce que cela signifie, et la décision suivante.
Task 1: List Docker networks and spot the obvious
cr0x@server:~$ docker network ls
NETWORK ID NAME DRIVER SCOPE
a1b2c3d4e5f6 bridge bridge local
f6e5d4c3b2a1 host host local
112233445566 none null local
77aa88bb99cc app-net bridge local
Ce que cela signifie : Vous avez le bridge par défaut, plus un bridge défini par l’utilisateur (app-net). C’est bien : les réseaux bridge définis par l’utilisateur offrent un meilleur comportement DNS et une meilleure séparation.
Décision : Si vos services utilisent encore le bridge par défaut et des patterns legacy, migrez-les vers un réseau défini par l’utilisateur sauf raison contraire.
Task 2: Inspect a network and confirm subnet, gateway, and options
cr0x@server:~$ docker network inspect app-net
[
{
"Name": "app-net",
"Driver": "bridge",
"IPAM": {
"Config": [
{
"Subnet": "172.22.0.0/16",
"Gateway": "172.22.0.1"
}
]
},
"Options": {
"com.docker.network.bridge.name": "br-77aa88bb99cc"
}
}
]
Ce que cela signifie : Ce bridge utilise un device bridge Linux dédié et un sous-réseau défini. Prévisible.
Décision : Si ce sous-réseau chevauche votre VPN d’entreprise ou les plages du datacenter, changez-le maintenant. Les chevauchements provoquent des incidents « seulement cassé depuis certains laptops ».
Task 3: Check which network a container is actually using
cr0x@server:~$ docker inspect -f '{{json .NetworkSettings.Networks}}' api-1
{"app-net":{"IPAddress":"172.22.0.10","Gateway":"172.22.0.1","MacAddress":"02:42:ac:16:00:0a"}}
Ce que cela signifie : Le conteneur est sur app-net avec une IP interne.
Décision : Si l’appli doit être joignable depuis le LAN sans mapping de ports, le bridge n’est pas suffisant ; envisagez macvlan ou un ingress/proxy adéquat.
Task 4: Confirm published ports and where they bind
cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Ports}}'
NAMES PORTS
api-1 0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp
db-1 5432/tcp
Ce que cela signifie : api-1 est exposé sur le port hôte 8080. db-1 n’est pas publié ; il est interne uniquement (bien).
Décision : Si vous voyez des bindings 0.0.0.0 non voulus, verrouillez-les (-p 127.0.0.1:... ou pare-feu) avant que quelqu’un d’autre ne les découvre.
Task 5: Check Docker’s NAT rules (iptables) and whether they exist
cr0x@server:~$ sudo iptables -t nat -S | sed -n '1,40p'
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N DOCKER
-N DOCKER_OUTPUT
-N DOCKER_POSTROUTING
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER_OUTPUT
-A POSTROUTING -s 172.22.0.0/16 ! -o br-77aa88bb99cc -j MASQUERADE
Ce que cela signifie : Docker gère le NAT pour le subnet bridge. Cette règle MASQUERADE est votre chemin sortant.
Décision : Si vous êtes sur un système nftables-only, vérifiez que Docker est compatible avec votre stack de pare-feu. Les outils mixtes causent la confusion « des règles existent mais ne s’appliquent pas ».
Task 6: Check conntrack pressure (NAT pain shows up here)
cr0x@server:~$ sudo conntrack -S
cpu=0 found=120384 invalid=42 ignore=0 insert=120410 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=0
Ce que cela signifie : Les paquets invalides sont peu nombreux ; pas de drops. Conntrack n’est pas en feu pour l’instant.
Décision : Si vous voyez drops/insert_failed augmenter lors de la charge, augmenter les limites conntrack ou réduire le churn NAT/connexion devient urgent. Le mode host « corrige » parfois cela en évitant le NAT, mais c’est un compromis.
Task 7: Verify route and MTU from inside the container
cr0x@server:~$ docker exec api-1 ip route
default via 172.22.0.1 dev eth0
172.22.0.0/16 dev eth0 proto kernel scope link src 172.22.0.10
cr0x@server:~$ docker exec api-1 ip link show eth0
2: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:ac:16:00:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
Ce que cela signifie : La route par défaut est la gateway du bridge ; MTU = 1500.
Décision : Si votre underlay est à 1450 à cause d’un VPN/overlay, configurez le MTU du réseau Docker ou du host pour que le conteneur n’envoie pas de paquets qui seront noircie.
Task 8: See the host-side veth and bridge membership
cr0x@server:~$ ip link show br-77aa88bb99cc
7: br-77aa88bb99cc: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:11:22:33:44 brd ff:ff:ff:ff:ff:ff
cr0x@server:~$ bridge link | grep br-77aa88bb99cc | head
21: veth6d2b1a2@if20: <BROADCAST,MULTICAST,UP,LOWER_UP> master br-77aa88bb99cc state forwarding priority 32 cost 2
Ce que cela signifie : Le pair veth du conteneur est attaché au bridge et en forwarding.
Décision : Si vous ne voyez pas l’interface ou si elle ne forwarde pas, vous avez probablement un problème kernel/bridge ou le conteneur est dans un état de namespace réseau corrompu. Redémarrer Docker peut « régler » le problème, mais capturez d’abord des preuves.
Task 9: Test DNS inside a user-defined bridge network
cr0x@server:~$ docker exec api-1 getent hosts db-1
172.22.0.11 db-1
Ce que cela signifie : Le DNS embarqué de Docker fonctionne ; la résolution conteneur→conteneur est OK.
Décision : Si le DNS échoue ici, ne blâmez pas d’abord votre DNS d’entreprise. Vérifiez que les conteneurs partagent un réseau bridge défini par l’utilisateur ; le bridge par défaut se comporte différemment.
Task 10: Confirm host networking behavior by checking container IP (there won’t be one)
cr0x@server:~$ docker run --rm --network host alpine ip addr show eth0 | sed -n '1,12p'
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 0c:de:ad:be:ef:01 brd ff:ff:ff:ff:ff:ff
inet 10.20.30.40/24 brd 10.20.30.255 scope global eth0
valid_lft forever preferred_lft forever
Ce que cela signifie : Vous voyez l’adressage de l’interface hôte depuis l’intérieur du conteneur. C’est ce que signifie « host network ».
Décision : Si vous avez besoin de règles firewall par conteneur ou d’identités IP distinctes, le mode host n’est pas l’outil adapté.
Task 11: Create a macvlan network with a controlled IP range
cr0x@server:~$ docker network create -d macvlan \
--subnet=10.50.10.0/24 --gateway=10.50.10.1 \
--ip-range=10.50.10.128/25 \
-o parent=eno1 macvlan-net
8f9e0d1c2b3a4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e
Ce que cela signifie : Les conteneurs peuvent se voir attribuer des adresses dans 10.50.10.128/25 tandis que le reste du sous-réseau reste réservé pour d’autres usages.
Décision : Si vous ne pouvez pas réserver une plage propre et la documenter, n’utilisez pas macvlan. Les conflits IP sont des catastrophes en slow-motion.
Task 12: Run a container on macvlan and confirm it has a LAN IP
cr0x@server:~$ docker run -d --name web-mv --network macvlan-net --ip 10.50.10.140 nginx:alpine
c2b1a0f9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b1
cr0x@server:~$ docker exec web-mv ip addr show eth0 | sed -n '1,10p'
2: eth0@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:0a:32:0a:8c brd ff:ff:ff:ff:ff:ff
inet 10.50.10.140/24 brd 10.50.10.255 scope global eth0
Ce que cela signifie : Le conteneur est maintenant un endpoint LAN de première classe.
Décision : Vérifiez la politique de votre switch (sécurité des ports, limites MAC). Si les paquets tombent après quelques conteneurs, ce n’est pas « Docker qui flanche ». C’est votre L2 qui applique des règles.
Task 13: Confirm the classic macvlan limitation: host can’t reach container (default)
cr0x@server:~$ ping -c 2 10.50.10.140
PING 10.50.10.140 (10.50.10.140) 56(84) bytes of data.
--- 10.50.10.140 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1017ms
Ce que cela signifie : Le trafic hôte→enfant macvlan ne fonctionne pas. C’est attendu dans de nombreuses configurations.
Décision : Si des services hôte (agents de sauvegarde, moniteurs locaux, sidecars) doivent parler à ces conteneurs, créez un shim macvlan sur l’hôte.
Task 14: Add a macvlan shim interface on the host to reach macvlan containers
cr0x@server:~$ sudo ip link add macvlan-shim link eno1 type macvlan mode bridge
cr0x@server:~$ sudo ip addr add 10.50.10.2/24 dev macvlan-shim
cr0x@server:~$ sudo ip link set macvlan-shim up
cr0x@server:~$ ping -c 2 10.50.10.140
PING 10.50.10.140 (10.50.10.140) 56(84) bytes of data.
64 bytes from 10.50.10.140: icmp_seq=1 ttl=64 time=0.412 ms
64 bytes from 10.50.10.140: icmp_seq=2 ttl=64 time=0.398 ms
--- 10.50.10.140 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
Ce que cela signifie : L’hôte peut maintenant atteindre le réseau macvlan via le shim.
Décision : Rendez-le persistant (systemd-networkd/NetworkManager) ou il disparaîtra au redémarrage et vous redécouvrirez cette limitation lors d’un incident.
Task 15: Validate ARP/neighbor table sanity on the host
cr0x@server:~$ ip neigh show dev eno1 | head
10.50.10.1 lladdr 00:11:22:33:44:55 REACHABLE
10.50.10.140 lladdr 02:42:0a:32:0a:8c REACHABLE
10.50.10.141 lladdr 02:42:0a:32:0a:8d STALE
Ce que cela signifie : L’hôte apprend des voisins. Si vous voyez beaucoup de FAILED ou un churn constant, macvlan peut stresser le L2/L3.
Décision : Si le churn des voisins corrèle avec des pertes de paquets, réduisez le churn des conteneurs, ajustez les seuils GC, ou reconsidérez macvlan pour cet environnement.
Task 16: Confirm which process owns a port (host mode and “mystery listeners”)
cr0x@server:~$ sudo ss -lntp | grep ':8080'
LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("nginx",pid=21457,fd=6))
Ce que cela signifie : Quelque chose (ici, nginx) possède le port 8080 sur la pile réseau de l’hôte.
Décision : Si vous attendiez une publication de port Docker mais voyez un listener direct, vous êtes peut-être en mode host ou exécutez le service sur l’hôte par erreur. Corrigez le modèle de déploiement avant de poursuivre les recherches sur des problèmes de pare-feu fantômes.
Task 17: Trace packet path quickly with tcpdump (bridge vs host vs macvlan)
cr0x@server:~$ sudo tcpdump -ni br-77aa88bb99cc port 5432 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on br-77aa88bb99cc, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:01:10.112233 IP 172.22.0.10.49822 > 172.22.0.11.5432: Flags [S], seq 123456789, win 64240, options [mss 1460,sackOK,TS val 1 ecr 0,nop,wscale 7], length 0
Ce que cela signifie : Vous voyez du trafic conteneur→conteneur sur le bridge. Cela confirme que le problème n’est pas « les paquets ne quittent jamais le conteneur ».
Décision : Si le trafic apparaît sur le bridge mais pas sur l’uplink, concentrez-vous sur le routage/règles NAT. S’il apparaît sur l’uplink mais n’atteint pas la destination, c’est en amont.
Fast diagnosis playbook
Voici l’ordre qui fait gagner du temps. Pas l’ordre qui vous fait ressentir comme un sorcier réseau.
Première étape : identifier le mode réseau et la portée attendue
- Exécutez
docker inspectsur le conteneur et confirmez s’il est en bridge/host/macvlan. - Clarifiez : l’échec est-il conteneur → internet, conteneur → conteneur, LAN → conteneur, ou hôte → conteneur ?
Astuce goulot : La plupart des problèmes « réseau Docker » sont en réalité des problèmes de mauvaise modélisation mentale. Corrigez le modèle, puis la configuration.
Deuxième étape : vérifier les bases L3 évidentes (dans le conteneur et sur l’hôte)
- Dans le conteneur :
ip addr,ip route, résolution DNS avecgetent hosts. - Sur l’hôte : état du bridge, présence du veth, route vers le subnet, table de voisins (macvlan).
Astuce goulot : Une route par défaut manquante ou un DNS cassé cause 80% des plaintes « je ne peux pas joindre X ».
Troisième étape : valider la politique et la traduction (iptables/nftables, bindings de ports, conntrack)
- Problèmes inbound en bridge : vérifiez les ports publiés et les chaînes NAT iptables.
- Problèmes en host : vérifiez quel processus possède le port, et les règles firewall de l’hôte.
- Problèmes macvlan : vérifiez l’état ARP/voisin et les contraintes/limitations du switch.
- Comportement sous forte charge : vérifiez les stats conntrack et les logs kernel.
Astuce goulot : Si le trafic fonctionne brièvement puis échoue sous charge, supposez une pression conntrack ou une application de L2 en amont avant d’accuser Docker.
Quatrième étape : capturer les paquets à la bonne interface
- Bridge :
tcpdumpsur l’interface du conteneur (intérieure) et sur le bridge (br-*). - Host :
tcpdumpsur l’interface hôte et utilisez des outils de niveau processus pour attribuer le trafic. - Macvlan :
tcpdumpsur l’interface parent et sur le shim macvlan (si utilisé).
Astuce goulot : Si les paquets quittent le conteneur mais n’atteignent jamais le bridge/uplink, vous avez un problème de wiring de namespace. S’ils atteignent l’uplink, c’est en amont.
Common mistakes: symptoms → root cause → fix
1) « Le conteneur n’atteint pas Internet, mais le DNS résout »
Symptôme : getent hosts example.com fonctionne ; curl se bloque ou expire.
Cause racine : Route par défaut absente/incorrecte, règle masquerade NAT cassée, ou pare-feu hôte bloquant le forwarding.
Correction : Vérifiez ip route dans le conteneur ; sur l’hôte, confirmez la MASQUERADE dans iptables -t nat et que le forwarding IP est activé. Assurez-vous que le pare-feu de l’hôte autorise le forwarding depuis le subnet du bridge.
2) « Le service est up, mais rien ne peut se connecter depuis l’extérieur » (bridge)
Symptôme : Le conteneur écoute sur 0.0.0.0:8080 en interne, mais les clients LAN ne peuvent pas se connecter.
Cause racine : Port non publié, ou publié seulement sur localhost, ou pare-feu hôte bloquant le port publié.
Correction : Vérifiez docker ps pour 0.0.0.0:hostport->containerport. Si manquant, ajoutez -p. Puis vérifiez que le pare-feu hôte autorise l’entrée.
3) « Deux conteneurs flappent sans cesse, parfois l’un ne démarre pas » (host)
Symptôme : Boucles de redémarrage ou erreurs de bind intermittentes ; logs mentionnent « address already in use. »
Cause racine : Le mode host partage le namespace de ports ; les deux services veulent le même port.
Correction : Cessez d’utiliser le networking host pour les deux, ou repensez l’allocation des ports. En mode host, traitez l’allocation de ports comme une ressource globale par hôte.
4) « Les conteneurs macvlan fonctionnent depuis le LAN, mais l’hôte ne peut pas les atteindre »
Symptôme : Depuis une autre machine sur le sous-réseau, vous pouvez vous connecter ; depuis l’hôte Docker, cela échoue.
Cause racine : Le comportement macvlan par défaut empêche la communication hôte→enfant sur le même parent.
Correction : Ajoutez une interface shim macvlan sur l’hôte avec une IP dans le même subnet et routez via elle (ou choisissez ipvlan L3 si approprié pour votre environnement).
5) « Tout a marché jusqu’à ce qu’on ajoute des conteneurs ; puis des timeouts aléatoires » (macvlan)
Symptôme : Les nouveaux conteneurs sont parfois joignables ; ARP semble instable ; les logs du switch se plaignent.
Cause racine : Sécurité des ports du switch ou limites d’adresses MAC, pression sur la table CAM, ou limitation du taux ARP.
Correction : Coordonnez-vous avec l’équipe réseau : augmentez les limites MAC sur le port, désactivez la sécurité stricte si approprié, ou évitez macvlan sur ce segment.
6) « Certaines requêtes se bloquent, surtout les réponses volumineuses » (tous modes)
Symptôme : Petits pings OK ; gros transferts bloquent ; handshakes TLS échouent parfois.
Cause racine : Incompatibilité MTU et PMTU défaillant.
Correction : Mesurez la MTU effective, réglez le MTU du réseau Docker ou ajustez les MTU d’interface de façon cohérente, et validez avec des pings DF-bit.
7) « Conteneur→conteneur fonctionne par IP, pas par nom » (bridge)
Symptôme : ping 172.22.0.11 fonctionne ; ping db-1 échoue.
Cause racine : Les conteneurs ne sont pas sur le même réseau bridge défini par l’utilisateur, ou vous utilisez le bridge par défaut sans le comportement DNS approprié.
Correction : Placez les deux conteneurs sur le même réseau bridge défini par l’utilisateur et utilisez les noms de conteneur (ou des alias explicites) là-bas.
Checklists / step-by-step plan
Étape par étape : choisir le driver en sécurité
- Rédigez la matrice de reachabilité. Qui doit parler à qui (LAN → conteneur, conteneur → LAN, hôte → conteneur, conteneur → internet).
- Décidez des exigences d’identité. Les systèmes en amont exigent-ils des IP par workload ? Si oui, macvlan peut être requis ; sinon, préférez bridge.
- Décidez du modèle d’exposition. Voulez-vous des ports publiés explicitement (bridge) ou des ports partagés de l’hôte (host) ? Par défaut, optez pour l’explicite.
- Vérifiez votre modèle de propriété du pare-feu. Si le pare-feu hôte est géré centralement et que les changements Docker sont mal vus, planifiez l’intégration soigneusement (bridge) ou évitez les patterns lourds en NAT.
- Vérifiez la politique de votre switch si macvlan est envisagé (limites MAC, sécurité de port, inspection ARP). Faites-le avant le déploiement.
- Choisissez le modèle le plus simple qui satisfait les besoins. Puis documentez-le pour que la prochaine personne n’« optimise » pas sans réfléchir.
Checklist production pour le réseau bridge
- Utilisez des réseaux bridge définis par l’utilisateur, pas le
bridgepar défaut, pour les vraies apps. - Choisissez des sous-réseaux qui ne se chevauchent pas avec VPN/plages datacenter.
- Publiez uniquement les ports requis ; liez-les à des IP hôtes spécifiques si possible.
- Décidez comment vous gérez iptables/nftables (et testez après les upgrades OS).
- Validez la MTU de bout en bout et réglez-la intentionnellement.
Checklist production pour le mode host
- Réservez le mode host aux workloads qui le justifient (débit de paquets, vrais démons réseau, agents hôte).
- Tenez une carte de propriété des ports par hôte (ou faites-en respecter via l’automatisation).
- Durcissez les règles du pare-feu hôte ; ne comptez pas sur les paramètres par défaut des conteneurs.
- Vérifiez l’attribution observabilité : pouvez-vous rattacher le trafic à un conteneur/cgroup ?
Checklist production pour macvlan
- Réservez une plage IP documentée ; évitez de mélanger avec le DHCP sauf si vous savez ce que vous faites.
- Confirmez que la politique de switch supporte plusieurs MAC par port et ne fera pas flapper.
- Planifiez l’accès hôte→conteneur (interface shim) si nécessaire.
- Surveillez le comportement ARP/table de voisins et les limites de taux.
- Décidez qui gère le DNS : vous voudrez des enregistrements forward/reverse si les outils d’entreprise les attendent.
Three corporate mini-stories (because you will repeat them otherwise)
Mini-story 1: an incident caused by a wrong assumption (macvlan host reachability)
Une société de taille moyenne containerisait un service de reporting legacy qui devait être joignable par un ensemble de jobs batch en amont. Ils ont choisi macvlan parce que chaque job upstream avait une allowlist codée en dur des IP de destination, et réécrire la politique aurait coûté du capital politique qu’ils n’avaient pas.
La mise en staging semblait correcte. Depuis d’autres machines sur le subnet, ils pouvaient atteindre les IP des conteneurs directement. Le déploiement en production s’est fait un vendredi après-midi, parce que bien sûr, et immédiatement leurs checks de santé sur l’hôte ont commencé à échouer. L’orchestrateur a marqué les instances comme unhealthy et les a redémarrées. Maintenant le service flappait, ce qui faisait échouer davantage de jobs en amont, ce qui déclenchait des retries, et tout devenait plus bruyant.
L’hypothèse erronée : « Si le LAN peut l’atteindre, l’hôte peut l’atteindre. » Avec macvlan, le chemin hôte→enfant est une exception connue dans de nombreuses configurations. Leurs checks de santé tournaient dans le namespace réseau de l’hôte et ne pouvaient pas atteindre l’IP du conteneur macvlan. Le service fonctionnait ; l’hôte ne le voyait tout simplement pas.
La correction fut banale : créer une interface shim macvlan sur chaque hôte, lui attribuer une IP dans le subnet macvlan, et mettre à jour les checks de santé pour utiliser ce chemin. Ils ont aussi documenté la contrainte pour que personne ne « simplifie » cela plus tard.
Mini-story 2: an optimization that backfired (host networking to avoid NAT)
Une autre organisation exécutait une pipeline d’ingestion de métriques à haut débit. Quelqu’un a remarqué les compteurs conntrack monter sous la charge et a décidé que le NAT bridge « gaspillait du CPU ». La solution proposée était simple : déplacer les conteneurs d’ingestion en --network host pour que les paquets évitent NAT et conntrack totalement.
Dans un benchmark limité, cela a marché. Le CPU a baissé, la latence s’est améliorée, et tout le monde s’est senti avoir découvert un cheat code. Le changement a été déployé graduellement sur une flotte.
Puis des bizarreries ont commencé : un sous-ensemble d’hôtes a eu des échecs d’ingestion intermittents après les déploiements. Pas tous les hôtes, pas tout le temps. La cause racine était des collisions de ports entre le service d’ingestion et un sidecar de debug qui bindait aussi un port UDP. En mode bridge, ils pouvaient coexister via des namespaces séparés. En mode host, le deuxième service ne pouvait tout simplement pas binder. Parfois l’ordre de déploiement masquait le problème ; parfois ça explosait.
Ils ont reverti le networking host pour la couche d’ingestion et ont plutôt ajusté la capacité conntrack et réduit le churn de connexions avec du batching. Le véritable goulot n’était pas « le NAT est lent ». C’était « nous avons créé trop de flux très courts ». Le mode host traitait le symptôme et introduisait une nouvelle classe de pannes plus difficile à raisonner.
Ensuite, ils ont ajouté une règle : tout changement qui augmente le rayon d’impact (comme host networking) nécessite un modèle de menace écrit et un plan de rollback. Cette politique était moins spectaculaire que le graphe du benchmark, mais elle a porté ses fruits.
Mini-story 3: boring but correct practice that saved the day (user-defined bridge + explicit publishing)
Une équipe de services financiers exécutait plusieurs services clients sur des hôtes partagés. Ils se sont standardisés sur des réseaux bridge définis par l’utilisateur par stack applicative et publiaient les ports explicitement, en les liant à des interfaces hôtes spécifiques. Ce n’était pas à la mode. C’était aussi résilient.
Une nuit, une mise à jour d’une image vendor a introduit un nouveau listener de debug dans le conteneur. Rien de malveillant ; juste un de ces defaults « whoops, laissé activé ». Le service lui-même fonctionnait toujours normalement.
Parce que l’équipe utilisait la publication explicite de ports, le nouveau port interne est resté interne. Il n’est pas apparu soudainement sur l’hôte, et il n’est pas devenu joignable depuis le LAN. La supervision n’a pas hurlé, la sécurité n’a pas paniqué, et l’incident n’est jamais devenu un titre.
Le postmortem fut court : « Nous n’avons pas eu à nous en préoccuper parce que nous ne l’exposions pas. » Voilà le type de victoire ennuyeuse que vous ne remarquez que quand vous la comparez à la timeline alternative.
FAQ
1) Le networking bridge est-il toujours plus lent que host ?
Non. Le mode bridge ajoute un overhead (veth, lookup bridge, souvent NAT/conntrack), mais pour beaucoup de workloads ce n’est pas votre goulot. Mesurez avant de « corriger » cela. Si vous ne saturiez pas le CPU sur softirq/conntrack, le bridge est généralement suffisant.
2) Pourquoi préférer un bridge défini par l’utilisateur au bridge par défaut ?
Les bridges définis par l’utilisateur fournissent un meilleur comportement DNS/découverte de service, une séparation plus claire et des configurations multi-réseaux plus prévisibles. Le bridge par défaut est adapté à la rétrocompatibilité, pas à la production.
3) Quand --network host est-il une bonne idée ?
Quand le workload a vraiment besoin des sémantiques réseau directes de l’hôte : débits de paquets élevés où le NAT est pénalisant, démons réseau, ou agents hôte. Aussi quand l’hôte est effectivement mono-usage. Sinon, le mode host augmente le rayon d’impact et la complexité du débogage.
4) Pourquoi l’hôte ne peut-il pas atteindre par défaut les conteneurs macvlan ?
Parce que macvlan isole le trafic entre l’interface parent et les endpoints macvlan d’une manière qui empêche la pile de l’hôte de parler directement à ses enfants sur cette même interface. Le contournement courant est une interface shim macvlan sur l’hôte.
5) Puis-je utiliser macvlan sur du Wi‑Fi ?
Parfois, mais c’est souvent pénible. Beaucoup de drivers Wi‑Fi et d’AP ne gèrent pas plusieurs adresses MAC source par station comme vous le souhaitez. Si vous devez essayer, faites-le en labo d’abord et préparez-vous à être déçu.
6) Dois‑je utiliser macvlan juste pour éviter les conflits de ports ?
Généralement non. Les conflits de ports se résolvent mieux avec le réseau bridge et la publication de ports, ou en plaçant un reverse proxy/ingress en frontal. Macvlan échange les conflits de ports contre la complexité IPAM et L2.
7) Comment empêcher Docker de modifier mon pare-feu ?
Vous pouvez contraindre le comportement de Docker, mais vous ne pouvez pas prétendre qu’il n’a pas besoin de règles si vous voulez NAT/ports publiés. L’approche pratique est de décider si le pare-feu hôte est « Docker-aware » et de tester l’ordre des règles après les upgrades. Si votre environnement interdit les changements dynamiques de pare-feu, repensez le design autour d’un routage ou de load balancers en amont.
8) Quelle est la manière la plus sûre d’exposer des services en mode bridge ?
Publiez uniquement les ports requis, liez-les à une IP hôte spécifique quand c’est approprié (par exemple interface interne seulement), et appliquez des règles pare-feu sur l’hôte. Traitez les ports publiés comme une surface API externe.
9) Comment gérer les logs et la visibilité de l’IP client avec le NAT du bridge ?
Vous pouvez perdre l’IP source originale si le trafic est proxyfié/NATé selon le chemin. Utilisez des reverse proxies qui transmettent X-Forwarded-For ou le proxy protocol quand c’est applicable, ou évitez le NAT pour ce saut (macvlan/host ou un design routé) si l’IP client réelle est obligatoire.
Next steps you can do this week
- Inventairez vos hôtes : listez les conteneurs et notez lesquels utilisent le networking host et pourquoi. Si « parce que ça marchait » est la raison, ce n’est pas une raison.
- Migrez une stack vers un bridge défini par l’utilisateur si vous utilisez encore le
bridgepar défaut. Validez la résolution de noms et la discipline de publication de ports. - Choisissez un plan de sous-réseau non chevauchant pour les bridges Docker entre environnements (dev/stage/prod). Les chevauchements sont une fuite lente qui devient un déluge.
- Décidez de votre politique macvlan : soit « autorisé avec validation du switch et réservation de plage IP », soit « interdit ». L’ambiguïté est la façon dont macvlan arrive en production sans adultes dans la salle.
- Rédigez un runbook d’une page en utilisant le Fast diagnosis playbook ci‑dessus et incluez les commandes exactes que votre on‑call peut exécuter sans réfléchir.
Si vous voulez une règle qui tient sous pression : utilisez bridge jusqu’à ce que vous puissiez nommer la limitation précise du bridge que vous rencontrez. Ensuite, choisissez host ou macvlan comme exception délibérée, pas par intuition.