Contactez-nous

Communication entre conteneurs sur un même réseau

Découvrez comment les conteneurs Docker sur le même réseau communiquent facilement entre eux, notamment grâce à la résolution DNS intégrée par nom.

Le principe fondamental : connectivité au sein d'un réseau

L'un des avantages majeurs de placer plusieurs conteneurs sur un même réseau Docker défini par l'utilisateur est la facilité avec laquelle ils peuvent communiquer entre eux. Par défaut, Docker configure le routage et les règles de pare-feu nécessaires pour permettre à tous les conteneurs attachés à un réseau spécifique d'établir des connexions TCP et UDP les uns avec les autres.

Cette connectivité directe est essentielle pour les architectures modernes, notamment les microservices, où différentes parties d'une application (par exemple, un serveur web, une API, une base de données, un service de cache) s'exécutent dans des conteneurs séparés mais doivent interagir constamment. L'isolation est maintenue vis-à-vis de l'extérieur et des autres réseaux Docker, mais la communication est ouverte au sein du même réseau logique.

Il est crucial de comprendre que cette facilité de communication s'applique principalement aux réseaux définis par l'utilisateur (créés avec `docker network create`). Le réseau `bridge` par défaut a des limitations historiques concernant la découverte de services par nom, ce qui renforce la recommandation d'utiliser systématiquement des réseaux personnalisés pour vos applications.

Communication directe par adresse IP

La forme la plus basique de communication entre conteneurs sur le même réseau est l'utilisation de leurs adresses IP respectives. Chaque conteneur connecté à un réseau Docker se voit attribuer une adresse IP unique au sein de ce réseau par le gestionnaire d'adresses IP (IPAM) de Docker.

Vous pouvez trouver l'adresse IP d'un conteneur sur un réseau spécifique en utilisant la commande `docker inspect`. Par exemple, pour trouver l'IP du conteneur `mon-app` sur le réseau `mon-reseau` :

docker inspect mon-app | grep -A 5 "mon-reseau" 
# Ou une approche plus robuste avec --format
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mon-app

Une fois l'adresse IP obtenue (par exemple, `172.18.0.3`), un autre conteneur sur le même réseau (`172.18.0.0/16` dans cet exemple) peut s'y connecter directement, en supposant qu'aucun pare-feu à l'intérieur du conteneur cible ne bloque la connexion.

# Depuis un autre conteneur sur 'mon-reseau'
ping 172.18.0.3
curl http://172.18.0.3:8080

Cependant, se fier aux adresses IP présente un inconvénient majeur : elles peuvent changer si un conteneur est recréé. Les adresses IP sont généralement attribuées dynamiquement. Pour une communication fiable et découplée, il est fortement déconseillé de coder en dur les adresses IP dans la configuration de vos applications.

La puissance du DNS intégré : communication par nom

C'est ici que la magie des réseaux Docker définis par l'utilisateur opère. Docker intègre un serveur DNS qui fournit automatiquement la résolution de noms pour les conteneurs au sein d'un même réseau défini par l'utilisateur. Cela signifie qu'un conteneur peut joindre un autre conteneur sur le même réseau simplement en utilisant son nom de conteneur comme nom d'hôte.

Lorsque vous lancez un conteneur avec un nom (via `docker run --name mon-app ...`) et que vous l'attachez à un réseau défini par l'utilisateur (`--network mon-reseau`), Docker enregistre automatiquement ce nom (`mon-app`) dans son serveur DNS interne pour ce réseau spécifique. Tout autre conteneur sur `mon-reseau` pourra alors résoudre `mon-app` vers l'adresse IP correcte actuelle de ce conteneur.

Exemple : Lançons deux conteneurs Nginx sur un réseau personnalisé `web-net` :

docker network create web-net
docker run -d --name server1 --network web-net nginx:alpine
docker run -d --name server2 --network web-net nginx:alpine

Maintenant, depuis `server1`, nous pouvons pinger `server2` par son nom :

docker exec server1 ping server2

PING server2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.101 ms
...

