◀ Retour au blog
Docker

Guide réseau Docker

Publié le 08 Jun 2024· 7 min de lecture
#Docker#Réseau#Infrastructure

Les modes réseau Docker

Docker propose plusieurs drivers réseau, chacun adapté à des cas d'usage spécifiques.

Comprendre ces drivers évite la plupart des problèmes de connectivité que l'on rencontre avec les conteneurs : une application qui ne trouve pas sa base de données, un port inaccessible depuis l'extérieur, ou au contraire une base exposée sur Internet sans qu'on l'ait voulu. Les drivers les plus utilisés sont bridge, host et overlay ; il existe aussi none, qui coupe tout accès réseau, et macvlan, plus rare. Les commandes de base pour explorer l'existant :

docker network ls
docker network inspect bridge
docker network inspect mon-reseau --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'

Bridge (par défaut)

Le mode bridge crée un réseau isolé pour les conteneurs sur un même hôte :

docker network create --driver bridge mon-reseau
docker run --network mon-reseau --name app myapp
docker run --network mon-reseau --name db mysql

Les conteneurs sur le même réseau bridge peuvent communiquer via leur nom de conteneur comme hostname.

Une nuance importante : cette résolution par nom ne fonctionne que sur les réseaux bridge créés par l'utilisateur. Le réseau bridge par défaut, auquel sont rattachés les conteneurs lancés sans option --network, ne fournit pas de DNS entre conteneurs. C'est une raison suffisante pour toujours créer ses propres réseaux, ce que Docker Compose fait automatiquement : chaque projet reçoit un réseau nommé <projet>_default.

Sur un réseau bridge, les conteneurs ont des adresses privées et ne sont pas joignables depuis l'extérieur. Pour rendre un service accessible, on publie un port : Docker ajoute alors une règle de NAT de l'hôte vers le conteneur. Par défaut, le port est ouvert sur toutes les interfaces de l'hôte ; précisez une adresse pour le limiter :

# Accessible depuis l'extérieur sur le port 8080 de l'hôte
docker run -d -p 8080:80 --name web nginx:alpine

# Accessible uniquement depuis l'hôte lui-même
docker run -d -p 127.0.0.1:8081:80 --name admin nginx:alpine

# Rattacher un conteneur existant à un second réseau
docker network connect mon-reseau web

Attention : les règles iptables créées par Docker pour les ports publiés s'appliquent avant celles d'un pare-feu comme UFW. Un port publié sur 0.0.0.0 est donc joignable même si UFW semble le bloquer.

Host

Le mode host supprime l'isolation réseau. Le conteneur utilise directement le réseau de l'hôte :

docker run --network host nginx

Utile pour les performances maximales mais sans isolation.

Il n'y a ni NAT ni port à publier : Nginx écoute directement sur le port 80 de l'hôte, et les options -p sont ignorées. En contrepartie, deux conteneurs ne peuvent pas utiliser le même port, et le conteneur voit toutes les interfaces réseau de la machine. Ce mode se justifie pour des outils qui doivent observer le réseau de l'hôte (agents de monitoring, certains outils VPN) ou pour des charges où la moindre latence compte. Il est pleinement pris en charge sur Linux ; avec Docker Desktop sur macOS et Windows, les conteneurs tournent dans une VM et le comportement n'est pas le même.

None

Avec --network none, le conteneur n'a qu'une interface loopback. C'est utile pour un traitement qui n'a besoin d'aucun accès réseau, par exemple la conversion de fichiers non fiables : même compromis, le processus ne peut rien exfiltrer.

docker run --rm --network none -v "$PWD/input:/data:ro" alpine ls -l /data

Overlay

Pour les clusters Docker Swarm, le réseau overlay permet la communication entre conteneurs sur différents nœuds :

docker network create --driver overlay --attachable mon-overlay

Un réseau overlay nécessite que l'hôte soit un nœud Swarm (docker swarm init). Le trafic entre nœuds est encapsulé en VXLAN : les ports 2377/tcp (gestion du cluster), 7946/tcp et udp (découverte entre nœuds) et 4789/udp (données) doivent être ouverts entre les machines. L'option --attachable permet à des conteneurs lancés avec docker run, et pas seulement aux services Swarm, de rejoindre le réseau. Le trafic de gestion est chiffré par défaut ; pour chiffrer aussi le trafic applicatif entre nœuds, ajoutez --opt encrypted.

Macvlan

Le driver macvlan donne au conteneur sa propre adresse MAC et une adresse IP sur le réseau physique, comme s'il s'agissait d'une machine à part entière. Il sert surtout à intégrer des applications historiques qui attendent une IP dédiée. Particularité à connaître : par défaut, l'hôte lui-même ne peut pas communiquer avec ses conteneurs macvlan.

DNS interne

Docker intègre un serveur DNS permettant la résolution par nom de service :

services:
  app:
    networks:
      - frontend
      - backend
  nginx:
    networks:
      - frontend
  db:
    networks:
      - backend

networks:
  frontend:
  backend:

Dans chaque conteneur, ce DNS est joignable à l'adresse 127.0.0.11. Il résout les noms de service, les noms de conteneur et les éventuels alias déclarés, mais uniquement pour les conteneurs qui partagent un réseau. Dans l'exemple ci-dessus, app peut joindre nginx et db, tandis que nginx ne peut pas résoudre db : un Nginx compromis n'a aucun chemin direct vers la base. Si un service est mis à l'échelle sur plusieurs conteneurs, son nom renvoie plusieurs adresses.

Pour aller plus loin, déclarez le réseau backend comme interne (sans accès vers l'extérieur) et partagez un réseau créé en dehors du projet avec un reverse proxy :

networks:
  frontend:
  backend:
    internal: true
  proxy:
    external: true

Pour diagnostiquer un problème de résolution ou de connectivité, lancez un conteneur d'outils sur le même réseau plutôt que d'installer des paquets dans vos images :

docker run --rm -it --network mon-reseau nicolaka/netshoot
# Puis, dans le conteneur :
dig db
nc -zv db 3306

Conflits de sous-réseaux

Docker attribue à chaque réseau un sous-réseau privé, souvent en 172.17.0.0/16 et au-delà. Si votre réseau d'entreprise ou votre VPN utilise déjà ces plages, certaines destinations deviennent injoignables depuis l'hôte. La solution consiste à définir des plages dédiées dans /etc/docker/daemon.json, puis à redémarrer Docker et recréer les réseaux :

{
  "default-address-pools": [
    { "base": "10.200.0.0/16", "size": 24 }
  ]
}

Récapitulatif des drivers

DriverPortéeCas d'usage
bridgeUn hôteCas général, applications Compose
hostUn hôtePerformance maximale, outils réseau
noneUn conteneurTraitements sans accès réseau
overlayPlusieurs hôtesClusters Docker Swarm
macvlanRéseau physiqueApplications qui exigent une IP dédiée

Bonnes pratiques réseau

  • Isolez les services par réseau (frontend, backend, monitoring)
  • Utilisez des réseaux internes pour les services non exposés
  • Documentez vos topologies réseau
  • Limitez les ports exposés au strict minimum
  • Utilisez les noms de service plutôt que les adresses IP, qui changent à chaque recréation
  • Liez à 127.0.0.1 les ports qui ne doivent servir qu'en local

Pour la sécurité des conteneurs au-delà du réseau, voir l'article Sécuriser vos conteneurs Docker.