Docker a une architecture réseau appelée “Container Networking Model” (CNM). Ce modele utilise la librairie libnetwork qui implémente le modele CNM.
CNM utilise les conceptions suivant :
Sandbox : Une unité isolée contenant tous les composants réseau associés à un seul conteneur. Habituellement, un namespace Linux (interfaces, port, routes, DNS). Une sandbox par container.
Endpoint : Connecte une sandbox à un network. Chaque sandbox/conteneur peut avoir un nombre quelconque de points d'extrémité (endpoint), mais a exactement un point d'extrémité pour chaque réseau auquel il est connecté.
Network : Une collection de points d'extrémité (endpoint) connectés les uns aux autres
Network driver : Gère la mise en œuvre effective des concepts du CNM.
IPAM Driver : IPAM signifie gestion des adresses IP. Il attribue automatiquement des sous-réseaux et des adresses IP pour les réseaux et les terminaux.
Par défaut on a different “drivers” réseau :
- bridge : réseau par défaut de docker, il permet d'interconnecter les containers connectés au même bridge. Bridge fourni une isolation entre l'hôte et les containers.
- host : permet l'accès au réseau de l'hôte sans aucune isolation.
- overlay (EE, swarm) : réseau par défaut pour docker swarm. Permet l'interconnexion de container entre plusieurs noeud docker.
- macvlan : permet l'association d'une adresse MAC ainsi que des VLANs à un container.
- none : comme son nom l'indique, aucun réseau attaché au container.
Si on regarde en pratique avec le fonctionnement de CNM :
Docker fait en sorte que le réseau puisse communiquer avec les autres containers mais aussi avec l’hote docker.
On peut voir la liste des réseaux en faisant :
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
23f4f9629e77 bridge bridge local
f80ad5726667 host host local
18730864b45c none null local
On peut forcer l’utilisation d’un réseau lors du lancement d’un container :
docker container run -d --name myhost --network host ubuntu:latest
Le bridge network va permettre aux containers connectés au même bridge de pouvoir communiquer entre eux tout en isolant des autres containers non connectés au même réseau.
Pour identifier le réseau qu’utilise un container on peut faire :
docker inspect 8e55e763925a -f "{{json .NetworkSettings}}" | jq
Les containers qui sont sur le même réseau pourront logiquement communiquer entre eux.
Lorsqu’on ne spécifie pas le network, le réseau sera le bridge de base !
On peut aussi créer un User-Defined Bridge Network qui est supérieur au bridge par défaut. (voir point suivant)
Usert-Defined Bridge = Bridge créé par l’utilisateur et non le bridge par défaut !
On peut lister tous les containers présents sur un network via la commande :
docker network inspect bridge
Il y a plusieurs différences entre le bridge et le User-Defined Bridge :
En pratique :
# Création d'un UDB
$ docker network create --driver bridge mybridge
# Si on ne précise pas le --driver, par défaut bridge sera choisi (UDB)
# Chaque réseau créé rajoute une carte virtuelle dans le “ifconfig”
$ docker container run -d --name mybridge01 --network mybridge ubuntu:latest sleep 3600
$ docker container run -d --name mybridge02 --network mybridge ubuntu:latest sleep 3600
$ docker network inspect mybridge
# Plus précis en utilisant --format
$ docker network inspect mybridge --format "{{json .Containers }}" | jq
{
"60d7d9148053594eb27da9cb1cdeca44e5bd79dd7013f4831d1f237d67c9577d": {
"Name": "mybridge01",
"EndpointID": "f06e914ae2983192b86b970caaa226ba81e6768f838a7c37e2540127867c9c70",
"MacAddress": "02:42:ac:16:00:02",
"IPv4Address": "172.22.0.2/16",
"IPv6Address": ""
},
"a16e7d000c4f2c7c9b543d48713ca3687895e78270be6b061925bce692e51807": {
"Name": "mybridge02",
"EndpointID": "86a0c8f5a19bbcd565c83f0b8784593e67704c9348c1936f0e5c45e859b6e641",
"MacAddress": "02:42:ac:16:00:03",
"IPv4Address": "172.22.0.3/16",
"IPv6Address": ""
}
}
$ docker container exec -it mybridge01 bash
$ apt update && apt install -y net-tools iputils-ping
root@60d7d9148053:/> ping mybridge02
PING mybridge02 (172.22.0.3) 56(84) bytes of data.
64 bytes from mybridge02.mybridge (172.22.0.3): icmp_seq=1 ttl=64 time=1.19 ms
64 bytes from mybridge02.mybridge (172.22.0.3): icmp_seq=2 ttl=64 time=0.288 ms
Si le nom DNS ne convient pas, on peut changer l’alias réseau d’un container :
docker run -d --name my-net-nginx --network-alias my-nginx-alias --network my-net nginx
On pourra faire un curl sur my-net-nginx ET my-nginx-alias
Si le container run déjà, on peut attacher un alias :
docker network connect --alias ALIAS NETWORK CONTAINER
# Exemple
docker network connect --alias another-alias my-net my-nginx
On peut évidemment déconnecter un réseau d’un container :
docker network disconnect NETWORK CONTAINER
docker network disconnect my-net my-nginx
Et supprimer un réseau :
docker network rm NETWORK
On peut supprimer tous les réseaux non utilisés :
docker network prune
Lors de la création d’un bridge on peut mettre des options : docker network create | Docker Documentation
On peut aussi connecter/ajouter un réseau à un container actif à un réseau :
docker network connect NETWORK CONTAINER
docker network connect mybridge mybridge01
Ce driver enlève l’isolation réseau entre l’hote docker et le container pour utiliser le réseau de l'hôte directement.
Donc si on lance une application avec le port 80 ouvert, celle-ci sera accessible sur l’ip de l'hôte docker directement !
En pratique :
# Création d'un container
$ docker container run -d --name myhost --network host ubuntu
$ docker container exec -it myhost /bin/bash
apt update && apt install net-tools
netstat -ntlp # les mêmes ports que la machine hôte sont ouverts
ifconfig # Les interfaces de l’hôte sont affichées
apt install nginx -y
/etc/init.d/nginx start
netstat -ntlp # le port 80 sera ouvert en plus des autres
# On peut atteindre nginx depuis l’IP de la machine hôte
Si on ne veut pas configurer de réseau sur un container on peut donc utiliser “none” network.
Ce driver ne configure donc pas d’IP pour le container et n’a donc pas d’accès à l’extérieur du container ni même aux autres containers.
En pratique :
$ docker container run -d --name my-none --network none alpine:latest
$ docker container exect -it mynone ash
ifconfig # Seulement la loopback va être affiché
ping google.com # Bad request
Pour publier le port 80 sur le port 8080 de l'hôte :
docker container run -d --name webserver -p 8080:80 nginx
Nginx sera accessible depuis le port 8080 sur toutes les interfaces de notre hote.
Il y a cependant une deuxième façon d'exposer un port. Ici on va exposer TOUS les ports du container :
docker container run -d -P --name webserver nginx
Dans ce cas, tous les ports seront publiés sur un port host aléatoire.
# Création d'un container
$ docker container run -d --name container01 busybos sh
# On link container02 au container01
$ docker container run -d --link container01:container --name container02 busybox sh
# Exécution du shell
$ docker container exec -it container02 sh
ping container # Success, ça ping l’alias du container01
Cependant cette commande peut disparaître à tout moment (voir doc docker). Il est donc préférable d’utiliser un User-Defined Bridge afin d’avoir le DNS dynamique directement. Cependant elle reste utile pour le DCA.
On peut changer le DNS par défaut. Soit par le fichier /etc/docker/daemon.json
(fichier de configuration Docker Engine) :
{
“dns”: [“8.8.8.8”]
}
Soit via docker run :
docker run -d --dns 8.8.8.8 --name my-dns nginx
Par défaut un réseau créera une plage d'adresse IP. On peut forcer un sous-réseau via les commandes suivantes :
$ docker network create --subnet <SUBNET> --gateway <GATEWAY> <NAME>
# Soit :
$ docker network create --subnet 10.1.0.0/24 --gateway 10.1.0.1 my-network
On peut aussi spécifier un range d'IP :
$ docker network create --subnet <SUBNET> --gateway <GATEWAY> --ip-range <IP-RANGE> <NAME>
On peut assigner une IP à un container via la commande suivante :
$ docker container run -d --name network-test02 --ip 10.1.0.100 --network my-network nginx
On peut créer un réseau interne afin que rien ne soit exposé sur l'exterieur :
$ docker network create -d bridge --internal localhost
# On va tester la connexion
$ docker container run -d --name private-nginx -p 8081:80 --network localhost nginx
$ curl localhost:8081
curl: (7) Failed connect to localhost:8081; Connection refused
On pourra accéder à nginx via l'IP du container uniquement (ou via un container sur le même réseau). Pour se faire, docker container inspect.