Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save olljanat/b96ed26583c452118313fc18e4a663c1 to your computer and use it in GitHub Desktop.
Save olljanat/b96ed26583c452118313fc18e4a663c1 to your computer and use it in GitHub Desktop.
Overlay network and static IPs for Docker containers

I don't expect moby/moby#24170 to be solved by Docker team on near future so this gist is about looking for least ugly workarounds which are available today and on near future.

What is possible on Docker 19.03

Network

It is possible to create overlay network with use user specified subnet. On this example I create network which is size is two C -class networks ( IP range 10.0.0.0 - 10.0.1.255 ) and force Docker to use IPs from second part of it ( 10.0.1.0 - 10.0.1.255 ).

That way I can make sure that IPs 10.0.0.2 - 10.0.0.254 can be specified to containers and they do not collide with IPs which Docker engine assign for service(s)/container(s).

docker network create --driver overlay --subnet 10.0.0.0/23 --ip-range 10.0.1.0/24 --gateway 10.0.0.1 --attachable test

Containers

By default containers IP addresses are controlled by Docker engine and those cannot be changed inside of container. Luckily that behavior can be overridden by adding NET_ADMIN capability for container.

Custom Docker image

So first we need create custom docker image which is able set IP for container when it starts. That we can do example with Dockerfile like this:

FROM ubuntu
RUN apt-get update \
        && apt-get install -y net-tools iputils-ping
COPY /start.sh /
ENTRYPOINT /start.sh

and start script like this:

#!/bin/bash
if [[ -f "${STATIC_IP}" ]]; then
        echo "Using default IP from Docker"
else
        echo "Found static IP: ${STATIC_IP} using it"
        ifconfig eth0 ${STATIC_IP} netmask 255.255.255.0 up
fi
sleep infinity

Then just build that with command:

docker build . -t static-ip

Starting container

Then we can start two containers with commands like these which connects then to overlay network and changes IP addresses inside of the to static ones.

docker run --name test1 --detach=true --rm --cap-add=NET_ADMIN --network=test -e STATIC_IP=10.0.0.11 static-ip
docker run --name test2 --detach=true --rm --cap-add=NET_ADMIN --network=test -e STATIC_IP=10.0.0.12 static-ip

What will be possible on next version

Above solution solves two issues:

  • It sets static IPs for containers.
  • It allow containers communicate between Docker nodes using overlay network.

However those containers are not swarm services which means that you lose some control to them. Luckily I have already implemented docker/cli#1940 and I'm expecting that it will get merged and released as part of next Docker version.

That makes possible to use swarm stack like this:

version: "3.7"
services:
  test1:
    image: static-ip
    environment:
      STATIC_IP: 10.0.0.11
    networks:
      - test
    cap_add:
      - NET_ADMIN
  test2:
    image: static-ip
    environment:
      STATIC_IP: 10.0.0.12
    networks:
      - test
    cap_add:
      - NET_ADMIN

networks:
  test:
   external: true
   name: test

and deploy it with command:

docker stack deploy -c static-ip.yml static-ip
@gm0neyl0ve
Copy link

Brilliant. What's the time frame where this becomes available. I have need of static assigned containers for swarm.

@ksl20200108
Copy link

Oh my god. It is a life saver.

@ksl20200108
Copy link

Actually I don't quite understand how to make cap_add works in docker swarm. Could you please give me some guidance?

@olljanat
Copy link
Author

olljanat commented Oct 4, 2021

It should works like it is above on as long you use Docker version 20.10.x.

@ksl20200108
Copy link

Now it works. Thank you.

@ZahraPakzad
Copy link

ZahraPakzad commented Aug 25, 2022

It does not work for me either. This link states that 'The cap_add and cap_drop options are ignored when deploying a stack in swarm mode'.
I'm using "Docker version 20.10.17" and compose file of version 3.3 (later compose file versions show no updates for eliminating this problem).

This capability is exactly what I need. I followed the instructions and yet my network generated new IPs for the containers.

@olljanat
Copy link
Author

That documentation is not up to date. Try run image ollijanatuinen/capsh with and without those settings. It should print list of capabilities to container console if I remember correctly.

@nrodday
Copy link

nrodday commented Oct 23, 2022

I was trying to use this but it does not work. Docker creates an overlay network and one is able to see the assigned IP addresses once the stack got deployed with "docker network inspect NAME". If you use the proposed method from above, the IP is changed inside the container, without updating the mapping assigned by the docker engine. Therefore, networking works fine in the overlay network as long as you remain on one node, but if you want to establish inter-node communication via the overlay network, packets are not forwarded.

I was doing the following inside my docker-compose.yml before:
command:
- /bin/sh
- -c
- |
ip addr flush dev eth0
ip addr add 172.30.0.101/15 dev eth0
cap_add:
- NET_ADMIN

It is essentially the same that you are doing via the environment variable. Therefore, also the result is the same. The IP is changed but then communication only works for containers within the node, not inbetween nodes (which is exactly what the overlay is supposed to be for).

I could still not find a working solution - how to assign a static IP to a docker swarm container in the docker-compose.yml while maintaining inter-node communication?
I am only spawning one instance per container and believe there should be such a feature. That it does not make sense for multiple replicas is obvious, but for just one instance one should be able to assign a static IP.

@olljanat
Copy link
Author

@nrodday honestly I wrote this so many years ago that I don't remember if actually tested multi node setup. We eventually found solution make applications working with dynamic IPs so there was not need for that.

However if there would have mandatory use case for this then I probably would be testing multi network or multi IP per network config depending on what is reason for static IPs.

Those who are familiar with Golang can also build ipam plugin for this purpose. There is somekind of example available on https://github.com/ishantt/docker-ipam-plugin

@nrodday
Copy link

nrodday commented Oct 25, 2022

I found a workaround and I am adding it here for reference:

docker network disconnect OVERLAY_NETWORK_NAME CONTAINER_NAME
docker network connect --ip 172.30.0.101 OVERLAY_NETWORK_NAME CONTAINER_NAME

This also updates the docker overlay network configuration such that inter-node communication is possible. This command has to be executed from within every node for the respective containers running on that node. One could build some scripts around to automate the reassignment.

From my point of view, "docker stack deploy --compose-file FILE_NAME STACK_NAME" should respect the ipv4_address attribute within the compose file for the first replica and go for additional replicas with dynamic IPs, which it currently does not.
networks:
bgp_net:
ipv4_address: 172.30.0.101

Anyway, via the workaround one is able to get a static IP into a swarm via an overlay network.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment