This is the start of a blog series dedicated to enabling IPv6 for the various components in the Docker toolbox.
I am starting the series off by talking about the basic configuration for enabling IPv6 with Docker Engine. There are some good examples that the Docker folks have put together that you will want to read through: https://docs.docker.com/engine/userguide/networking/default_network/ipv6/
Disclaimer: I am not teaching you Docker. There are a zillion places to go learn Docker. I am making the dangerous assumption that you already know what Docker is, how to install it and how to use it.
I am also not teaching you IPv6. There are also a zillion places to go learn IPv6. I am making the even more dangerous assumption that you know what IPv6 is, what the addressing details are and how to use it.
The graphic below shows a high-level view of my setup. I have two Docker hosts (docker-v6-1 and docker-v6-2) that are running Ubuntu 14.04. As of this first post, I am using Docker 1.8.2. Both hosts are attached to a Layer-2 switch via their eth0 interfaces. I am using static IPv4 addresses (not relevant here) for the host and StateLess Address AutoConfiguration (SLAAC) for IPv6 address assignment out of the Unique Local Address (ULA) FD15:4BA5:5A2B:1009::/64 range.
Preparing the Docker Host for IPv6:
As I mentioned before, I am using SLAAC-based assignment for IPv6 addressing on each host. You can use static, SLAAC, Stateful DHCPv6 or Stateless DHCPv6 if you want. I am not covering any of that as they don’t pertain directly to Docker.
Each Docker host as an IPv6 address and can reach the outside world:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:f3:f8:48 brd ff:ff:ff:ff:ff:ff inet 192.168.80.200/24 brd 192.168.80.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fd15:4ba5:5a2b:1009:cc7:2609:38b7:e6c6/64 scope global temporary dynamic valid_lft 86388sec preferred_lft 14388sec inet6 fd15:4ba5:5a2b:1009:20c:29ff:fef3:f848/64 scope global dynamic valid_lft 86388sec preferred_lft 14388sec inet6 fe80::20c:29ff:fef3:f848/64 scope link valid_lft forever preferred_lft forever
root@docker-v6-1:~# ping6 -n www.google.com PING www.google.com(2607:f8b0:400f:802::2004) 56 data bytes 64 bytes from 2607:f8b0:400f:802::2004: icmp_seq=1 ttl=255 time=13.7 ms 64 bytes from 2607:f8b0:400f:802::2004: icmp_seq=2 ttl=255 time=14.5 ms
Since I am using router advertisements (RAs) for my IPv6 address assignment, it is important to force the acceptance of RAs even when forwarding is enabled:
Now, if you haven’t already, install Docker using whatever method you are comfortable with. Again, this is not a primer on Docker. 🙂
Docker! Docker! Docker!
Now that the IPv6 basics are there on the host and you have Docker installed, it is time to set the IPv6 subnet for Docker. You can do this via the ‘docker daemon’ command or you can set it in the /etc/default/docker file. Below is the example using the ‘docker daemon’ command. Here, I am setting the fixed IPv6 prefix as FD15:4BA5:5A2B:100A::/64.
root@docker-v6-1:~# docker daemon --ipv6 --fixed-cidr-v6="fd15:4ba5:5a2b:100a::/64
Here is the same IPv6 prefix being set, but this is using the /etc/default/docker file:
DOCKER_OPTS="--dns 126.96.36.199 --dns 188.8.131.52 --ipv6 --fixed-cidr-v6=fd15:4ba5:5a2b:100a::/64"
Let’s fire up a container and see what happens. The example below shows that the container got an IPv6 address out of the prefix we set above:
root@docker-v6-1:~# docker run -it ubuntu bash root@aea405985524:/# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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 5: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:01 brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 scope global eth0 valid_lft forever preferred_lft forever inet6 fd15:4ba5:5a2b:100a:0:242:ac11:1/64 scope global valid_lft forever preferred_lft forever inet6 fe80::42:acff:fe11:1/64 scope link valid_lft forever preferred_lft forever
Ping the outside world:
root@aea405985524:/# ping6 www.google.com PING www.google.com(den03s10-in-x04.1e100.net) 56 data bytes 64 bytes from den03s10-in-x04.1e100.net: icmp_seq=1 ttl=254 time=14.6 ms 64 bytes from den03s10-in-x04.1e100.net: icmp_seq=2 ttl=254 time=12.5 ms
Fire up another container and ping the first container over IPv6:
root@docker-v6-1:~# docker run -it ubuntu bash root@e8a8662fad76:/# ping6 fd15:4ba5:5a2b:100a:0:242:ac11:1 PING fd15:4ba5:5a2b:100a:0:242:ac11:1(fd15:4ba5:5a2b:100a:0:242:ac11:1) 56 data bytes 64 bytes from fd15:4ba5:5a2b:100a:0:242:ac11:1: icmp_seq=1 ttl=64 time=0.094 ms 64 bytes from fd15:4ba5:5a2b:100a:0:242:ac11:1: icmp_seq=2 ttl=64 time=0.057 ms
Add the 2nd Docker host
Sweet! We have one host (docker-v6-1) running with two containers that can reach each other over IPv6 and reach the outside world. Now let’s add the second Docker host (docker-v6-2).
Repeat all of the steps from above but change the IPv6 prefix that Docker is going to use. Here is an example using FD15:4BA5:5A2B:100B::/64:
DOCKER_OPTS="--dns 184.108.40.206 --dns 220.127.116.11 --ipv6 --fixed-cidr-v6=fd15:4ba5:5a2b:100b::/64”
In order to have containers on one host reach containers on another host over IPv6, we have to figure out routing. You can enable host-based routing (the example I will show below) or you can just use the Layer-3 infrastructure you likely already have in your Data Center. I would recommend the latter option. Remember that Docker is not doing NAT for IPv6 so you have to have some mechanism to allow for pure L3 reachability between the various IPv6 address spaces you are using.
Here is an example of using host-based routing on each of the two Docker hosts. First, configure a static IPv6 route on the first Docker host (i.e. docker-v6-1). The route statement below says to route all traffic destined for the fd15:4ba5:5a2b:100b::/64 prefix (the one being used on docker-v6-2) to the IPv6 address of the docker-v6-2 eth0 interface.
root@docker-v6-1:~# ip -6 route add fd15:4ba5:5a2b:100b::/64 via fd15:4ba5:5a2b:1009:20c:29ff:febb:cbf8
Now, do the same on the 2nd Docker host (docker-v6-2). This route statement says to route all traffic destined for the fd15:4ba5:5a2b:100a::/64 prefix (used on docker-v6-1) to the IPv6 address of the docker-v6-1 eth0 interface:
root@docker-v6-2:~# ip -6 route add fd15:4ba5:5a2b:100a::/64 via fd15:4ba5:5a2b:1009:20c:29ff:fef3:f848
The final test is to ping from one container on docker-v6-1 to a container on docker-v6-2:
root@e8a8662fad76:/# ping6 fd15:4ba5:5a2b:100b:0:242:ac11:1 PING fd15:4ba5:5a2b:100b:0:242:ac11:1(fd15:4ba5:5a2b:100b:0:242:ac11:1) 56 data bytes 64 bytes from fd15:4ba5:5a2b:100b:0:242:ac11:1: icmp_seq=3 ttl=62 time=0.570 ms 64 bytes from fd15:4ba5:5a2b:100b:0:242:ac11:1: icmp_seq=4 ttl=62 time=0.454 ms
We will build on this scenario in upcoming posts as we walk through enabling IPv6 functionality in a variety of Docker network scenarios and other Docker services.