Réseau Docker#
Le réseau est l’un des aspects les plus souvent mal compris de Docker. Comment deux conteneurs se parlent-ils ? Comment un conteneur communique-t-il avec l’extérieur ? Comment isole-t-on des groupes de conteneurs ? Ce chapitre répond à toutes ces questions, des mécanismes noyau jusqu’aux commandes du quotidien.
Les drivers réseau de Docker#
Docker propose plusieurs drivers réseau, chacun adapté à un cas d’usage différent. Choisir le bon driver est essentiel pour la sécurité, les performances et la connectivité de vos applications.
Driver |
Portée |
Description |
|---|---|---|
|
Machine locale |
Réseau privé virtuel sur l’hôte (défaut) |
|
Machine locale |
Partage le namespace réseau de l’hôte |
|
Machine locale |
Aucune interface réseau (isolation totale) |
|
Multi-hôtes |
Réseau étendu entre plusieurs machines (Docker Swarm) |
|
Machine locale |
Attribue une adresse MAC physique au conteneur |
|
Machine locale |
Similaire à macvlan, L2/L3 configurable |
Règle d’or
Pour 90 % des cas de développement local, vous utiliserez le driver bridge. En production avec Docker Swarm ou pour des besoins réseau avancés, overlay ou macvlan entrent en jeu.
Le réseau bridge : au cœur du réseau Docker#
L’interface docker0#
Quand Docker est installé, il crée automatiquement une interface réseau virtuelle nommée docker0 sur l’hôte. C’est le pont (bridge) entre les conteneurs et le réseau extérieur.
# Sur l'hôte, vous pouvez voir l'interface docker0
ip addr show docker0
# Résultat typique :
# 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP>
# inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
L’interface docker0 a l’adresse 172.17.0.1 et gère le sous-réseau 172.17.0.0/16. Chaque conteneur lancé sur le réseau bridge par défaut reçoit une adresse IP dans ce plage.
Les paires veth#
La connexion entre un conteneur et le bridge docker0 se fait via des paires d’interfaces virtuelles (veth pairs). C’est comme un câble Ethernet virtuel : un bout est dans le conteneur (nommé eth0), l’autre bout est sur l’hôte (nommé vethXXXXXX).
# Sur l'hôte, voir les interfaces veth créées par Docker
ip link show type veth
# Résultat : une paire par conteneur en cours d'exécution
# veth3a1b2c3 correspond à eth0 dans le conteneur
NAT et iptables#
Pour permettre à un conteneur d’accéder à Internet, Docker configure automatiquement des règles iptables sur l’hôte :
MASQUERADE : le trafic sortant du conteneur est traduit (NAT) avec l’IP de l’hôte
FORWARD : les paquets sont autorisés à transiter via le bridge
# Voir les règles NAT créées par Docker
sudo iptables -t nat -L -n --line-numbers
# Règle MASQUERADE typique :
# MASQUERADE all -- 172.17.0.0/16 !172.17.0.0/16
Le DNS interne de Docker#
L’une des fonctionnalités les plus pratiques de Docker est son serveur DNS embarqué. Sur un réseau bridge personnalisé (pas le bridge par défaut docker0), les conteneurs peuvent se joindre par leur nom plutôt que par leur adresse IP.
# Créer un réseau bridge personnalisé
docker network create mon-reseau
# Lancer deux conteneurs sur ce réseau
docker run -d --name web --network mon-reseau nginx
docker run -d --name db --network mon-reseau postgres
# Dans le conteneur "web", on peut pinger "db" par son nom !
docker exec web ping db
# PING db (172.18.0.3): 56 data bytes
# 64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.089 ms
Bridge par défaut vs bridge personnalisé
Sur le réseau bridge par défaut (docker0), le DNS par nom de conteneur ne fonctionne pas. Les conteneurs ne peuvent se joindre que par IP. C’est pourquoi il est fortement recommandé de toujours créer un réseau bridge nommé pour vos applications. Docker Compose fait cela automatiquement.
Les commandes réseau Docker#
Créer et inspecter des réseaux#
# Lister tous les réseaux
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# a1b2c3d4e5f6 bridge bridge local
# f6e5d4c3b2a1 host host local
# 123456789abc none null local
# Créer un réseau bridge personnalisé
docker network create mon-reseau
# Créer avec un subnet et gateway spécifiques
docker network create \
--driver bridge \
--subnet 192.168.100.0/24 \
--gateway 192.168.100.1 \
reseau-prod
# Inspecter un réseau (voir les conteneurs connectés, la config IP...)
docker network inspect mon-reseau
Connecter et déconnecter des conteneurs#
# Connecter un conteneur existant à un réseau
docker network connect mon-reseau mon-conteneur
# Déconnecter
docker network disconnect mon-reseau mon-conteneur
# Un conteneur peut appartenir à plusieurs réseaux simultanément !
docker network connect reseau-frontend mon-conteneur
docker network connect reseau-backend mon-conteneur
Nettoyage#
# Supprimer un réseau (seulement s'il n'est plus utilisé)
docker network rm mon-reseau
# Supprimer tous les réseaux inutilisés
docker network prune
Le réseau host#
Avec le driver host, le conteneur partage directement le namespace réseau de l’hôte. Il n’y a plus de NAT, plus d’interface virtuelle séparée : le conteneur « est » l’hôte du point de vue réseau.
# Le conteneur utilise directement le réseau de l'hôte
docker run --network host nginx
# nginx écoute sur le port 80 de l'HÔTE directement
# Pas besoin de -p 80:80 — le port est déjà celui de l'hôte
Quand utiliser le réseau host ?
Le mode host est utile pour :
Performances maximales : zéro overhead de NAT (trading haute fréquence, analyse réseau)
Outils de monitoring réseau : qui doivent accéder aux interfaces de l’hôte
Applications qui gèrent elles-mêmes des ports dynamiques (ex. : serveurs FTP passif)
Inconvénient majeur : le conteneur peut écouter sur n’importe quel port de l’hôte → risque de sécurité. À éviter en production si possible.
Le driver none : isolation totale#
# Conteneur sans aucune connectivité réseau
docker run --network none mon-image
# Utile pour :
# - Traitement de données sensibles (aucune exfiltration possible)
# - Jobs de calcul pur sans besoin réseau
# - Sécurité maximale
Le réseau overlay : Docker multi-hôtes#
Le driver overlay permet de créer un réseau virtuel s’étendant sur plusieurs hôtes physiques. Il utilise le protocole VXLAN (Virtual Extensible LAN) pour encapsuler le trafic réseau des conteneurs dans des paquets UDP échangés entre les hôtes.
Overlay et Docker Swarm
Le driver overlay nécessite soit Docker Swarm (mode cluster intégré à Docker) soit un key-value store externe (comme etcd). En pratique, si vous avez besoin d’overlay, vous utilisez Docker Swarm ou vous passez à Kubernetes qui gère cela nativement.
# Initialiser Docker Swarm (mode cluster)
docker swarm init --advertise-addr 192.168.1.10
# Créer un réseau overlay
docker network create --driver overlay mon-overlay
# Les services Swarm peuvent utiliser ce réseau
docker service create --network mon-overlay --name web nginx
Publication de ports#
La publication de ports permet d’accéder à un conteneur depuis l’extérieur de l’hôte. Docker crée des règles iptables pour rediriger le trafic entrant vers le bon conteneur.
# -p HOST_PORT:CONTAINER_PORT
docker run -p 8080:80 nginx
# Le port 80 du conteneur est accessible via le port 8080 de l'hôte
# Lier à une IP spécifique (sécurité : écoute uniquement sur localhost)
docker run -p 127.0.0.1:8080:80 nginx
# Accessible uniquement depuis la machine locale, pas depuis le réseau
# Port aléatoire sur l'hôte (-P, expose tous les ports déclarés dans l'image)
docker run -P nginx
# Docker choisit un port disponible (ex: 32768)
# Voir les ports publiés
docker port mon-conteneur
# 80/tcp -> 0.0.0.0:8080
Liaison à 127.0.0.1 — Bonne pratique de sécurité
En développement, il est courant d’utiliser -p 8080:80 qui lie le port à toutes les interfaces (0.0.0.0). En production ou sur un serveur exposé, préférez -p 127.0.0.1:8080:80 et mettez un reverse proxy (nginx, Traefik) devant. Cela évite d’exposer accidentellement des services de développement (bases de données, outils d’administration) sur Internet.
Simulation Python : allocation d’IP dans un subnet Docker#
Docker utilise le module réseau du noyau Linux pour allouer les adresses IP. Voici une simulation de la logique d’allocation en Python pur avec le module ipaddress de la bibliothèque standard.
import ipaddress
import random
from collections import OrderedDict
class DockerIPAMSimulator:
"""Simulation simplifiée du gestionnaire d'adresses IP (IPAM) de Docker."""
def __init__(self, subnet: str, gateway: str = None):
self.network = ipaddress.IPv4Network(subnet, strict=False)
self.hosts = list(self.network.hosts())
# Le gateway est typiquement la première IP (.1)
if gateway:
self.gateway = ipaddress.IPv4Address(gateway)
else:
self.gateway = self.hosts[0]
# Réserver le gateway
self.allocated: OrderedDict = OrderedDict()
self.allocated[str(self.gateway)] = "gateway (docker0)"
self.next_idx = 1 # Commence après le gateway
def allocate(self, container_name: str) -> str:
"""Alloue la prochaine IP disponible à un conteneur."""
while self.next_idx < len(self.hosts):
ip = self.hosts[self.next_idx]
self.next_idx += 1
ip_str = str(ip)
if ip_str not in self.allocated:
self.allocated[ip_str] = container_name
return ip_str
raise RuntimeError("Plus d'adresses IP disponibles dans ce subnet !")
def release(self, container_name: str):
"""Libère l'IP d'un conteneur (mais Docker ne la réutilise pas immédiatement)."""
for ip, name in list(self.allocated.items()):
if name == container_name:
del self.allocated[ip]
print(f" IP {ip} libérée (conteneur '{container_name}' supprimé)")
return
print(f" Conteneur '{container_name}' non trouvé")
def status(self):
"""Affiche l'état actuel des allocations."""
print(f"\nRéseau : {self.network}")
print(f"Capacité : {self.network.num_addresses - 2} hôtes disponibles")
print(f"Allocations actuelles ({len(self.allocated)}) :")
for ip, name in self.allocated.items():
role = " [GATEWAY]" if name == f"gateway (docker0)" else ""
print(f" {ip:<18} → {name}{role}")
libres = self.network.num_addresses - 2 - len(self.allocated)
print(f"Adresses libres : {libres}")
def __repr__(self):
return f"DockerIPAMSimulator(subnet={self.network}, allocated={len(self.allocated)})"
# Simulation du réseau bridge par défaut de Docker
print("=" * 55)
print("Simulation IPAM — Réseau bridge par défaut (docker0)")
print("=" * 55)
ipam = DockerIPAMSimulator("172.17.0.0/16", gateway="172.17.0.1")
conteneurs = ["nginx-web", "postgres-db", "redis-cache", "app-backend", "worker-1"]
ips_allouees = {}
for nom in conteneurs:
ip = ipam.allocate(nom)
ips_allouees[nom] = ip
print(f" [+] Conteneur '{nom}' → {ip}")
ipam.status()
print("\n--- Suppression de quelques conteneurs ---")
ipam.release("redis-cache")
ipam.release("nginx-web")
print("\n--- Nouveau conteneur (réutilisation d'IP ?) ---")
# Docker alloue séquentiellement, la nouvelle IP est après les précédentes
new_ip = ipam.allocate("nouveau-service")
print(f" [+] 'nouveau-service' → {new_ip}")
print(" Note : Docker n'a PAS réutilisé les IPs libérées (allocation séquentielle)")
ipam.status()
=======================================================
Simulation IPAM — Réseau bridge par défaut (docker0)
=======================================================
[+] Conteneur 'nginx-web' → 172.17.0.2
[+] Conteneur 'postgres-db' → 172.17.0.3
[+] Conteneur 'redis-cache' → 172.17.0.4
[+] Conteneur 'app-backend' → 172.17.0.5
[+] Conteneur 'worker-1' → 172.17.0.6
Réseau : 172.17.0.0/16
Capacité : 65534 hôtes disponibles
Allocations actuelles (6) :
172.17.0.1 → gateway (docker0) [GATEWAY]
172.17.0.2 → nginx-web
172.17.0.3 → postgres-db
172.17.0.4 → redis-cache
172.17.0.5 → app-backend
172.17.0.6 → worker-1
Adresses libres : 65528
--- Suppression de quelques conteneurs ---
IP 172.17.0.4 libérée (conteneur 'redis-cache' supprimé)
IP 172.17.0.2 libérée (conteneur 'nginx-web' supprimé)
--- Nouveau conteneur (réutilisation d'IP ?) ---
[+] 'nouveau-service' → 172.17.0.7
Note : Docker n'a PAS réutilisé les IPs libérées (allocation séquentielle)
Réseau : 172.17.0.0/16
Capacité : 65534 hôtes disponibles
Allocations actuelles (5) :
172.17.0.1 → gateway (docker0) [GATEWAY]
172.17.0.3 → postgres-db
172.17.0.5 → app-backend
172.17.0.6 → worker-1
172.17.0.7 → nouveau-service
Adresses libres : 65529
Récapitulatif — Choisir son driver réseau#
Points clés à retenir#
Docker crée une interface
docker0(bridge) sur l’hôte et connecte chaque conteneur via des paires vethLe NAT (iptables MASQUERADE) permet aux conteneurs d’accéder à Internet
Sur un réseau bridge personnalisé, Docker fournit un DNS interne permettant la résolution par nom de conteneur
Le réseau bridge par défaut (
docker0) ne supporte pas le DNS par nom — toujours créer un réseau nommédocker network create/ls/inspect/connect/disconnectsont les commandes essentiellesLa publication de ports (
-p HOST:CONTAINER) crée des règles iptables DNATLe driver overlay utilise VXLAN pour étendre un réseau sur plusieurs machines (Docker Swarm)
En développement, Docker Compose crée automatiquement un réseau bridge nommé pour chaque projet