Docker und IPv6

Docker unterstützt seit geraumer Zeit IPv6. Leider ist die Dokumentation aber derart schwammig und in Teilen sogar falsch, dass es eine ziemliche Herausforderung ist, IPv6 tatsächlich zu aktivieren. Ein Bug auf Github zeigt, wie die korrekte Konfiguration gelingt.

Was die Dokumentation verschweigt, ist, dass dem “default bridge network” zwingend ein IPv6-Subnet zugewiesen werden muss. Tut man dies nicht, scheitert der Start des Docker-Daemons mit ehr unschönen und etwas kryptischen Fehlermeldungen:

IPv6 Routing

Docker spricht kein SLAAC oder DHCPv6 und kann sich daher nicht selbstständig seine Subnets beim nächsten Router “bestellen”. Außerdem sieht IPv6 kein NAT vor, sodass die von IPv4 bekannte Herangehensweise, ein privates Subnet hinter einem NAT zu verstecken, ebenfalls nicht funktioniert. Bevor Sie mit der Konfiguration beginnen können, müssen Sie also etwas manuelles Subnetting betreiben und Ihren Router konfigurieren.

Gemäß der aktuellen RFC-Norm zu IPv6, sind ISPs angehalten, ihren Kunden mindestens einen /56-Prefix, maximal einen /48-Prefix zuzuweisen. Für diese Dokumentation gehen wir davon aus, dass wir von unserem Provider den festen Prefix 2001:db8:1::/48 erhalten haben. Wir gehen außerdem davon aus, dass unser Router innerhalb dieses Subnets die IP-Adressen 2001:db8:1::1/64 besitzt.

An dieser Stelle sei auch direkt angemerkt, dass mangels SLAAC und DHCPv6 keine dynamischen Prefixe unterstützt werden. Wenn Sie also von Ihrem Provider nur einen dynamischen Prefix oder eine /64-Prefix erhalten, nutzen Sie einfach ein privates Subnet aus dem Bereich fd00::/10.

Zu allererst gilt es, dem Docker-Host eine statische IPv6-Adresse zuzuteilen, damit wir das Routing konfigurieren können:

iface eth0 inet6 static
    address 2001:db8:1::2
    netmask 64
    gateway 2001:db8:1::1

Für unseren Docker-Daemon nutzen wir einen /56-Prefix aus unserem vorhandenen Subnet. Um Überschneidungen mit dem SLAAC-Subnet 2001:db8:1::/64 zu vermeiden, nutzen wir 2001:db8:1:100::/56. Auf dem Router erstellen wir nun eine statische Route in das Netz 2001:db8:1:100::/56 mit Gateway 2001:db8:1::2. Wie Sie das auf Ihrem Modell durchführen entnehmen Sie bitte der Dokumentation des Gerätes.

Auf dem Docker-Host müssen wir nun das IPv6-Routing aktivieren. Dazu tragen wir die Zeile

net.ipv6.conf.all.forwarding=1

in die Datei /etc/sysctl.conf ein und aktivieren diese mit dem Befehl

sysctl -p /etc/sysctl.conf

Die Docker-Dokumentation verlangt nun, eine Route zum Interface docker0 zu erstellen. Das ist falsch. Dieser Schritt entfällt, denn die Docker-Netze sind einzelne virtuelle Interfaces auf dem Hostsystem. Würden wir unseren gesamtes Subnet in das “default bridge network” (docker0) leiten, wären die anderen Netze funktionsuntüchtig. Der Linux-Kernel erstellt die erforderlichen Routen für jedes Docker-Netzwerk selbstständig.

Docker-Konfiguration

Nun ist es an der Zeit, Docker zu konfigurieren. Dazu beendet wir erst einmal den Daemon:

service docker stop

Anschließend aktivieren wir über zwei Einträge in der Datei /etc/docker/daemon.json die IPv6-Konfiguration und teilen dem “default bridge network” (docker 0) einen /64-Prefix aus unserem Docker-Subnet zu:

{
"ipv6": true,
"fixed-cidr-v6": "2001:db8:1::100:/64",
}

Anschließend starten wir den Docker-Daemon wieder:

service docker start

Im letzten Schritt erstellen unsere zusätzlichen Docker-Netzwerke. Wir benötigen mindestens eines, denn schlißelich ist das “default bridge network” seit geraumer Zeit deprecated und sollte keine Container mehr enthalten. Diese zusätzlichen Netze können, müssen aber nicht mit IPv6 ausgestattet werden:

docker network create --ipv6 --subnet "2001:db8:1:101::/64" testnet1

Der Parameter --ipv6 aktiviert die IPv6-Unterstützung, der Parameter --subnet teilt dem Netzwerk sein Subnet zu. Es ist nicht erforderlich, dem Docker-Host eine IP-Adresse für diese Subnets zu verpassen. Das erledigt er selbstständig.

Selbstverständlich können wir mit einem weiteren –subnet auch noch ein IPv4-Subnet zuteilen. DAs ist aber nicht wirklich erforderlich, denn Docker übernimmt diese Aufgabe automatisch. Wer aus kosmetischen Gründen sein Subnet selbst benennen möchte, kann dies aber selbstverständlich tun:

docker network create --ipv6 --subnet "2001:db8:1:102::/64" --subnet 192.168.102.0/24 testnet 2

Sobald wir nun einen Container in das Docker-Netz einfügen, wird er von Docker automatisch mit IPv4- und IPv6-Adressen ausgestattet:

docker network connect testnet1 testcontainer1
docker network disconnect bridge testcontainer1

Selbstverständlich ist es auch möglich, dem Container statische IP-Adressen zuzuordnen. Bei IPv4 ist das aufgrund des NAT relativ nutzlos. Über IPv6 ist der Container allerdings mangels NATv6 direkt von außen ansprechbar, ohne das Portweiterleitungen erforderlich wären. Es kann also zweckmäßig sein, ihm eine statische IPv6-Adresse zu verpassen, um DNS-Namen u.ä. besser verwalten zu können:

docker network connect --ip6 2001:db8:1:102::2 testnet2 testcontainer2
docker network disconnect bridge testcontainer2

Kommentar hinterlassen