Linux – How to create docker ingress network with ipv6 support

dockerlinuxlinux-networkingnetworking

I am trying to figure out issue with my docker network setup, (docker containers give out blank ipv6 address)

I am not able to reach the service over ipv6 on localhost, I have to use curl -4 http://localhost:8080 instead of curl http://localhost:8080

While investigation I found out that the docker ingress network does not have ipv6 enabled, so I created removed older one and created new ingress network with ipv6 address

sunils@sunils-pc ~ $ docker network inspect ingress
[
    {
        "Name": "ingress",
        "Id": "8sn7034q646ayadix9nmsmv50",
        "Created": "2018-09-29T04:42:10.857389865Z",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": true,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.11.0.0/24",
                    "Gateway": "172.11.0.1"
                },
                {
                    "Subnet": "2002:ac0b:0000::/48",
                    "Gateway": "2002:ac0b::1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": true,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": null,
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4106,4107"
        },
        "Labels": null
    }
]

Also created new overlay network ipv6_overlay that I would be using from my containers,

sunils@sunils-pc ~ $ docker network inspect ipv6_overlay
[
    {
        "Name": "ipv6_overlay",
        "Id": "n7fv85sqhm0wd1ekpo8evnit2",
        "Created": "2018-09-29T06:47:17.363996665Z",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": true,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.10.0.0/24",
                    "Gateway": "172.10.0.1"
                },
                {
                    "Subnet": "2002:ac0a:0000::/48",
                    "Gateway": "2002:ac0a::1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": null,
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4113,4114"
        },
        "Labels": null
    }
]

I configured my container to use the overlay network ipv6_overlay with predefined ip addresses,

sunils@sunils-pc /mnt/share/sunils/repos/github/ec2-sample-docker $ cat docker-compose.yml 
version: '3.2'

services:
    sessions:
        image: redis:4
        ports: 
            - 6379:6379
        networks:
           web:
               ipv4_address: 172.10.0.10
               ipv6_address: 2002:ac0a:0000::10
               aliases:
                   - redis
    cowsay-service:
        image: spsarolkar/cowsay 
        ports:
                - 8000
        environment:
            - SERVICE_PORTS=8000
        deploy:
            replicas: 5
            restart_policy:
                condition: on-failure
                max_attempts: 3
                window: 120s
        networks:
                web:
                    ipv4_address: 172.10.0.9
                    ipv6_address: 2002:ac0a:0000::9


    cowsay-proxy:
        image: dockercloud/haproxy
        depends_on:
            - cowsay-service
        environment: 
            - BALANCE=leastconn
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock
        ports:
                - "8000:80"
        networks:
            web:
               ipv4_address: 172.10.0.8
               ipv6_address: 2002:ac0a:0000::8
               aliases:
                    - cowsay

    cowsay-ui:
        image: spsarolkar/cowsay-ui
        ports:
                - "[::1]:8080:8080"
        depends_on:
            - redis 
            - cowsay
        networks:
            web:
               ipv4_address: 172.10.0.7
               ipv6_address: 2002:ac0a:0000::7

networks:
        web:
                external:
                        name: ipv6_overlay

But when I start my swarm services I get error in /var/log/docker.log as,

time="2018-09-29T12:09:19.693307864+05:30" level=error msg="fatal task error" error="Invalid address 2002:ac0b::2: It does not belong to any of this network's subnets" module=node/agent/taskmanager node.id=luqw5to6dike43h88h25xj7tg service.id=tfttw36jqmsq3ew6wzn61gyku task.id=7jo89apxj585pdtacmr2d7jpe

I am not sure even when I specified my own overlay network in the docker compose its falling back to ingress network. I get same error when I do not specify any ip addresses.

Can someone please help me with this

Best Answer

I have exactly the same situation, and so far I have not come up with an actual solution, but rather a simple workaround. I tried creating different types of ingress networks, modifying the docker_gwbridge and whatnot, but IPv6 support is just bad. Even in this day and age..

So the workaround I came up with is socat. I created a small systemd service that starts after docker.service so it ought to be fairly resilient. Let's say your actual service wants to listen on port 8080, so instead make it listen on 8081 and socat IPv6 connections from 8080 to 8081.

socat -ly TCP6-LISTEN:8080,fork,reuseaddr,bind=<my_ipv6_addr> TCP:0.0.0.0:8081

The bind there is optional, but handy if you want to limit access. The systemd unit is as follows (/etc/systemd/system/swarm-socat@.service):

[Unit]
Description=socat for docker swarm (%i)
After=docker.service

[Service]
Type=simple
EnvironmentFile=/etc/socat-%i.conf
ExecStart=/usr/bin/socat -ly TCP6-LISTEN:${SOURCEPORT},fork,reuseaddr TCP:0.0.0.0:${TARGETPORT}
Restart=on-failure

[Install]
WantedBy=multi-user.target

and the env file just contains two lines which give it the ports, for example (/etc/socat-viz.conf):

SOURCEPORT=8080
TARGETPORT=8081

The service I'm running in the example is the Docker Swarm visualizer.

docker service create \
  --name=viz \
  --publish published=8081,target=8080 \
  --constraint=node.role==manager \
  --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
  dockersamples/visualizer

Then simply enable and start the systemd service and you're good to go.

sudo systemctl enable swarm-socat@viz.service
sudo systemctl start swarm-socat@viz.service