DCNA
Docker Certified Network Associate
DCNADocker Network Architecture

Docker Network Architecture

Quand tu lances docker run nginx, Docker ne crée pas juste un processus isolé — il crée un environnement réseau entièrement séparé pour ce container. Comprendre comment cela fonctionne explique pourquoi les containers peuvent se parler, pourquoi les ports doivent être "publiés", et pourquoi un container peut accéder à internet sans configuration particulière.

L'analogie : un immeuble d'appartements

Imagine que chaque container est un appartement dans un immeuble :

  • Chaque appartement a sa propre boîte aux lettres (adresse IP, ex: 172.17.0.2)
  • Il y a un couloir commun par étage (le bridge réseau)
  • Le concierge (iptables) gère le trafic entrant/sortant
  • La rue (réseau physique de la machine hôte) est partagée entre tous

Les Network Namespaces : l'isolation de base

Linux fournit les network namespaces : chaque namespace est une pile réseau complète et indépendante (interfaces, tables de routage, règles iptables, ports). Docker crée un namespace par container.

# Voir les namespaces réseau actifs sur l'hôte
ip netns list
# (Docker ne les affiche pas toujours ici, mais ils existent)

# Lancer un container et trouver son namespace
docker run -d --name test nginx
docker inspect test --format '{{.NetworkSettings.SandboxKey}}'
# → /var/run/docker/netns/abc123...

Ce que ça signifie concrètement : si le container écoute sur le port 80, ce port 80 n'est PAS le même que le port 80 de ta machine. Ils sont dans des namespaces différents. C'est pourquoi tu dois faire -p 8080:80 pour exposer le port.

Les veth pairs : le câble virtuel

Pour connecter le namespace du container à l'hôte, Docker crée des paires veth (virtual ethernet). Une paire veth, c'est deux interfaces réseau virtuelles reliées comme les deux bouts d'un câble :

Container namespace          Hôte (namespace global)
┌────────────────────┐      ┌────────────────────┐
│  eth0 (container)  │======│  veth3a2b (hôte)   │
│  172.17.0.2        │      │  branché sur docker0│
└────────────────────┘      └────────────────────┘
# Voir les paires veth créées par Docker
ip link show | grep veth
# → veth3a2b@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> ...

# Voir combien de containers sont connectés
brctl show docker0
# ou
bridge link show

Un paquet qui sort de eth0 dans le container arrive automatiquement sur veth3a2b côté hôte — comme si les deux interfaces étaient reliées par un câble physique.

Le bridge docker0 : le switch virtuel

docker0 est un bridge Linux (switch virtuel logiciel) créé par Docker au démarrage. Tous les containers du réseau bridge par défaut y sont connectés via leurs veth pairs.

# Voir le bridge et son adresse IP
ip addr show docker0
# → docker0: <BROADCAST,MULTICAST,UP,LOWER_UP>
# →   inet 172.17.0.1/16 brd 172.17.255.255

# Voir les interfaces connectées au bridge
brctl show docker0
# bridge name  bridge id         STP enabled  interfaces
# docker0      8000.024238c32591  no           veth3a2b
#                                              vethf1c9

172.17.0.1 est l'adresse IP de docker0 sur l'hôte — c'est la gateway par défaut de tous les containers.

Le Container Network Model (CNM)

Docker formalise son architecture réseau avec le CNM :

ComposantRôleExemple concret
SandboxNamespace réseau isolé du containerLe namespace avec eth0, routes, DNS
EndpointInterface virtuelle connectant le sandbox à un réseauLa paire veth (côté container = eth0)
NetworkGroupe d'endpoints pouvant communiquerLe bridge docker0 ou my-app-net

Un container peut avoir plusieurs endpoints (donc plusieurs IPs) s'il est connecté à plusieurs réseaux. Exemple : un container API connecté à la fois au réseau frontend et au réseau backend.

libnetwork est la bibliothèque Go qui implémente le CNM dans le daemon Docker. Les drivers réseau (bridge, overlay, macvlan…) sont des plugins de libnetwork.

Le parcours complet d'un paquet

