Docker networking

Zodra een docker-container wordt gestart zal de Docker Engine hier een netwerk interface aan koppelen en er een IP adres aan ‘binden’. Tevens wordt een default gateway verwijzing gemaakt en DNS en routing tables. Standaard kunnen alle containers op de host met elkaar communiceren. Soms is dat niet gewenst en kan er een custom-network gebruikt worden of door gebruik te maken van een network provider plugin.

Network providers maken gebruik van drivers en voor docker wordt dat gedaan met de ‘--net=‘ parameter, bijvoorbeeld:

$ docker run --rm --net=host busybox ip addr

Er zijn voor docker 5 network types beschikbaar:

  • Host Networking: the docker container gebruikt hetzelfde netwerk en IP adres als de host waarop het draait.
  • Bridge Networking: de container maakt gebruik van een private network op de host en kan communiceren met alle containers op de host die hier ook gebruik van maken. Communicatie vanuit andere netwerk(en) gaat via NAT. Dit is tevens de default zla de –net optie niet wordt meegegeven.
  • Custom Bridge Networking: Gelijk aan Bridge Networking echter met een specifieke network interface op de host.
  • Container-defined Networking: Een container kan een netwerk voor een andere container delen.
  • No Networking: containers zonder netwerk interface.

Host Networking

Op een host met hierop docker geïnstalleerd, voer de volgende commando’s uit:

$ ip addr
$ docker run --rm -it --net=host busybox ip addr

De output van beide commando’s zal hetzelfde zijn, de docker container maakt immers gebruik van de shared network adapters van de host waarop het draait.

1: lo:  mtu 65536 qdisc noqueue 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
2: eth0:  mtu 1500 qdisc mq qlen 1000
     link/ether dc:a6:32:f4:f2:a2 brd ff:ff:ff:ff:ff:ff
     inet 192.168.1.51/24 brd 192.168.1.255 scope global dynamic eth0
        valid_lft 61134sec preferred_lft 61134sec
     inet6 fe80::dea6:32ff:fef4:f2a2/64 scope link
        valid_lft forever preferred_lft forever
3: wlan0:  mtu 1500 qdisc fq_codel qlen 1000
     link/ether dc:a6:32:f4:f2:a3 brd ff:ff:ff:ff:ff:ff
4: docker0:  mtu 1500 qdisc noqueue
     link/ether 02:42:28:27:aa:3f 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
     inet6 fe80::42:28ff:fe27:aa3f/64 scope link
        valid_lft forever preferred_lft forever

Bridge Networking

Indien hetzelfde docker commando wordt gegeven zonder de --net optie, zal gebruik gemaakt worden van de docker0 bridge.

docker run --rm -it busybox /bin/sh
$ ip addr
1: lo:  mtu 65536 qdisc noqueue 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
6: eth0@if33:  mtu 1500 qdisc noqueue
     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

Op de host zelf zal een interface gemaakt worden voor de container:

$ ip addr | grep veth
7: veth1718da2@if46:  mtu 1500 qdisc noqueue master docker0 state UP group default
     link/ether ae:eb:61:f3:c1:e5 brd ff:ff:ff:ff:ff:ff link-netnsid 1
     inet6 fe80::aceb:61ff:fef3:c1e5/64 scope link
        valid_lft forever preferred_lft forever

Ook al maakt de docker container gebruik van een IP adres van het bridge network, services die in de container draaien zijn niet beschikbaar buiten de host waar de container op draait. Om dat wel beschikbaar te maken moet een de Docker Engine de instructie gegeven worden om een bepaalde TCP-port door te sturen van de host naar de container. Bijvoorbeeld een webserver dir op port 80 draait in een container en die bereikbaar moet zijn vanaf de host op port 8080:

$ docker run --name nginx --rm -p 8080:80 -d nginx
$ docker inspect nginx --format='{{json .NetworkSettings.IPAddress}}'
"172.17.0.2"

Docker Daemon maakt nu een rule aan in Netfilter dat outbound packages het IP adres van de host meegeeft en inbound packages worden doorgestuurd naar de docker container.

$ sudo iptables -L -t nat -v
Chain PREROUTING (policy ACCEPT 141 packets, 11704 bytes)
  pkts bytes target     prot opt in     out     source               destination
  344K   26M DOCKER     all  --  any    any     anywhere             anywhere             ADDRTYPE match dst-type LOCAL
  
 Chain INPUT (policy ACCEPT 141 packets, 11704 bytes)
  pkts bytes target     prot opt in     out     source               destination

 Chain OUTPUT (policy ACCEPT 147 packets, 9629 bytes)
  pkts bytes target     prot opt in     out     source               destination
    47  2820 DOCKER     all  --  any    any     anywhere            !localhost/8          ADDRTYPE match dst-type LOCAL
  
 Chain POSTROUTING (policy ACCEPT 147 packets, 9629 bytes)
  pkts bytes target     prot opt in     out     source               destination
     0     0 MASQUERADE  all  --  any    !br-844b6c606eb4  172.18.0.0/16        anywhere
     0     0 MASQUERADE  all  --  any    !docker0  172.17.0.0/16        anywhere
     0     0 MASQUERADE  tcp  --  any    any     172.17.0.2           172.17.0.2           tcp dpt:http

Chain DOCKER (2 references)
  pkts bytes target     prot opt in     out     source               destination
     0     0 RETURN     all  --  docker0 any     anywhere             anywhere
     0     0 DNAT       tcp  --  !docker0 any     anywhere             anywhere             tcp dpt:http-alt to:172.17.0.2:80

Custom-Bridge Networking

Indien er geen noodzaak is om de default bridge te gebruiken, maak een custom bridge network op de host.

$ docker network create customnetwork
$ docker run -it --rm -d --name=container-1 --net=customnetwork busybox /bin/sh
$ docker run -it --rm -d --name=container-2 --net=customnetwork busybox /bin/sh

Container-defined Networking

De volgende commando’s maken twee containers aan die een gedeeld network interface gebruiken. Dit is feitelijk hoe Kubernetes ook werkt met pods.

$ docker run -it --rm -d --name=container-1 busybox /bin/sh
$ docker run -it --rm -d --name=container-2 --net=container:container-1 busybox /bin/sh

No Network

In die gevallen waarbij geen netwerk voor de container nodig is.

$ docker run --rm --net=none --name busybox busybox ip addr
1: lo:  mtu 65536 qdisc noqueue 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