Bei der Erstinstallation bringt Docker auch ein Setup für das Netzwerk mit. Auch wenn Container aus Sicht des Speicher- und Rechtemanagements etc. isoliert sein sollen, brauchen sie doch eine Schnittstelle zur Kommunikation nach außen oder untereinander – und das ist das Netzwerk. Dabei soll durchaus unterschieden werden, ob ein Container die Netzwerkeigenschaften des Hosts übernimmt , nur mit bestimmten Containern unter sich oder gar nicht mit der Außenwelt kommunizieren soll.
Grundsätzlich gibt es noch eine vierte Art der Netzwerkkommunikation, nämlich wenn mehrere Hosts in einem Docker-Schwarm kommunizieren sollen, aber das soll uns vorerst nicht weiter interessieren (Overlay-Netzwerk).
Doch zuerst betrachten wir uns die Netzwerkkonfiguration des Hosts ohne Docker:
root@Docker ~: ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:56:58:8b brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
valid_lft 86315sec preferred_lft 86315sec
inet6 fe80::a00:27ff:fe56:588b/64 scope link
valid_lft forever preferred_lft forever
root@Docker ~: ip route show
default via 10.0.2.2 dev enp0s3
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15
root@Docker ~: ping -c 3 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=55 time=26.5 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=55 time=20.8 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=55 time=21.9 ms
--- 1.1.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 7ms
rtt min/avg/max/mdev = 20.830/23.086/26.521/2.471 ms
Du solltest grundsätzlich 2 Netzwerkkarten sehen, das Loopback (interne Schnittstelle des Hosts) und eine physikalische Karte. In meinem Code-Schnipsel ist ein Auszug des Docker-Hosts aus meiner virtuellen Labor-Umgebung zu sehen, das erkennst du zum einen an den MAC-Adressen (Virtualbox) und ggf. auch an der IP-Adresse der „physikalischen“ Netzwerkkarte enp0s3 (Virtualbox NAT). Aber das es sich hier lediglich um eine Virtualbox-Umgebung handelt, ist nebensächlich. Auf einem physikalischen Server oder vServer im Internet ist das ähnlich. Die enp0s3 (kann bei dir anders heißen) ist hier die Netzwerkarte, die mit der Außenwelt (LAN/WAN) kommuniziert, sie hat derzeit die IP-Adresse 10.0.2.15/24 und eine funktionale Verbindung zum Internet. Als Default Gateway ist die 10.0.2.2 über enp0s3 eingetragen, das ist die IP-Adresse von NAT-Interface des Host, der natürlich auch eine funktionale Verbindung zum Internet haben muss, sonst wäre der Ping nicht erfolgreich.
Nach der Installation und dem automatischen Start von Docker ist eine weitere Netzwerkkarte verfügbar, die die Grundlage unseres virtuellen Netzwerkes bildet – docker0
root@Docker ~: ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:56:58:8b brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
valid_lft 84994sec preferred_lft 84994sec
inet6 fe80::a00:27ff:fe56:588b/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:13:41:bc:93 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
root@Docker ~: ip route show
default via 10.0.2.2 dev enp0s3
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
root@Docker ~: docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@Docker ~: docker network list
NETWORK ID NAME DRIVER SCOPE
1a4af9aa8f95 bridge bridge local
1c1adf3d581b host host local
23752a841ea3 none null local
docker0 ist die virtuelle Netzwerkschnittstelle der Docker-Instanz, an die sich weitere interne Netzwerkschnittstellen (bridge, host, none) der Docker-Instanz binden. Per default hat die Docker-Instanz die IP-Adresse 172.17.0.1/16. Ohne aktive Container ist das Interface down, die Route in dieses Subnetz inaktiv. Ein Container ist grundsätzlich Mitglied einer oder mehrerer Docker-Netzwerke (siehe docker network list), die Docker-Netzwerke kommunizieren mit docker0 und docker0 kann widerrum mit dem Host kommunizieren. Damit ungewollte Kommunikation unterbunden wird, installiert Docker bei der Erstinstallation gleich passende iptables-Regeln mit.
root@Docker ~: iptables -nvL
Chain INPUT (policy ACCEPT 14 packets, 732 bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER-USER all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 DOCKER-ISOLATION-STAGE-1 all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 18 packets, 892 bytes)
pkts bytes target prot opt in out source destination
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
Chain DOCKER-ISOLATION-STAGE-1 (1 references)
pkts bytes target prot opt in out source destination
0 0 DOCKER-ISOLATION-STAGE-2 all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain DOCKER-ISOLATION-STAGE-2 (2 references)
pkts bytes target prot opt in out source destination
0 0 DROP all -- * docker0 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain DOCKER-USER (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Um den Umgang mit dem Netzwerk näher erläutern zu können, werde ich die folgende Containertopologie nutzen:
AUßENWELT 10.0.2.2/24
|
|
+-------+-------------------------------------------------------------------+
| NAT 10.0.2.15/24 host Docker |
| |
| ---+-------------------------------------------+------------------ |
| | | |
| | | |
| | | |
| +----+---------------------------+ +-----+------------------+ |
| | | bridge | | | bridge2| |
| | +-+--------+ | | +--+-------+ | |
| | |172.17.0.1| | | |172.18.0.1| | |
| | |docker0 | | | |br-b011...| | |
| | +--+-------+ | | +--+-------+ | |
| | | | | | | |
| | | 172.17.0.0/16 | | | 172.18.0.0/16 | |
| | ---+-+-----------------+--- | | ---+-+-------------- | |
| | | | | | | | |
| | | | | | | | |
| | | | | | | | |
| | +--+-------+ +-------+--+ | | +--+-------+ | |
| | |172.17.0.2| |172.17.0.3| | | |172.18.0.2| | |
| | |busybox1 | |busybox2 | | | |busybox3 | | |
| | +----------+ +----------+ | | +----------+ | |
| | | | | |
| +--------------------------------+ +------------------------+ |
| |
+---------------------------------------------------------------------------+
Erläuterung: Der Host Docker hängt weiterhin mit seiner physikalischen Schnittstelle im LAN/WAN (IP-Adresse 10.0.2.15/24. Die Docker bringt per Default die virtuelle Netzwerkkarte docker0 mit, welche in einer Bridge eingebunden ist und die IP-Adresse 172.17.0.1/16 hat. Weiterhin sind an diese Bridge die Container busybox1 und busybox2 gebunden. Busybox ist ein Container, welcher ein minimalistisches embedded-Linux enthält. Somit sind wir in der Lage, später auch IP-Adressen zu pingen und das Netzwerk auf Funktion zu testen.
Später erzeugen wir eine weitere Bridge, binden Container busybox3 an diese und testen ebenfalls alle Netzwerkverbindungen. So erarbeiten wir uns Schritt für Schritt die Arbeitsweise des Docker-Netzwerkes. Die Farben der Container oben entspricht auch den Farben der Shell-Eingaben weiter unten – also achte darauf, in welchem Terminal du gerade Eingaben machst.
Bridge – das Standard-Netzwerk unter Docker
Starten wir den ersten Container busybox1 in der Standard-Bridge (Angabe von –net bridge) kann deshalb weggelassen werden.
root@Docker ~: docker run -it --net bridge --name busybox1 --rm busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> 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
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> 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
/ # ip route show
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 scope link src 172.17.0.2
/ # ping -c 3 1.1.1.1
PING 1.1.1.1 (1.1.1.1): 56 data bytes
64 bytes from 1.1.1.1: seq=0 ttl=54 time=22.973 ms
64 bytes from 1.1.1.1: seq=1 ttl=54 time=21.182 ms
64 bytes from 1.1.1.1: seq=2 ttl=54 time=20.545 ms
--- 1.1.1.1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 20.545/21.566/22.973 ms
/ #
Okay, das Netzwerk ist funktional. Wir haben eine IP, ein Gateway und erreichen auch das Internet (hier exemplarisch 1.1.1.1). Doch wie schaut es hinter den Kulissen aus, werfen wir doch mal ein Blick auf den Docker-Host (öffne dazu ein neues Terminal und beende die Session busybox1 nicht).
root@Docker ~: docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4d0c23103472 busybox "sh" 7 minutes ago Up 7 minutes busybox1
root@Docker ~: ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:56:58:8b brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
valid_lft 57158sec preferred_lft 57158sec
inet6 fe80::a00:27ff:fe56:588b/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:42:e3:6f:c2 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:42ff:fee3:6fc2/64 scope link
valid_lft forever preferred_lft forever
10: vethabc42a8@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether a2:fd:e5:9c:81:f8 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::a0fd:e5ff:fe9c:81f8/64 scope link
valid_lft forever preferred_lft forever
root@Docker ~: ip route show
default via 10.0.2.2 dev enp0s3
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
root@Docker ~: brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024242e36fc2 no vethabc42a
Wie du mit docker ps siehst, läuft jetzt die busybox1. War ja auch zu erwarten. Aber viel interessanter ist, das es eine weitere (virtuelle) Netzwerkkarte auf dem Host gibt – vethabc42a8@if9. In der ersten Zeile steht auch, das diese an master docker0 gebunden ist (ich habe beides orange markiert). Außerdem ist das Routing über 172.17.0.1 jetzt aktiv und mit dem letzten Kommando brctl show sieht du, das diese neue Netzwerkkarte tatsächlich an docker0 gebunden ist (inkl. bridge id). Falls das letzte Kommando bei dir nicht funktioniert, dann installiere mit apt install bridge-utils auf dem Host das passende Paket einfach nach.
Das du tatsächlich NAT nutzt, siehst du, wenn du dir die Tabelle NAT der iptables anschaust:
root@Docker ~: iptables -nvL -t nat
Chain PREROUTING (policy ACCEPT 2 packets, 1152 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 13 packets, 692 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 13 packets, 692 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
So weit – so gut. Als nächstes starten wir Container busybox2 und schauen uns ebenfalls die Netzwerkfunktionalität an (auch hier am besten ein weiteres Terminal öffnen).
root@Docker ~: docker run -it --net bridge --name busybox2 --rm busybox
/ # ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> 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
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
/ # ip route show
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 scope link src 172.17.0.3
/ # ping -c 3 1.1.1.1
PING 1.1.1.1 (1.1.1.1): 56 data bytes
64 bytes from 1.1.1.1: seq=0 ttl=54 time=66.460 ms
64 bytes from 1.1.1.1: seq=1 ttl=54 time=20.838 ms
64 bytes from 1.1.1.1: seq=2 ttl=54 time=21.471 ms
--- 1.1.1.1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 20.838/36.256/66.460 ms
/ # 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.070 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.077 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.099 ms
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.082/0.099 ms
/ #
Das auch dieser Container eine IP-Adresse, ein Gateway und eine Verbindung zum Internet hat, war ja bereits zu erwarten. Allerdings kann dieser Container auch mit dem anderen Container busybox1 auf derselben Bridge kommunizieren.
Das wird auch deutlich, wenn wir uns auf dem Host nochmal die Bridge näher ansehen, es gibt jetzt 2 virtuelle Netzwerkkarten vetha… und beide sind an docker0 gebunden.
root@Docker ~: ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:56:58:8b brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
valid_lft 56141sec preferred_lft 56141sec
inet6 fe80::a00:27ff:fe56:588b/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:42:e3:6f:c2 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:42ff:fee3:6fc2/64 scope link
valid_lft forever preferred_lft forever
10: vethabc42a8@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether a2:fd:e5:9c:81:f8 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::a0fd:e5ff:fe9c:81f8/64 scope link
valid_lft forever preferred_lft forever
12: vetha3b5123@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 5e:87:63:e2:7e:86 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::5c87:63ff:fee2:7e86/64 scope link
valid_lft forever preferred_lft forever
root@Docker ~: brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024242e36fc2 no vetha3b5123
vethabc42a8
Ansonsten hat sich nichts verändert. Also erstellen wir uns eine zweite Bridge und starten den dritten Container busybox3 auf dieser.
root@Docker ~: docker network create bridge2
root@Docker ~: docker run -it --net bridge2 --name busybox3 --rm busybox
/ # ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> 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
13: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
valid_lft forever preferred_lft forever
/ # ip route show
default via 172.19.0.1 dev eth0
172.19.0.0/16 dev eth0 scope link src 172.19.0.2
/ # ping -c 3 1.1.1.1
PING 1.1.1.1 (1.1.1.1): 56 data bytes
64 bytes from 1.1.1.1: seq=0 ttl=54 time=20.876 ms
64 bytes from 1.1.1.1: seq=1 ttl=54 time=23.088 ms
64 bytes from 1.1.1.1: seq=2 ttl=54 time=20.846 ms
--- 1.1.1.1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 20.846/21.603/23.088 ms
/ # ping -c 3 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss
/ #
Auch hier verhält sich der dritte Container wie die anderen beiden. Er hat eine IP- Adresse, eine Default Route und kann mit der Außenwelt kommunizieren. Aber er kann nicht mit den anderen beiden Containern „sprechen“. Dir fällt bestimmt auf, das er eine andere IP-Adresse hat – 172.19.0.2/16 und sich somit in einem anderen Subnetz befindet. Schauen wir uns den Host nochmal näher an:
root@Docker ~: docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
25cb66f94185 busybox "sh" 4 minutes ago Up 4 minutes busybox3
b8bb63b8a0b7 busybox "sh" 25 minutes ago Up 25 minutes busybox2
4d0c23103472 busybox "sh" 41 minutes ago Up 41 minutes busybox1
root@Docker ~: ip adress show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:56:58:8b brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
valid_lft 55268sec preferred_lft 55268sec
inet6 fe80::a00:27ff:fe56:588b/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:42:e3:6f:c2 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:42ff:fee3:6fc2/64 scope link
valid_lft forever preferred_lft forever
4: br-b011af4f4b33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:e3:fb:5f:29 brd ff:ff:ff:ff:ff:ff
inet 172.19.0.1/16 brd 172.19.255.255 scope global br-b011af4f4b33
valid_lft forever preferred_lft forever
inet6 fe80::42:e3ff:fefb:5f29/64 scope link
valid_lft forever preferred_lft forever
10: vethabc42a8@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether a2:fd:e5:9c:81:f8 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::a0fd:e5ff:fe9c:81f8/64 scope link
valid_lft forever preferred_lft forever
12: vetha3b5123@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 5e:87:63:e2:7e:86 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::5c87:63ff:fee2:7e86/64 scope link
valid_lft forever preferred_lft forever
14: veth4ad43b3@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-b011af4f4b33 state UP group default
link/ether 7e:96:65:b4:82:90 brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::7c96:65ff:feb4:8290/64 scope link
valid_lft forever preferred_lft forever
root@Docker ~: brctl show
bridge name bridge id STP enabled interfaces
br-b011af4f4b33 8000.0242e3fb5f29 no veth4ad43b3
docker0 8000.024242e36fc2 no vetha3b5123
vethabc42a8
root@Docker ~: ip route show
default via 10.0.2.2 dev enp0s3
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.19.0.0/16 dev br-b011af4f4b33 proto kernel scope link src 172.19.0.1
Wire du erkennst, laufen alle 3 Container, außerdem gibt es gleich 2 neue Netzwerkschnittstellen. Eine weitere Bridge br-b011af4f4b33 und ein virtuelles Kabel veth4ad43b3@if13, welches an diese Bridge gebunden ist. Das verdeutlicht auch nochmal das Kommando brctl show. Außerdem gibt es jetzt eine weitere Route, damit die Außenwelt mit der busybox3 kommunizieren kann.
Okay, vielleicht fragst du dich jetzt, wenn der Host doch ein Beinchen in allen 3 Netzen hat und gleichzeitig Gateway aller dieser Netze ist, warum routet er dann nicht einfach von einem Netz ins andere. Nun, dann schauen wir uns nochmal die Firewall iptables an:
root@Docker ~: iptables -nvL
Chain INPUT (policy ACCEPT 16 packets, 840 bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
5 420 DOCKER-USER all -- * * 0.0.0.0/0 0.0.0.0/0
5 420 DOCKER-ISOLATION-STAGE-1 all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 DOCKER all -- * docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- docker0 docker0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * br-b011af4f4b33 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 DOCKER all -- * br-b011af4f4b33 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- br-b011af4f4b33 !br-b011af4f4b33 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- br-b011af4f4b33 br-b011af4f4b33 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 20 packets, 1000 bytes)
pkts bytes target prot opt in out source destination
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
Chain DOCKER-ISOLATION-STAGE-1 (1 references)
pkts bytes target prot opt in out source destination
0 0 DOCKER-ISOLATION-STAGE-2 all -- docker0 !docker0 0.0.0.0/0 0.0.0.0/0
5 420 DOCKER-ISOLATION-STAGE-2 all -- br-b011af4f4b33 !br-b011af4f4b33 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain DOCKER-ISOLATION-STAGE-2 (2 references)
pkts bytes target prot opt in out source destination
5 420 DROP all -- * docker0 0.0.0.0/0 0.0.0.0/0
0 0 DROP all -- * br-b011af4f4b33 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain DOCKER-USER (1 references)
pkts bytes target prot opt in out source destination
5 420 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Wow! Was ist denn hier alles im Hintergrund passiert? Wenn du mit iptables ausreichend bewandert bist, wirst du wissen, das die FORWARD-Chain eine tragende Rolle für unser Setup hat. Alles was nicht geswitched (L2) wird, also geroutet (L3) werden muss, muss durch die FORWARD-Chain der interne Firewall.
DOCKER-USER können wir hier ignorieren, da liegen bei unserem Setup noch keine Einschränkungen vor. Aber schon die 2. Zeile der FORWARD-Chain schiebt alle Pakete in die Chain DOCKER-ISOLATION-STAGE-1 und diese prüft sowohl mit der ersten als auch zweiten Zeile, dass wenn ein Paket von einer Bridge auf eine andere Bridge will, dann wird es in die Chain DOCKER-ISOLATION-STAGE-2 verwiesen und dort dann verworfen. Das erklärt, warum eine Kommunikation von einer Bridge docker0 zu einer anderen Bridge br-b011af4f4b33 und umgekeht nicht möglich ist.
Werfen wir volllständigkeitshalber noch einen Blick auf die NAT-Tabelle der iptables:
root@Docker ~: iptables -nvL -t nat
Chain PREROUTING (policy ACCEPT 7 packets, 1572 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 15 packets, 800 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
0 0 MASQUERADE all -- * !br-b011af4f4b33 172.19.0.0/16 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 15 packets, 800 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- br-b011af4f4b33 * 0.0.0.0/0 0.0.0.0/0
Hier wurde lediglich für die neue Bridge ein weiteres MASQUERADE angelegt, alles schick sozusagen.
Host – gib mir einfach alles 😉
Als nächstes schauen wir uns an, was passiert, wenn wir als Netz statt einer Bridge den Host angeben. Dazu kannst du einfach in der Shell der busybox3 ein exit ausführen und diese erneut mit –net host starten (oder du startest eine 4. Busybox, du weiß spätestens jetzt , wie das geht).
/ # exit
root@Docker ~: docker run -it --net host --name busybox3 --rm busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 08:00:27:56:58:8b brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
valid_lft 83292sec preferred_lft 83292sec
inet6 fe80::a00:27ff:fe56:588b/64 scope link
valid_lft forever preferred_lft forever
3: br-b011af4f4b33: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
link/ether 02:42:49:73:6b:5c brd ff:ff:ff:ff:ff:ff
inet 172.19.0.1/16 brd 172.19.255.255 scope global br-b011af4f4b33
valid_lft forever preferred_lft forever
inet6 fe80::42:49ff:fe73:6b5c/64 scope link
valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
link/ether 02:42:c2:ed:82:0b 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:c2ff:feed:820b/64 scope link
valid_lft forever preferred_lft forever
8: veth1feb0b5@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
link/ether 62:b2:33:6c:77:95 brd ff:ff:ff:ff:ff:ff
inet6 fe80::60b2:33ff:fe6c:7795/64 scope link
valid_lft forever preferred_lft forever
14: veth5b3db0c@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
link/ether da:61:3c:7f:d1:ad brd ff:ff:ff:ff:ff:ff
inet6 fe80::d861:3cff:fe7f:d1ad/64 scope link
valid_lft forever preferred_lft forever
/ # ip r
default via 10.0.2.2 dev enp0s3
10.0.2.0/24 dev enp0s3 scope link src 10.0.2.15
172.17.0.0/16 dev docker0 scope link src 172.17.0.1
172.19.0.0/16 dev br-b011af4f4b33 scope link src 172.19.0.1
/ # ping -c 3 1.1.1.1
PING 1.1.1.1 (1.1.1.1): 56 data bytes
64 bytes from 1.1.1.1: seq=0 ttl=55 time=21.472 ms
64 bytes from 1.1.1.1: seq=1 ttl=55 time=21.158 ms
64 bytes from 1.1.1.1: seq=2 ttl=55 time=19.896 ms
--- 1.1.1.1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 19.896/20.842/21.472 ms
/ #
/ # 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.067 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.249 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.096 ms
--- 172.17.0.2 ping statistics --
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.067/0.137/0.249 ms
Oh, das ist interessant. Alle Netzwerkschnittstellen, die der Host kennt, sind auch jetzt im Container verfügbar. Somit kann der Container dieselben Ziele wie der Host erreichen, inklusive der Container der anderen Bridge. An der Firewall iptables hat sich nichts geändert, denn die im Zusammenhang stehenden Settings werden schon beim anlegen/löschen einer neuen Bridge erzeugt und die Bridge haben wir ja nicht gelöscht – wir nutzen sie nur gerade nicht.
root@Docker ~: brctl show
bridge name bridge id STP enabled interfaces
br-b011af4f4b33 8000.024249736b5c no
docker0 8000.0242c2ed820b no veth1feb0b5
veth5b3db0c
None – mehr Isolation geht wirklich nicht
Und als letztes schauen wir, was passiert, wenn du statt –net host einfach gar kein Netzwerk auswählst (also –net none).
/ # exit
root@Docker ~: docker run -it --net none --name busybox3 --rm busybox
/ # ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> 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
/ # ip route show
/ #
Traurig, oder? Es existiert nicht mal eine Netzwerkkarte außer dem Loopback, da kann ich mir einen Ping außer auf localhost sparen.
So und mit den Erkenntnissen wünsche ich dir noch viel Docker-Netzwerk-Spaß 😉