Peter Thomassen
Peter Thomassen

Reputation: 1880

How to assign IPv6 address with docker-compose

I am using docker 1.12.1 on Ubuntu 16.04, and docker-compose 1.8.1. I am trying to get the Compose file from https://docs.docker.com/compose/compose-file/#ipv4-address-ipv6-address to run. For reference, I created docker-compose.yml with the following content:

version: '2'

services:
  app:
    image: busybox
    command: ifconfig
    networks:
      app_net:
        ipv4_address: 172.16.238.10
        ipv6_address: 2001:3984:3989::10

networks:
  app_net:
    driver: bridge
    driver_opts:
      com.docker.network.enable_ipv6: "true"
    ipam:
      driver: default
      config:
      - subnet: 172.16.238.0/24
        gateway: 172.16.238.1
      - subnet: 2001:3984:3989::/64
        gateway: 2001:3984:3989::1

Now, running docker-compose up produces

Creating network "tmp_app_net" with driver "bridge"
Creating tmp_app_1
Attaching to tmp_app_1
app_1  | eth0      Link encap:Ethernet  HWaddr 02:42:AC:10:EE:0A  
app_1  |           inet addr:172.16.238.10  Bcast:0.0.0.0  Mask:255.255.255.0
app_1  |           inet6 addr: fe80::42:acff:fe10:ee0a/64 Scope:Link
app_1  |           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
app_1  |           RX packets:4 errors:0 dropped:0 overruns:0 frame:0
app_1  |           TX packets:1 errors:0 dropped:0 overruns:0 carrier:0
app_1  |           collisions:0 txqueuelen:0 
app_1  |           RX bytes:520 (520.0 B)  TX bytes:90 (90.0 B)
app_1  | 
app_1  | lo        Link encap:Local Loopback  
app_1  |           inet addr:127.0.0.1  Mask:255.0.0.0
app_1  |           inet6 addr: ::1/128 Scope:Host
app_1  |           UP LOOPBACK RUNNING  MTU:65536  Metric:1
app_1  |           RX packets:0 errors:0 dropped:0 overruns:0 frame:0
app_1  |           TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
app_1  |           collisions:0 txqueuelen:1 
app_1  |           RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
app_1  | 
tmp_app_1 exited with code 0

The IPv6 address is not assigned.

I already tried:

  1. Starting the docker daemon with --ipv6
  2. Starting the docker daemon with --ipv6 --fixed-cidr-v6="2001:3984:3989::/64"
    • Note that docker run -it busybox ifconfig actually gives me an IPv6 address here (from the --fixed-cidr subnet which is assigned to the default bridge network)
  3. Using my actual IPv6 subnet instead of the one from the code example, and repeating 2. with this subnet

No success. Any ideas?

Upvotes: 12

Views: 43840

Answers (3)

F1Linux
F1Linux

Reputation: 4373

Short Answer:

Add (3) directives in your docker-compose.yml file, (1) to daemon.json then re-build your containers and drop the Docker host's SLAAC addressed interface onto the docker-compose bridge and the container will itself receive an IPV6 SLAAC address.

This solution is demonstrated to be compatible with:

  • docker-compose v. 2.1.1 and
  • docker-compose file format v. 3.9.

If you don't know how to achieve the above read the longer answer below. It's actually not very difficult or time consuming.

Intro:

(Feel free to skip this intro- just context on the problems I'm trying to solve).

Like others visiting this question, I too have bumped-up against the issues of IPv6 networking in docker-compose. From what I could see certain IPV6 directives only worked with much older docker-compose file versions. And this could have knock-ons in respect to newer directives not being supported in the older docker-compose file versions I guess. And some of the solutions wouldn't work with swarm.

The only way many folks seemed to be able to get IPV6 connectivity working in their docker-compose configs was to use "IPV6 NAT" that some frustrated Docker user coded. That's not a criticism BTW: we need folks that will not be defeated who doggedly find ways around problems/limitations. But IPv6 was supposed to solve the problem of NAT'ing ;-)

I thought: There has to be an easier way!

And there was...

MY SETUP:

I'll describe my setup to enable comparative analysis if you run into difficulties:

  • MikroTik router configured for IPv6 SLAAC addressing which is connected to a dual-stack IPV4/V6 switch.

  • A Raspberry Pi 4 running 64bit Ubuntu 20.04 LTS connected to the switch via eth0 from which it gains its' IPv6 Global Unicast Address ("GUA") from the router.

  • Docker version used = Docker Engine Community v. 20.10.11

  • docker-compose = v. 2.1.1

  • docker-compose file format = v. 3.9

docker-compose IPv6 Config:

docker-compose.yml

My networks section:

networks:
  my-custom-network:
    name: my-custom-network
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 192.168.XX.0/24
          gateway: 192.168.xx.1

Note that the only IPv6-specific directive is just enable_ipv6: true

Next, the relevant part of my docker-compose.yml file containing the other (2) IPv6-specific directives in the services section for the container I want to IPV6 address is:

