Exposing docker containers to external network

Categories Uncategorized

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

Where:

  • –subnet should be the same as the network you want to expose containers to (e.g. eth0)
  • –gateway specifies 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-range is 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.name’ option 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 –ip.