Docker, IPv6 and –net=”host”

As you recall from the last few blog posts, I went through basic IPv6 deployment for Docker Engine, Docker HubDocker Registry and Docker Swarm.  All of those configurations were using default Docker networking using the Docker-provided bridge layout.

Over the past few months, I have met with several customers who don’t use the Docker bridge setup at all. They use the Docker run option of –net=”host” to do what they call “native” networking.  Simply put, this flag has containers run using the networking configuration of the underlying Linux host.  The advantage of this is that it is brain-dead simple to understand, troubleshoot and use.  The one drawback to this setup is that you can very easily have port conflicts.  Meaning that if I run a container on port 80 which is listening natively on the Linux host and I run another container that needs that same port number then there is a conflict because only one listener can be active for that port at a time.

All of the customers I have met with have no need to run  containers on the same host that use the exact same port so this is a magical option for them.

IPv6 works just as it should in this networking scenario.  Let’s take a look at an example setup.

In the ‘ip a’ output below, you can see that ‘docker-v6-1’ has IPv6 addressing on the eth0 interface (See the Docker Engine post on enabling IPv6):

root@docker-v6-1:~# 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
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:e91e:221:a4a0:2223/64 scope global temporary dynamic
       valid_lft 83957sec preferred_lft 11957sec
    inet6 fd15:4ba5:5a2b:1009:20c:29ff:fef3:f848/64 scope global dynamic
       valid_lft 83957sec preferred_lft 11957sec
    inet6 fe80::20c:29ff:fef3:f848/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 00:0c:29:f3:f8:52 brd ff:ff:ff:ff:ff:ff
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:93:33:cc:66 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fd15:4ba5:5a2b:100a::1/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::42:93ff:fe33:cc66/64 scope link
       valid_lft forever preferred_lft forever
    inet6 fe80::1/64 scope link
       valid_lft forever preferred_lft forever

As a test, I will run a NGINX container using the –net=”host” option. Before I run the container, I disable the “ipv6only” functionality in the NGINX default.conf file so that I have dual stack support.

Create/edit a NGINX default.conf file with the following setting changed:

listen       [::]:80 ipv6only=off;

Now, run the container with the –net-“host” option set and bind mount a volume to where that default.conf file is located on the Docker host:

root@docker-v6-1:~# docker run -itd --net="host" -v ~/default.conf:/etc/nginx/conf.d/default.conf nginx

Using the –net=”host”, the new container will use the same IPv4 and IPv6 address of the host and listen on port 80 (the default in the NGINX setup):

root@docker-v6-1:~# netstat -nlp | grep nginx
tcp6       0      0 :::80                   :::*                    LISTEN      2554/nginx: master

Test accessing the NGINX default page over IPv4 using the 192.168.80.200 address (reference eth0 above):

root@docker-v6-1:~# wget -O - http://192.168.80.200
--2016-04-21 11:00:50--  http://192.168.80.200/
Connecting to 192.168.80.200:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 612 
Saving to: ‘STDOUT’

 0% [                                                                                                 ] 0           --.-K/s              

Welcome to nginx!

.....[output truncated for clarity]

Test accessing the NGINX default page over IPv6 using the fd15:4ba5:5a2b:1009:20c:29ff:fef3:f848 address (Reference eth0 above)”

root@docker-v6-1:~# wget -O - http://[fd15:4ba5:5a2b:1009:20c:29ff:fef3:f848]
--2016-04-21 11:01:13--  http://[fd15:4ba5:5a2b:1009:20c:29ff:fef3:f848]/
Connecting to [fd15:4ba5:5a2b:1009:20c:29ff:fef3:f848]:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 612 
Saving to: ‘STDOUT’

 0% [                                                                                                 ] 0           --.-K/s              

Welcome to nginx!

.....[output truncated for clarity]

It works!

Remember that this is cool and all but watch out for port conflicts between containers.  Shown below is an example of what you will see if you run two containers on the same host with –net=”host” set and both use the same port.  You will see one or more of the containers exit and likely pop-up a message in the log that looks like this:

root@docker-v6-1:~# docker logs b47aa56cc822
2016/06/17 17:49:34 [emerg] 1#1: bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)

One thought on “Docker, IPv6 and –net=”host””

Leave a Reply

Your email address will not be published. Required fields are marked *