Guide complet des réseaux Docker

Réseaux none et host : quand les utiliser

Ce chapitre aborde les différents types de réseaux disponibles dans Docker. Nous allons examiner les réseaux natifs proposés par Docker, la création de réseaux personnalisés, ainsi que les mécanismes de communication entre conteneurs et avec l'extérieur.

Docker distingue deux catégories de réseaux : ceux limités à un seul hôte et ceux déployés sur plusieurs hôtes. Ce chapitre se concentre sur la première catégorie. Les configurations multi-hôtes seront traitées dans un chapitre avancé dédié.

L'installation de Docker crée automatiquement trois réseaux sur l'hôte. Pour les visualiser, utilisez la commande docker network ls.

Le réseau none

Comme son nom l'indique, le réseau none représente un réseau vide. Les conteneurs attachés à ce réseau disposent uniquement de l'interface loopback (lo) et d'aucune autre carte réseau. Pour utiliser ce réseau, spécifiez l'option --network=none lors de la création du conteneur.

À quoi peut bien servir un réseau aussi fermé ? Il existe effectivement des cas d'utilisation pratiques. L'isolement total convient parfaitement aux applications nécessitant une sécurité maximale et n'ayant pas besoin de connectivité réseau.

Par exemple, un conteneur dont la seule fonction est de générer des mots de passe aléatoires peut être placé dans le réseau none pour éviter tout risque de fuite.

La majorité des conteneurs nécessitent cependant une connexion réseau. Examinons maintenant le réseau host.

Le réseau host

Les conteneurs connectés au réseau host partagent entièrement la pile réseau de l'hôte Docker. La configuration réseau du conteneur est identique à celle de l'hôte. Spécifiez --network=host pour utiliser ce réseau.

Depuis le conteneur, vous pouvez voir toutes les interfaces réseau de l'hôte, et le hostname correspond également à celui de l'hôte. Quels sont les cas d'utilisation appropriés pour ce réseau ?

L'avantage principal réside dans les performances réseau. Pour les conteneurs exigeants en termes de bande passante et de latence, le réseau host constitue un choix pertinent. Cependant, cette solution présente des inconvénients : vous devez gérer les conflits de ports, car les ports déjà utilisés par l'hôte ne peuvent pas être réutilisés.

Le réseau host s'avère également utile pour certains outils réseau fonctionnant en conteneur et nécessitant une configuration directe du réseau hôte, comme les solutions de réseau inter-hôtes qui manipulent les iptables. Ces aspects seront approfondis dans les chapitres avancés.

Maîtriser le réseau bridge

Examinons la configuration du réseau bridge avec docker network inspect bridge.

Pour connaître la version Linux du conteneur, exécutez cat /etc/issue.

Voici un exemple de configuration des mirrors pour Debian :

sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
apt-get update && apt-get install -y iproute2

L'installation de Docker crée automatiquement un bridge Linux nommé docker0. Sans spécifier l'option --network, les conteneurs sont automatiquement attachés à ce bridge.

Lorsque docker0 ne possède aucun équipement réseau, la création d'un conteneur génère une nouvelle interface.

Une nouvelle interface virtuelle est attachée au bridge docker0. Cette interface représente la carte réseau virtuelle du nouveau conteneur.

Examinons la configuration réseau du conteneur. celui-ci possède une interface eth0@ifXX. Pourquoi n'observe-t-on pas le même nom d'interface ?

En réalité, eth0@ifXX et vethYYYYYY constituent une paire veth. Les paires veth sont des dispositifs réseau spéciaux apparaissant par paires, comme deux cartes réseau reliées par un cable virtuel. Une extrémité (eth0@ifXX) se trouve dans le conteneur, tandis que l'autre (vethYYYYYY) est connectée au bridge docker0, ce qui permet d'attacher effectively eth0@ifXX au bridge.

L'interface eth0@ifXX possède l'adresse IP 172.17.0.2. Pourquoi ce sous-réseau ? Consultons la configuration du réseau bridge.

Le réseau bridge est configuré avec le subnet 172.17.0.0/16 et la passerelle 172.17.0.1. Cette passerelle correspond au bridge docker0 lui-même.

Lors de la création d'un conteneur, Docker attribue automatiquement une adresse IP du pool 172.17.0.0/16. Le masque 16 bits garantit un nombre suffisant d'adresses IP disponibles.

Personnaliser le réseau des conteneurs

# Consulter les informations réseau
docker network inspect bridge

# Liste de toutes les interfaces réseau
ip a

# Visualiser les bridges
brctl show

# Détails de l'interface réseau
ifconfig enp0s8

# Créer un réseau personnalisé
docker network create --driver bridge mon_reseau

# Lister les réseaux disponibles
docker network ls

# Installer l'outil de ping
apt-get install iputils-ping

Au-delà des réseaux none, host et bridge automatiquement créés, Docker permet de définir des réseaux personnalisés selon les besoins métier.

Docker propose trois pilotes pour les réseaux personnalisés : bridge, overlay et macvlan. Les pilotes overlay et macvlan servent à créer des réseaux multi-hôtes, traités dans un chapitre dédié.

