Docker expose service

by Sagi Zeevi ·
Published November 19, 2017
· Updated January 31, 2018

Docker is a great tool for DevOps, and here we’re going to make it even better! We’re going to use a simple but brilliant solution to automatically expose selected services (containers) whenever they are started. No complicated machine allocation is needed – just docker/docker-compose and a simple one time system change for your Linux.

Docker expose service

We all know we can expose docker ports, so why not services? I’ve been using docker for a while now, and have become used to pulling container IPs to connect to the services in them. Yes – you can export ports and connect to your local host at a specific port, but I find this somewhat awkward, and it doesn’t always work for what I need. Looking around for a solution, I didn’t find anything quite satisfying. The only thing which came close was this post, and I’m basing my solution on it.

The requirements

Expose by name which represents an IP

This goes almost without saying, since this is the main issue to solve here. It is expected to work much like a dynamic DNS service, in which the IP changes, but the name remains the same.

Selection mechanism

I want to choose which container to expose, and not in its build, since the same image could be exposed several times as services with different names.

Mapping mechanism

The container ‘hostname’ or ‘service’ names are not necessarily the names I want to use externally.

Domain and subdomain support

The docker-compose.yml can set up a small network domain with subdomain hosts, which need external access. Actually sometimes multiple subdomains can connect us to the same host, something which is common in website development.

Docker encapsulation

The selective configuration should be part of the docker-compose YAML file.

Automation

For each container started or stopped we want automatic detection if it needs to be exposed externally.

This service should be a registered system service and started at boot time.

The implementation

The implementation is based on dnsmasq, docker events, the dockerLABEL feature and a small bash script. The script which will be presented shortly identifies these labels:

com.theimpossiblecode.expose.host “name”

Here you are exposing the IP with the name.

If you have a domainname, then it will be appended to the hostname.

com.theimpossiblecode.expose.domain “domain.suffix”

If you want to reach the service with a domain name.

This will override your own external domainname.

You can reach the exposed host from above (if provided) with this domain suffix as well.

com.theimpossiblecode.expose.subdomainHosts “name [name…]”

These names will be appended the domain of your external host or the exposed domain name from above (overrides your own external domain).

Some or all of the above will cause the script to create a ‘hosts‘ entry for the dnsmasq, in a file which it is configured to use. After each such change the dnsmasq will be signaled to reload its config.

To install dnsmasq

sudo apt-get install dnsmasq

Configure dnsmasq for docker

Put this in /etc/dnsmasq.d/docker-dns:

addn-hosts=/etc/docker-container-hostsinterface=docker0

Restart dnsmasq:

sudo systemctl restart dnsmasq.service

The bash script

Note that this script is a compromise between fast-simple coding/hacking and desired functionality. It could be made better over time .

UPDATE:
This script is not on github and I’m enhancing it as I need.
Here is a list of changes from the original post:
1. Bug fixes.
2. Support swarm and compose service scaling by allowing names with com.theimpossiblecode.expose.useDockerName.
3. Support containers in internal docker networks, with IPs not showing in their docker inspect.
4. Support containers in multiple networks with several IPs.
5. Consider to some extent a docker with many containers and fast restarting ones. Delay the dnsmasq update a little to not disturb the dockerd too much.

2 Responses

I have studied your Post, this is a good idea to improve docker container handling. But a question inside the bash script i have:

You “handle race conditions with dnsmasq service, if its starts before docker service”. The function ‘generate_docker_hosts’, subsequently called after these lines, restarts the dnsmasq service with every call. Thus, in this case, you restart the dnsmask service twice. Is this intended?

You’re right this is a puzzling flow, but SIGHUP will only ask dnsmasq to reload its configuration files.
The problem is that if dnsmasq is started before the docker0 network is ready, then it will not read its configuration – not at startup and not at SIGHUP.
So we wait for docker networks to come up, and then restart dnsmasq.