Scénario 1 : Container → Internet

Container (172.17.0.2)
    │ eth0
    │ (veth pair)
    │ veth côté hôte
    ▼
docker0 bridge (172.17.0.1)
    │ ip_forward activé
    ▼
eth0 de l'hôte (ex: 192.168.1.10)
    │ iptables MASQUERADE (NAT)
    │ → remplace src IP 172.17.0.2 par 192.168.1.10
    ▼
Internet
# Vérifier que ip_forward est activé (requis pour que Docker fonctionne)
cat /proc/sys/net/ipv4/ip_forward
# → 1

# Voir la règle NAT que Docker crée automatiquement
sudo iptables -t nat -L POSTROUTING -n -v
# → MASQUERADE  all  --  172.17.0.0/16  !172.17.0.0/16
# "tout ce qui vient de 172.17.x.x et ne va pas vers 172.17.x.x → masquerade"

Scénario 2 : Container → Container (même réseau)

Container A (172.17.0.2)
    │ eth0 → vethA → docker0
    │         docker0 switche directement
    ▼
Container B (172.17.0.3)
    │ docker0 → vethB → eth0

Le trafic ne quitte jamais l'hôte. docker0 joue le rôle d'un switch L2.

Scénario 3 : Accès depuis l'extérieur (port publishing)

Quand tu fais docker run -p 8080:80 nginx :

# Docker crée cette règle iptables DNAT :
sudo iptables -t nat -L DOCKER -n -v
# → DNAT  tcp  --  !docker0  docker0  tcp dpt:8080 to:172.17.0.2:80
# "toute requête TCP sur port 8080 → redirige vers 172.17.0.2:80"
Requête externe → port 8080 de l'hôte
    │ iptables DNAT (règle DOCKER chain)
    │ dst: hôte:8080 → container:80
    ▼
docker0 → veth → eth0 du container (port 80)

Les chaînes iptables de Docker

Docker crée plusieurs chaînes iptables automatiquement :

sudo iptables -L -n --line-numbers
# DOCKER-USER          ← tes règles custom (jamais écrasées par Docker)
# DOCKER               ← règles de port publishing
# DOCKER-ISOLATION-STAGE-1  ← isolation inter-réseaux
# DOCKER-ISOLATION-STAGE-2  ← suite de l'isolation

Important : si tu dois ajouter des règles iptables personnalisées, utilise toujours la chaîne DOCKER-USER. Docker ne la touche jamais.

Observer l'architecture en direct

# 1. Lancer deux containers
docker run -d --name c1 --network bridge alpine sleep 3600
docker run -d --name c2 --network bridge alpine sleep 3600

# 2. Voir leurs IPs
docker inspect c1 --format '{{.NetworkSettings.IPAddress}}'
docker inspect c2 --format '{{.NetworkSettings.IPAddress}}'

# 3. Voir les veth sur l'hôte
ip link show | grep veth

# 4. Voir le bridge
brctl show docker0

# 5. Tester la connectivité
docker exec c1 ping -c2 $(docker inspect c2 --format '{{.NetworkSettings.IPAddress}}')

Résumé

  • Chaque container a son propre network namespace (pile réseau isolée)
  • Les veth pairs connectent le namespace du container au bridge de l'hôte
  • docker0 est le bridge par défaut — tous les containers s'y connectent
  • Le trafic vers internet passe par NAT (MASQUERADE) sur l'interface de l'hôte
  • Le trafic entrant est redirigé via DNAT (règles iptables)
  • Le CNM (Sandbox, Endpoint, Network) est l'abstraction formelle de Docker
  • libnetwork implémente le CNM ; les drivers réseau sont des plugins

Lab: Explore Docker Network Architecture

Explore Docker Network Architecture
Lab: Explore Docker Network Architecture
Practice listing, inspecting and understanding the default Docker networks.
────────────────────────────────────────
$
List all Docker networks on this host
Inspect the default bridge network to see its configuration
Create a user-defined bridge network called 'demo-net'
Verify the new network was created by listing all networks
Remove the demo-net network