Nous pouvons créer un réseau personnalisé avec le pilote bridge, similaire au bridge par défaut.

Après création, examinons les modifications de la structure réseau de l'hôte.

Un nouveau bridge apparaît, nommé d'après l'identifiant du réseau créé. Exécutons docker network inspect pour voir la configuration.

Docker attribue automatiquement le subnet 172.18.0.0/16.

Est-il possible de spécifier un subnet personnalisé ? Absolument.

Spécifiez les options --subnet et --gateway lors de la création.

Nous avons créé le réseau bridge mon_reseau2 avec le subnet 172.22.16.0/24 et la passerelle 172.22.16.1. Cette passerelle se trouve sur le bridge correspondant.

Pour utiliser ce nouveau réseau, spécifiez l'option --network au démarrage du conteneur.

Le conteneur reçoit l'adresse IP 172.22.16.2.

Jusqu'à présent, les adresses IP étaient attribuées dynamiquement. Peut-on définir une adresse IP statique ? Oui, avec l'option --ip.

Note importante : seul un réseau créé avec l'option --subnet permet de spécifier une IP statique.

Si vous尝试ez d'assigner une IP statique à un réseau créé sans --subnet>, une erreur se produit.

Examinons la topologie réseau actuelle de l'hôte Docker.

Comprendre la connectivité entre conteneurs

Après les manipulations précédentes, la topologie réseau de l'hôte Docker se présente comme suit. Étudions la communication entre ces différents conteneurs.

Deux conteneurs busybox étant sur le même réseau mon_reseau2, ils devraient pouvoir communiquer. Vérifions.

Les conteneurs d'un même réseau peuvent communiquer avec la passerelle.

Le réseau mon_reseau2 peut-il communiquer avec le bridge par défaut ?

D'après la topologie, ces deux réseaux étant sur des bridges différents, la communication devrait échouer. Testons en faisant pinguer le conteneur busybox vers le conteneur httpd.

Le ping échoue comme prévu.

« Attendez ! Avec des routes appropriées, ces réseaux différents pourraient communiquer »

C'est une excellente remarque. Si l'hôte dispose de routes vers chaque réseau et que le forwarding IP est activé, l'hôte fonctionne comme un routeur, permettant aux réseaux connectés à différents bridges de communiquer.

Vérifions si l'hôte Docker remplit ces conditions.

Consultons la table de routage avec ip r :

# ip r
......
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.22.16.0/24 dev br-XXXXXXXX proto kernel scope link src 172.22.16.1
......

Les routes vers 172.17.0.0/16 et 172.22.16.0/24 sont configurées. Vérifions le forwarding IP :

# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

Le forwarding IP est activé. Toutes les conditions sont réunies, mais pourquoi la communication échoue-t-elle ?

Examinons les règles iptables :

# iptables-save
......
-A DOCKER-ISOLATION -i br-XXXXXXXX -o docker0 -j DROP
-A DOCKER-ISOLATION -i docker0 -o br-XXXXXXXX -j DROP
......

Voilà le problème : iptables DROP tout le trafic bidirectionnel entre docker0 et le nouveau bridge.

Le nom DOCKER-ISOLATION indique que Docker isole intentionnellement les réseaux entre eux.

Comment permettre la communication entre busybox et httpd ?

La solution consiste à ajouter une interface réseau du réseau mon_reseau2 au conteneur httpd, via la commande docker network connect.

Vérifions la configuration réseau du conteneur httpd.

Une nouvelle interface eth1 apparaît, avec l'IP 172.22.16.3 du réseau mon_reseau2. Maintenant, busybox devrait pouvoir atteindre httpd. Testons.

Le conteneur busybox peut pinguer httpd et accéder à son service web. La structure réseau actuelle devient :

Trois méthodes de communication inter-conteneurs

Les conteneurs peuvent communiquer via IP, le DNS Docker, ou les conteneurs joints.

Communication par IP

Comme shown dans l'exemple précédent : deux conteneurs peuvent communiquer par IP à condition de posséder une interface sur le même réseau.

Pour cela, spécifiez --network lors de la création du conteneur, ou utilisez docker network connect pour ajouter un conteneur existant à un réseau. Reportez-vous à l'exemple précédent avec httpd et busybox.

Docker DNS Server

Communicating par IP fonctionne mais manque de flexibilité. Avant le déploiement, l'IP peut être inconnue, et préciser l'IP après coup pose des problèmes. Docker résout ce problème avec son serveur DNS intégré.

Depuis Docker 1.10, le daemon implémente un serveur DNS permettant aux conteneurs de communiquer par leur nom. Il suffit d'utiliser l'option --name lors du démarrage.

Démarrons deux conteneurs bbox1 et bbox2 :

docker run -it --network=mon_reseau2 --name=bbox1 busybox
docker run -it --network=mon_reseau2 --name=bbox2 busybox

bbox2 peut maintenant pinguer bbox1 directement par son nom.

