11 - Networking Docker, les bases
Ce que tu vas apprendre
- Comment fonctionne le réseau bridge par défaut
- Pourquoi le nom du service est le hostname dans Compose
- La différence entre expose et ports
- Le piège de localhost dans un conteneur
- Comment contacter la machine hote depuis un conteneur
- Le mapping de ports en détail
Prerequisites
- Avoir suivi les bases de Docker Compose
- Comprendre les bases TCP/IP (ports, hostname, DNS)
Le networking Docker est la source numero un de bugs mysterieux chez les devs qui debutent. "Ca marche sur ma machine mais pas dans le conteneur." Neuf fois sur dix, c'est un problème de réseau. Et la raison est simple : un conteneur a son propre espace réseau. Il ne voit pas le meme localhost que toi.
Le réseau bridge par défaut
Quand tu demarres un conteneur sans rien spécifier, Docker le place sur le réseau bridge par défaut. C'est un réseau interne, isole de ta machine hote.
bashdocker network ls
Tu verras un réseau bridge, un host et un none. Chaque conteneur sur bridge recoit une adresse IP interne (typiquement dans la plage 172.17.0.x).
Quand tu utilises Docker Compose, c'est différent. Compose créé un réseau dédié pour chaque projet, nomme <nom-du-dossier>_default. Les services du meme compose.yaml sont tous sur ce réseau.
Le DNS interne : le nom du service = le hostname
C'est une des fonctionnalités les plus pratiques de Compose, et beaucoup de devs ne la comprennent pas vraiment.
Dans ton compose.yaml :
yamlservices:
app:
build: .
environment:
DATABASE_URL: postgres://postgres:secret@db:5432/myapp
db:
image: postgres:16-alpine
Le db dans l'URL n'est pas magique. Compose créé un enregistrement DNS interne : le nom du service (db) pointe vers l'IP du conteneur PostgreSQL. Pas besoin de connaître l'IP, pas besoin de config supplementaire.
Sur paltemps.fr, j'ai trois services qui communiquent entre eux uniquement par leurs noms de service. Jamais une IP en dur.
expose vs ports
Ces deux directives sont souvent confondues :
yamlservices:
app:
ports:
- "3000:3000" # accessible depuis l'hote ET les autres conteneurs
expose:
- "9090" # accessible UNIQUEMENT depuis les autres conteneurs
ports mappe un port du conteneur vers un port de ta machine. Tu peux acceder au service depuis ton navigateur.
expose déclaré un port disponible sur le réseau interne Docker. Les autres conteneurs peuvent y acceder, mais pas ta machine hote. En pratique, expose est surtout documentaire : les conteneurs peuvent toujours communiquer entre eux sur n'importe quel port du réseau interne, meme sans expose.
La regle simple : utilise ports pour ce que tu veux atteindre depuis ton navigateur ou tes outils. Laisse tout le reste en interne.
Le piège de localhost
C'est le piège classique. Tu as une app qui écoûte sur localhost:5432 en local. Tu la mets dans un conteneur, et soudain elle ne trouve plus la base de donnees.
Pourquoi ? Parce que localhost dans un conteneur, c'est le conteneur lui-meme. Pas ta machine. Pas le conteneur d'a cote.
[Ta machine]
localhost:5432 --> PostgreSQL local
[Conteneur app]
localhost:5432 --> rien (il n'y a pas de PostgreSQL dans ce conteneur)
La solution : utiliser le nom du service comme hostname :
yaml# Dans le conteneur app, pour joindre PostgreSQL :
DATABASE_URL: postgres://postgres:secret@db:5432/myapp
# ^^ nom du service, pas localhost
Si ton app écoûte sur 127.0.0.1 (localhost only), elle ne sera pas accessible depuis l'extérieur du conteneur meme avec ports. Il faut écoûter sur 0.0.0.0 :
javascript// Mauvais - accessible uniquement dans le conteneur
app.listen(3000, "127.0.0.1");
// Bon - accessible depuis l'hote via le port mapping
app.listen(3000, "0.0.0.0");
host.docker.internal
Parfois tu as besoin de joindre un service qui tourne sur ta machine hote (pas dans Docker). L'hostname special host.docker.internal pointe vers ta machine :
yamlservices:
app:
environment:
API_URL: http://host.docker.internal:8080
Ca fonctionne sur Docker Desktop (Mac, Windows, Linux). Sur Linux sans Docker Desktop, tu dois ajouter la config manuellement :
yamlservices:
app:
extra_hosts:
- "host.docker.internal:host-gateway"
J'utilise ca quand je développé un microservice dans Docker qui doit parler a un autre service que je lance directement sur ma machine avec node index.js.
Le mapping de ports en détail
Le format complet :
yamlports:
- "3000:3000" # host:container (toutes les interfaces)
- "127.0.0.1:3000:3000" # seulement localhost de l'hote
- "3000" # port aleatoire de l'hote -> 3000 du conteneur
La version 127.0.0.1:3000:3000 est plus sécurisée : le port n'est accessible que depuis ta machine, pas depuis le réseau local. En dev, ca change rarement quelque chose. En production sur un serveur, ca peut éviter une exposition accidentelle.
Pour voir les ports mappes :
bashdocker compose ps
Ou pour un conteneur spécifique :
bashdocker port <container_id>
Résumé
- Docker Compose créé un réseau interne par projet : les services se trouvent par leur nom
localhostdans un conteneur = le conteneur lui-meme, pas ta machine- Écoûte toujours sur
0.0.0.0dans un conteneur, jamais sur127.0.0.1 host.docker.internalpermet de joindre la machine hote depuis un conteneurportsexpose vers l'hote,exposereste interne au réseau Docker- On verra les réseaux custom et l'isolation dans l'article suivant
Navigation : Precedent : 10 - Compose avance | Suivant : 12 - Networking avance
Sources
- Docker networking overview par Docker
- Compose networking par Docker
- host.docker.internal par Docker Desktop