sysctls:
  - net.ipv6.conf.all.disable_ipv6=0
  - net.ipv6.conf.eth0.proxy_ndp=1

Obviously being YAML, take care to drop that part in with the correct leading spaces or it will puke misleading errors when the only problems is the indenting of the directives.

/etc/docker/daemon.json

I added just (1) IPv6 specific directive here:

  "ipv6": true, "fixed-cidr-v6": "2001:470:1d3f:8::/64",

This step is necessary to assign IPv6 globally routable IPv6 addresses to our containers.

Finally, cut a fresh set of containers (but don't start them yet) after you're done tweaking:

docker-compose build --no-cache

Docker Host Network Config:

  1. Validate the IPv6 config of the router is righteous before doing the IPv6 Docker config by pinging something with ping6 from your Docker host:

    ping6 ipv6.google.com

If response is returned successfully, it establishes that both the Docker host & router can resolve and route IPv6 packets. The problem is now reduced to just getting the container IPV6 addressed.

  1. Since the Pi's eth0 interface is itself IPv6 addressed and talking IPv6 happily to our router, we'll join this to the docker-compose bridge:
  • Locate the docker-compose bridge name "br-series-of-random-chars":

    ip addr list

  • Add eth0 to bridge:

    ip link set dev eth0 master br-ckkde30ff0g

NOTE: The above command is NON-Persistent- it won't survive reboot. The process of adding eth0 could be completely automated..

  • Check eth0 is now joined:

    ip link | grep "master br-ckkde30ff0g"

  1. Start your containers:

    docker-compose start

  2. Verify the container now as an IPv6 GUA:

    docker exec -it containerName bash and then:

    hostname -I

You should see a SLAAC configured IPv6 GUA address now.

  1. Poke a hole in your IPv6 firewall to tailor the access as required

SECURITY:

What you- thankfully- WON'T see: Any other containers receiving IPv6 GUA addresses.

Only the containers with those above sysctls directives included in their configuration will receive an SLAAC IPv6 address- no others. In my case, I only wanted to expose the proxy container in front of a WordPress site, not the other containers, a webserver & backing database. So ONLY the proxy is reachable via IPv6.

Validation of Config:

The below site is a 100% docker-compose contraption configured using the above process, so the procedures outlined here can be demonstrated to work correctly:

http://[2001:470:1d3f:8:42:c0ff:fea8:1504]/

PLEASE NOTE: I'm changing ISP's next week, so might be some down-time while the new link is configured.

You can also validate the IPV6 config for a container's IPv6 addressing using an IPv6 Testing site:

https://ipv6-test.com/validate.php

  • If the AAAA DNS record is set for the bare domain- domain.com- then enter this as the url in the checker.

  • If the AAAA record is set for the subdomain www then enter www.domain.com into the checker.

Conclusion:

That's it: just (3) IPv6-specific directives in docker-compose, (1) in daemon.json, some bridging and a properly IPv6 configured router. Done. And no NATing required.

Again, not a perfect, nor ideal solution, but it's another way of getting round the IPv6 connectivity issues. if I've missed a step out somewhere please ping me a comment.

Upvotes: 5

mohan08p
mohan08p

Reputation: 5362

Yes. Docker compose supports IPv6 protocol and it was introduced into docker engine 1.5 onward. There is still issue with latest compose file format 3.3 so you could use the 2.1 format. Still docker swarm is not mature enough with advance networking configuration and it doesn't support IPv6, hence it is not included under 3.3 fil format. Here is sample example of File,

docker-compose.yml

version: ‘2.1’
services:
  app:
    image: busybox
    command: ping www.google.com
    networks:
      app_net:
        ipv6_address: 2001:3200:3200::20
networks:
  app_net:
    enable_ipv6: true
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 2001:3200:3200::/64
          gateway: 2001:3200:3200::1

This docker compose file will create a new network called testping_app_net based on IPv6 network under the subnet 2001:3200:3200::/64 and container should get IPv6 address automatically assigned.

Let us bring up services using docker-compose up and see if the services communicates over IPv6 protocol:

docker-compose up -d

and you could verify the IPv6 address for each container using,

docker exec -it 905 ip addr

You will see that a new container gets IPv6 address – 2001:3200:3200::20 and hence they are able to communicate with each other.

Note : If you want to enable IPv6 on host machine, which by default uses IPv4 address you need to add these two line into the daemon.json under /etc/docker directory:

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

and restart the docker daemon by command:

$ sudo systemctl restart docker

Upvotes: 6

Peter Thomassen
Peter Thomassen

Reputation: 1880

It turns out this is indeed a docker-compose bug that is going to be fixed in 1.9.0.

Meanwhile, there is a workaround by creating a custom network with the docker network command:

docker network create --subnet=172.16.2.0/24 --gateway=172.16.2.1 --ipv6 --subnet=<myV6Network/subnet> dockerbridge

... which can then be made available inside docker-composed.yml by writing

networks:
  dockerbridge:
    external:
      name: dockerbridge

Upvotes: 8

Related Questions