Cette fonctionnalité DNS présente une limitation : elle fonctionne uniquement avec les réseaux user-defined. Le bridge par défaut ne support pas le DNS.

Vérifions en créant bbox3 et bbox4 sur le bridge par défaut.

docker run -it --name=bbox3 busybox
docker run -it --name=bbox4 busybox

bbox4 ne peut pas pinguer bbox3.

Conteneurs joints

Les conteneurs joints représentent une autre méthode de communication.

Cette approche permet à plusieurs conteneurs de partager une pile réseau, les interfaces et configurations. Ils peuvent communiquer via 127.0.0.1.

Créons d'abord un conteneur httpd nommé web1.

docker run -d -it --name=web1 httpd

Puis créons un conteneur busybox en le joignant à web1 via --network=container:web1.

Examinons la configuration réseau du conteneur busybox.

Vérifions également les informations réseau de web1.

Les adresses MAC et IP de busybox et web1 sont identiques : ils partagent la même pile réseau. busybox peut accéder au service HTTP de web1 via 127.0.0.1.

Les conteneurs joints conviennent parfaitement aux scénarios suivants :

  1. Des programmes dans différents conteneurs devant communiquer efficacement via loopback, comme un serveur web et un serveur d'application.
  2. La surveillance du trafic réseau d'autres conteneurs, comme un programme de monitoring s'exécutant dans un conteneur séparé.

Communication des conteneurs avec l'extérieur

Nous avons résolu la communication inter-conteneurs. Abordons maintenant comment les conteneurs communiquent avec le monde extérieur, dans les deux sens :

  1. Le conteneur vers l'extérieur
  2. L'extérieur vers le conteneur

Le conteneur accède à l'extérieur

Dans notre environnement de test, l'hôte Docker peut accéder à internet.

Vérifions si les conteneurs peuvent également accéder à l'extérieur.

Les conteneurs ont accès à internet par défaut.

L'« extérieur » désigne tout réseau hors du conteneur, pas uniquement internet.

Comprendre le mécanisme sous-jacent est essentiel.

Dans l'exemple ci-dessus, busybox se trouve sur le réseau bridge privé docker0 (172.17.0.0/16). Quand busybox ping vers l'extérieur, comment le paquet atteint-il bing.com ?

La clé réside dans le NAT. Examinons les règles iptables de l'hôte Docker.

Dans la table NAT, cette règle existe :

-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

Son signification : si le bridge docker0 reçoit des paquets sortants du subnet 172.17.0.0/16, les traiter par MASQUERADE. MASQUERADE remplace l'adresse source par l'adresse de l'hôte — une traduction d'adresse réseau (NAT).

Observons comment l'adresse est traduite avec tcpdump. Consultons la table de routage de l'hôte.

La route par défaut passe par enp0s3. Surveillons simultanément enp0s3 et docker0 pour les paquets ICMP.

Quand busybox ping bing.com, tcpdump affiche :

docker0 reçoit le ping de busybox avec l'adresse source 172.17.0.2,没问题, puis traite par MASQUERADE. Sur enp0s3, observe-t-on un changement ?

L'adresse source du ping devient l'IP de enp0s3 : 10.0.2.15

C'est le résultat du traitement NAT iptables, permettant au paquet d'atteindre l'extérieur. Voici le processus en изображение :

  1. busybox envoie le ping : 172.17.0.2 > www.bing.com
  2. docker0 reçoit le paquet, détecte qu'il est destiné à l'extérieur, applique NAT
  3. NAT remplace l'adresse source par celle de enp0s3 : 10.0.2.15 > www.bing.com
  4. Le ping part de enp0s3 vers www.bing.com

Docker permet aux conteneurs d'accéder à l'extérieur via NAT.

L'extérieur accède au conteneur

Nous avons vu comment les conteneurs accedent à l'extérieur. Abordons la direction inverse : comment l'extérieur atteint-il les conteneurs ?

La réponse : le mapping de ports.

Docker peut mapper un port exposé par le conteneur vers un port de l'hôte. L'extérieur accède au conteneur via ce port. Utilisez l'option -p au démarrage.

Après démarrage, docker ps ou docker port révèlent le port mappé. Dans l'exemple, le port 80 du conteneur httpd est mappé vers le port 32773 de l'hôte. Le service web est accessible via <ip_hôte>:32773.

Vous pouvez spécifier un port fixe au lieu d'un port dynamique. Par exemple, mapper le port 80 du conteneur vers le port 8080 de l'hôte.

Pour chaque port mappé, l'hôte lance un processus docker-proxy pour gérer le trafic vers le conteneur.

Analysons le processus pour 0.0.0.0:32773->80/tcp :

  1. docker-proxy écoute sur le port 32773 de l'hôte
  2. Quand curl accède à 10.0.2.15:32773, docker-proxy redirige vers 172.17.0.2:80
  3. Le conteneur httpd répond et retourne le résultat

Ressource : Référence Docker Networking

Étiquettes: Docker réseau conteneur Bridge NAT

Publié le 30 juin à 23h43