Skip to content

Gestion du réseau

Container Network Model

Docker s'appuie sur le Container Network Model (CNM) pour la configuration des interfaces réseaux des containers Linux.

CNM

Le CNM repose sur 3 composants :

  • Sandbox : stack réseau d'un container
  • Endpoint : interface réseau qui relie un container à un réseau
  • Network : groupe de endpoints qui peuvent communiquer ensemble

Pour implémenter les drivers du CNM, Docker utilise différentes technologies :

  • bridges linux
  • namespaces réseau
  • paire d'interfaces virtuelles (veth pairs)
  • iptables

L'ensemble de ces éléments fournissent différentes fonctionnalités :

  • Règles de gestion du trafic
  • Segmentation réseau
  • Service discovery
  • Load balancing
  • Routing mesh (Swarm, deprecated)

Cli

Comme pour les autres primitives Docker fournit un ensemble de fonctionnalités :

shell
docker network --help

Usage:  docker network COMMAND

Manage networks

Commands:
  connect     Connect a container to a network
  create      Create a network
  disconnect  Disconnect a container from a network
  inspect     Display detailed information on one or more networks
  ls          List networks
  prune       Remove all unused networks
  rm          Remove one or more networks

Run 'docker network COMMAND --help' for more information on a command.

Drivers

La commande docker network create permet de créer un nouveau network en spécifiant le driver à utiliser :

shell
docker network create --driver DRIVER [OPTIONS] NAME

Suivant le driver utilisé, il sera possible d'utiliser certaines fonctionnalités

Par défaut plusieurs drivers sont disponibles :

  • host
  • none
  • bridge
  • overlay
  • macvlan

Il est possible d'en installer d'autres, compatibles avec le CNM via des plugins comme par exemple Calico, Flannel.

Networks d'un hôte Docker

Par défaut Docker crée 3 networks

shell
docker network ls

NETWORK ID     NAME          DRIVER    SCOPE
f1c0b9915771   bridge        bridge    local
8b4b32a64a0f   host          host      local
6ebccb73a16b   none          null      local
  • Bridge

Créé sur la machine hôte et représenté par le bridge Linux docker0 il permet à l'ensemble des containers de pouvoir dialoguer. Par défaut, tous les containers démarrés sont visibles via ce bridge.

shell
ip a show docker0

5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:4d:d7:a8:ff 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

Il est intéressant de noter qu'avec du bridge, la communication est limitée aux containers d'une même machine.

Bridge docker0

Pour pouvoir fonctionner le bridge, crée une paire d'interfaces virtuelles, l'une étant connectée au container et l'autre à la machine hôte.

Pour illustrer listons les interfaces bridges présentes sur la machine hôte

shell
brctl show

bridge name bridge id  STP enabled interfaces
docker0  8000.02424dd7a8ff no

Démarrons ensuite un container et regardons les interfaces réseaux

shell
docker container run -ti alpine sh
ip a

20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

Regardons à nouveau les interfaces bridges

shell
brctl show

bridge name bridge id  STP enabled interfaces
docker0  8000.02424dd7a8ff no  vethf8ac169

Le container démarré est donc visible sur le bridge docker0.

Le mode bridge est cependant limité : il n'est pas possible de faire de la résolution de noms dans ce mode.

Pour illustrer démarrons un container basé sur l'image nginx

shell
docker run -d --name=nginx

0a8514c66bf6946f55da967598777d3e7f4055f64f1dff55d5b7fb12cf446b72

Récupérons l'ip du container

shell
ocker inspect -f '{{.NetworkSettings.IPAddress}}' 0a8514

172.17.0.3

Démarrons un nouveau container et essayons de pinger le container par son nom, puis son ip

shell
docker run -it alpine sh

/ # ping -c3 nginx
ping: bad address 'nginx'

ping -c3  172.17.0.3

Inspectons maintenant les détails du network bridge docker0

shell
docker network ls

