If you’ve ever tried to assign a routable and externally visible IP address to your Docker containers, you probably know that it’s not a trivial task. There are a few posts out there that show a few interesting methods, like this or this or even from the creators of Docker itself.
I’ve been digging for a better method, that will still allow you to use Docker API and don’t execute any host-side commands after starting every container. Fortunately, it turns out that Docker doesn’t try to recreate a network bridge it uses to connect containers together, if the bridge already exists. So you may use it like this:
Create a new bridge:
brctl addbr mybridge ip link set mybridge up
Look at the settings of your primary adapter:
ifconfig eth0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.211.55.49 netmask 255.255.255.0 broadcast 10.211.55.255
See the default route:
ip route default via 10.211.55.1 dev eth0 proto static metric 100
Add eth0 to the bridge:
brctl addif mybridge eth0
Configure the bridge:
ip addr del 10.211.55.49/24 dev eth0 ip addr add 10.211.55.49/24 dev mybridge
Set up default route on the bridge:
ip route del default ip route add default via 10.211.55.1 dev mybridge
Create a new network (replace with the networks relevant to you):
docker network create --driver=bridge --subnet 10.211.55.0/24 --gateway 10.211.55.49 --ip-range 10.211.55.128/25 -o "com.docker.network.bridge.name"="mybridge" mybridge
--subnetshould be the same as the network you want to expose containers to (e.g. eth0)
--gatewayspecifies the IP address that will be set on the bridge and through which containers will route traffic. This is not the external gateway (10.211.55.1). Unfortunately there is no separate setting for the bridge’s IP.
--ip-rangeis important to specify so that docker won’t automatically allocate addresses that you know will intersect with other hosts on the network, as it assumes total control of the subnet otherwise.
- and the
com.docker.network.bridge.nameoption tells docker to not generate a bridge name, and use the provided one.
Then you may start creating containers as usual:
docker run --rm -t -i --net mybridge alpine:3.4 /bin/sh ifconfig eth0 eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02 inet addr:10.211.55.128 Bcast:0.0.0.0 Mask:255.255.0.0 ...
and then from outside of your host:
ping 10.211.55.128 PING 10.211.55.128 (10.211.55.128): 56 data bytes 64 bytes from 10.211.55.128: icmp_seq=0 ttl=64 time=0.393 ms 64 bytes from 10.211.55.128: icmp_seq=1 ttl=64 time=0.259 ms 64 bytes from 10.211.55.128: icmp_seq=2 ttl=64 time=0.352 ms
That works out of the box, with standard Docker API. You only need
to create a bridge in advance and set everything up carefully. You
may even prefer to use external IPAM system to allocate IPs, and set
them manually via