Skip to content

Latest commit

 

History

History
440 lines (318 loc) · 12.9 KB

File metadata and controls

440 lines (318 loc) · 12.9 KB

Vue d'ensemble des networks

Dans cette mise en pratique, nous allons étudier la mise en réseau des containers Docker.

Nous verrons notamment les networks qui sont crées par défaut lors de l’installation de la plateforme et nous passerons en revue différents drivers qui sont disponibles avec une installation standard.

Prequis : jq (sudo apt update && sudo apt install jq)

1. Les networks créés par défaut

Lors de l’installation de la plateforme Docker, plusieurs networks sont créés.

Listez ces networks avec la commande suivante:

$ docker network ls

Vous obtiendrez un résultat similaire à celui ci-dessous (aux identifiants près):

NETWORK ID         NAME      DRIVER    SCOPE

78600647cf6b      bridge     bridge     local

f68b62cc1152      host        host      local

5a4a4afa414b      none        null      local

Lorsqu’un container est créé, nous pourrons spécifier le network auquel il sera attaché:

  • un container attaché au network none n’aura pas de connectivité externe. Cela peut-être utile par exemple pour un container servant pour du debug.

  • un container attaché au network host bénéficiera de la stack network de la machine hôte

  • un container attaché au network bridge pourra communiquer avec les autres containers attaché à ce network.

Il faut cependant noter que ce type de network permet seulement une connectivité entre containers tournant sur la même machine.

Avec la commande suivante, listez les interfaces de la machine hôte:

$ ip a

Vous obtiendrez un résultat similaire à celui ci-dessous (les noms de certaines interfaces pourront être différentes):

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group
default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
group default qlen 1000
link/ether 02:4d:82:c4:d5:87 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
valid_lft 85937sec preferred_lft 85937sec
inet6 fe80::4d:82ff:fec4:d587/64 scope link
valid_lft forever preferred_lft forever
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
group default qlen 1000
link/ether 08:00:27:68:30:03 brd ff:ff:ff:ff:ff:ff
inet 192.168.33.10/24 brd 192.168.33.255 scope global enp0s8
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:fe68:3003/64 scope link
valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state
DOWN group default
link/ether 02:42:ac:d1:34:9b brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever

La chose importante à noter ici est l’existance d’une interface nommée docker0. C'est un bridge Linux qui a été créée lors de l’installation de la plateforme.

1.1. Lancement d’un container en utilisant le driver bridge

Par défaut, si l’option –-network n’est pas spécifiée au lancement d’un container, celui-ci est attaché au network nommé bridge.

Utilisez la commande suivante pour lancer un container nommé cnt1 basé sur l’image alpine.

$ docker container run -d --name cnt1 alpine:3.8 sleep 10000

Note: nous spécifions sleep 10000 dans la commande de façon à ce que le processus de PID 1 de ce container tourne quelques temps.