De même, `server2` peut joindre `server1`. C'est cette fonctionnalité qui permet, par exemple, à un conteneur applicatif de se connecter à un conteneur de base de données simplement en utilisant le nom du conteneur de la base de données comme hôte dans sa chaîne de connexion (ex: `jdbc:postgresql://db-container:5432/mydatabase`).

Pourquoi éviter le réseau `bridge` par défaut pour la communication par nom

Il est important de souligner que cette résolution DNS automatique par nom de conteneur n'est pas activée par défaut sur le réseau `bridge` historique (celui nommé `bridge` ou `docker0`). Si vous lancez deux conteneurs sans spécifier `--network` (ils atterrissent donc sur le `bridge` par défaut), ils ne pourront pas se résoudre mutuellement par leur nom de conteneur.

Historiquement, Docker proposait une fonctionnalité appelée "linking" (`--link`) pour établir des connexions et un semblant de découverte entre conteneurs sur le réseau `bridge` par défaut. Cependant, le linking est maintenant considéré comme une fonctionnalité héritée (legacy) et fortement déconseillée. Elle présente plusieurs limitations et est moins flexible que les réseaux définis par l'utilisateur.

La recommandation moderne est claire : toujours créer et utiliser des réseaux définis par l'utilisateur (généralement de type `bridge` ou `overlay`) pour vos applications multi-conteneurs afin de bénéficier de la résolution DNS intégrée et d'une meilleure isolation.

Utilisation des alias réseau

En plus de pouvoir joindre un conteneur par son nom, Docker permet de lui assigner des alias réseau spécifiques à un réseau donné. Un alias est un nom DNS alternatif qui résoudra également vers l'adresse IP du conteneur sur ce réseau. C'est utile si vous voulez qu'un service soit joignable sous un nom plus générique ou si un même conteneur doit être accessible sous différents noms pour différents consommateurs sur le même réseau.

Les alias peuvent être définis lors de la connexion initiale au réseau avec `docker run --network-alias ` ou en connectant un conteneur existant avec `docker network connect --alias ...`.

# Lance un conteneur PostgreSQL avec un alias 'database' sur 'app-net'
docker run -d --name pg-instance --network app-net --network-alias database postgres:15

Maintenant, tout autre conteneur sur `app-net` peut se connecter à ce serveur PostgreSQL en utilisant soit `pg-instance`, soit `database` comme nom d'hôte.

Dans Docker Compose, les alias sont définis avec la clé `networks: ... aliases:` sous la définition d'un service. C'est une pratique courante pour définir des noms de service stables (comme `db`, `cache`, `api`) indépendamment du nom réel du conteneur (qui peut inclure des numéros d'instance, etc.).

Exemple concret et bonnes pratiques

Imaginons une application simple avec un frontend `web` (Node.js) et un backend `api` (Python/Flask) sur un réseau `frontend-backend-net`.

docker network create frontend-backend-net

docker run -d --name frontend --network frontend-backend-net my-nodejs-image
docker run -d --name api --network frontend-backend-net my-python-image

Dans le code du conteneur `frontend`, la configuration pour appeler l'API pourrait ressembler à : `API_URL=http://api:5000/`. Le nom `api` sera automatiquement résolu par le DNS de Docker vers l'IP du conteneur `api` sur le réseau `frontend-backend-net`.

Bonnes pratiques :
  • Toujours utiliser des réseaux définis par l'utilisateur pour les applications multi-conteneurs.
  • Utiliser la résolution par nom (nom du conteneur ou alias réseau) pour la communication inter-conteneurs, plutôt que les adresses IP.
  • Choisir des noms de conteneurs et des alias significatifs et stables.
  • S'assurer que les ports nécessaires sont bien exposés et écoutés par les applications dans les conteneurs cibles. La résolution DNS fonctionne, mais la connexion ne réussira que si l'application écoute sur le port attendu.

Maîtriser la communication inter-conteneurs via les réseaux définis par l'utilisateur et la résolution DNS est une compétence clé pour déployer efficacement des applications modernes avec Docker.