NETWORK ID     NAME      DRIVER    SCOPE
f1c0b9915771   bridge    bridge    local
8b4b32a64a0f   host      host      locanl
6ebccb73a16b   none      null      local
shell
docker inspect f1c0b9915771

[
    {
        "Name": "bridge",
        "Id": "f1c0b99157713ab21716758a9405929ddee5ce46589090bdc5d118d7b436d43b",
        "Created": "2021-11-27T09:24:28.563137709+01:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "0a8514c66bf6946f55da967598777d3e7f4055f64f1dff55d5b7fb12cf446b72": {
                "Name": "nginx",
                "EndpointID": "f4a26177ab1ecf3d20724f7cc0f646c8b19464b324c93ecd59190cc4ae634ea6",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "d557cc0af0adedc5c4aec2906f9042e582502e7827d17dd135394f7df6156ac1": {
                "Name": "blissful_swartz",
                "EndpointID": "4af5dbf4ca3f3acb47ec13765206c6d3a58987029fc9f59bf980b2522fbc0b95",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

On constate que les container précédents sont attachés au bridge par défaut étant donné qu'aucun network n'a été spécifié au démarrage des containers. En outre on illustre également ici la connectivité via des paires virtuelles, la machine hôte étant accessible via 172.17.0.1 là où par exemple le container nginx a pour ip 172.17.0.2.

  • Host

Il permet d'accéder à la stack réseau de la machine hôte.

Démarrons un container

shell
docker container run -it --network=host alpine sh

Listons les networks depuis le container et depuis la machine hôte

shell
ip link

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp9s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether 04:92:26:d4:b1:d5 brd ff:ff:ff:ff:ff:ff
3: wlp6s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN qlen 1000
    link/ether be:d0:cd:94:cc:03 brd ff:ff:ff:ff:ff:ff
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN 
    link/ether 02:42:4d:d7:a8:ff brd ff:ff:ff:ff:ff:ff
15: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN qlen 1000
    link/ether 52:54:00:74:28:1f brd ff:ff:ff:ff:ff:ff

On constate que les résultats sont identiques, le container a accès aux mêmes interfaces.

  • None

Il donne à un container une interface locale et ne lui permet pas de dialoguer avec l'extérieur

Démarrons un container avec le driver none et listons les interfaces réseaux depuis celui-ci

shell
docker container run -it --network none alpine sh

/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    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

On constate que seul le loopback est initialisé. Il peut être utile pour faire en sorte que le container démarré ne puisse pas être accédé depuis l'extérieur ; dans le cadre d'un container de debug par exemple.

Création d'un network bridge

Il est possible de créer un network de type bridge qui pourra être ensuite utilisé pour connecter des containers. Comme pour docker0 une paire d'interface bridge sera créée, dont une paire sera associée au container et dont l'autre sera connectée au namespace network root, c'est à dire à la machine hôte.

L'intérêt principal est de permettre la résolution DNS, ce qui n'est pas possible avec le docker0. C'est ce type de bridge qui sera créé par docker-compose lorsque que l'on définit les différents networks utilisables par les services. Pour aller plus loin la documentation du driver précise son fonctionnement.

User Bridge

Créons un nouveau nework de type bridge

shell
docker network create --driver=bridge skynet

44568baae9fa5f2b8a27c3b03919ef1db97f0fcbcc45d3149b954f15089f990b

Listons les réseaux dispnoibles

shell
docker network ls

NETWORK ID     NAME      DRIVER    SCOPE
f1c0b9915771   bridge    bridge    local
8b4b32a64a0f   host      host      local
6ebccb73a16b   none      null      local
44568baae9fa   skynet    bridge    loca

Le réseau skynet est désormais disponible

Démarrons un container nginx sur ce network

shell
docker run -d --name=nginx --network=skynet nginx

On peut désormais pinger ce container depuis un autre container

shell
docker run -ti --name=alpine --network=skynet alpine sh

/ # ping -c3 nginx
PING nginx (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.094 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.096 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.095 ms

--- nginx ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.094/0.095/0.096 ms