Avec la commande suivante, récupérer la configuration réseau de ce container (nous utilisons la notation Go template pour récupérer directement le champ qui nous intéresse (.NetworkdSetings.Networks).

$ docker container inspect -f "{{ json .NetworkSettings.Networks }}" cnt1 | jq .

La sortie de cette commande nous donne l’ID du network auquel ce container est attaché, via la clé NetworkID. Nous pouvons alors voir que cette valeur correspond à l’ID du network bridge. Nous obtenons également d’autres information comme l’IP du container et celle de la passerelle.

{
"bridge": {
"Aliases": null,
"DriverOpts": null,
"EndpointID":
"1968a1143dba15cad49dc84b06839b83e42d249a5ca6a83c06092840ad205364",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAMConfig": null,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"Links": null,
"MacAddress": "02:42:ac:11:00:02",
"NetworkID":
"78600647cf6b67dbe6fcc0dcc9b06a59a0b5c36033fa088c030490959901ee16"
}
}

Avec la commande suivante, inspectez le network bridge.

Note: cette commande utilise le formalisme Go template pour extraire la clé Containers afin d’obtenir la liste des containers attachés à ce network.

$ docker network inspect -f "{{ json .Containers }}" bridge | jq .

Vous devriez obtenir un résultat proche de celui ci-dessous, dans lequel le container cnt1 est listé. On voit donc que cnt1 est attaché au network.

{
"3f41f1295700be13435e82df1e98f1575f4380cfdcb8b315e4b275485e4c2470": {
"EndpointID":
"1968a1143dba15cad49dc84b06839b83e42d249a5ca6a83c06092840ad205364",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": "",
"MacAddress": "02:42:ac:11:00:02",
"Name": "cnt1"}
}

Supprimez le container cnt1.

$ docker rm -f cnt1

1.2. Lancement d’un container en utilisant le driver host

Avec la commande suivante, listez les interfaces réseau existantes sur la machine hôte.

$ ip link show

Vous devriez obtenir un résultat proche de celui ci-dessous.

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state
DOWN
link/ether 02:42:4a:a3:69:00 brd ff:ff:ff:ff:ff:ff
50947: eth0@if50948: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc
noqueue state UP
link/ether 02:42:0a:00:a7:03 brd ff:ff:ff:ff:ff:ff
50949: eth1@if50950: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc
noqueue state UP
link/ether 02:42:ac:12:00:9d brd ff:ff:ff:ff:ff:ff

Lancez alors un shell interactif dans un container basé sur l’image alpine. Spécifiez l’option --network=host de façon à ce que ce container utilise la stack network de la machine hôte.

$ docker container run -ti --name cnt1 --network=host alpine:3.8 sh

Une fois dans le container, listez les interfaces réseau.

# ip link show

La liste des interfaces devrait être la même que celle obtenue directement depuis la machine hôte. Sortez du container et supprimez le.

# exit
$ docker rm cnt1

1.3. Lancement d’un container en utilisant le driver none

Lancez à présent un shell interactif dans un container basé sur l’image alpine mais cette fois en utilisant l’option *--network=none- de façon à ne pas donner au container de connectivité vers l’extérieur.

$ docker container run -ti --name cnt1 --network none alpine:3.8 sh

Listez les interfaces réseau du container.

# ip a show

Vous devriez constater que seule lo (l’interface locale) est disponible.

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever

Depuis ce container, essayez de lancer un ping sur le DNS de Google (8.8.8.8).

# ping 8.8.8.8

Vous devriez obtenir le message d'erreur suivant, car ce container n'a pas de connection avec l’extérieur.

PING 8.8.8.8 (8.8.8.8): 56 data bytesping: sendto: Network unreachable

Sortez du container et supprimez le.

# exit
$ docker rm cnt1

2. Bridge network

Le réseau bridge créé par défaut permet à des containers tournant sur la même machine de communiquer entre eux comme nous allons le voir maintenant.

Dans cette partie, nous allons lancer 2 containers, chacun étant attaché au network bridge.

Il n’est pas nécessaire de spécifier l’option --network car un container est attaché à ce network par défaut.

Utilisez la commande suivante pour lancer un premier container, nommé cnt1.

$ docker container run -d --name cnt1 alpine:3.8 sleep 10000

Avec la commande suivante, récupérer l'adresse IP du container.

$ docker container inspect -f "{{ .NetworkSettings.IPAddress }}" cnt1

Exemple de retour de cette commande:

172.17.0.2

Note: l’adresse IP obtenue sur votre environnement pourra être différente.

Utilisez la commande suivante pour lancer un shell interactif dans un second container basé sur l’image alpine, et nommé cnt2.

$ docker container run -ti --name cnt2 alpine:3.8 sh

Depuis ce shell, essayez de lancer un ping sur le container cnt1 en utilisant l’adresse IP de ce dernier.

Note: lancez la commande suivante en utilisant l’IP du container cnt1 obtenue précédemment.

# ping -c 3 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.135 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.098 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.110 ms
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.098/0.114/0.135 ms

Cette commande fonctionne correctement, le container cnt2 est capable de pinguer le container cnt1 en utilisant l’adresse IP de ce dernier.

Toujours depuis le shell obtenu dans le container cnt2, lancez un ping vers cnt1 mais cette fois-ci en utilisant le nom cnt1 au lieu de son adresse IP.

# ping -c 3 cnt1

Vous devriez obtenir le message suivant:

ping: bad address 'cnt1'

Il n’est pas possible, pour des containers attachés au network bridge de communiquer via leur nom. Nous verrons que cela est par contre possible si nous définissons notre propre network de type bridge.

Sortez du container cnt2 et supprimez cnt1 et cnt2.

# exit
$ docker rm -f cnt1 cnt2

3. User defined bridge network

Nous avons vu que, par défaut, 3 networks sont définis (ceux-ci sont nommés bridge, host et none). Il est également possible d’en créer d’autres, c’est ce que nous allons voir dans les sections suivantes.

Utilisez la commande suivante afin de créer un nouveau network de type bridge nommé bnet.

$ docker network create --driver bridge bnet

Note: nous spécifions ici l’option --driver bridge pour l’illustration même si ce n’est pas nécessaire car c’est le driver utilisé par défaut.

Utilisez la commande suivante pour lancer un container, nommé cnt1 et attaché au network bnet.

$ docker container run -d --name cnt1 --network bnet alpine:3.8 sleep 10000

Comme précédemment, recupérez l’adresse IP de ce container.

Note: cette fois, nous utilisons la clé .NetworkSettings.Networks.bnet.IPAddress afin de récupérer l’IP alouée sur le réseau bnet.

$ docker container inspect -f "{{ json .NetworkSettings.Networks.bnet.IPAddress
}}" cnt1

Exemple de résultat obtenu:

172.18.0.2

Note: l’adresse IP obtenue sur votre environnement peux être différente.

Lancez un shell interactif dans un second container, nommé cnt2, et également attaché au network bnet.

$ docker container run -ti --name cnt2 --network bnet alpine:3.8 sh

Depuis cnt2, lancez un ping sur l'adress IP de cnt1.

# ping -c 3 172.18.0.2

Vous devriez obtenir un résultat similaire à celui ci-dessous:

PING 172.18.0.2 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=1.557 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.093 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.088 ms
--- 172.18.0.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.088/0.579/1.557 ms

Nous pouvons constater que, comme c’est le cas pour le network bridge par défaut, les containers attachés au network bnet peuvent communiquer via leur adresse IP.

Comme précédemment, essayez de lancer un ping sur cnt1 en utilisant le nom du container au lieu de son adresse IP.

# ping -c 3 cnt1

Vous devriez obtenir un résultat similaire à celui ci-dessous:

PING cnt1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.096 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.095 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.089 ms
--- cnt1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.089/0.093/0.096 ms

Nous pouvons constater que, contrairement au cas du network bridge par défaut, les containers attachés au network bnet peuvent communiquer via leur nom, il y a une résolution de noms qui s'opère via un serveur DNS embarqué.

Sortez du container cnt2 et supprimez cnt1 et cnt2.

# exit
$ docker rm -f cnt1 cnt2