Docker expose service

devops docker - theimpossiblecode.com

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 docker LABEL 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).
  • com.theimpossiblecode.expose.domainIsHost “true”
    • If set to true a host entry will be added with the above domain.suffix.
  • com.theimpossiblecode.expose.useDockerName “true”
    • If set to true the hostname will be the docker host name.

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

This is now on github – see how to install it here.

Test it

Use this docker-compose.yml based on https://docs.docker.com/compose/wordpress/:

version: '3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     # We'll want to use a name to access this service, regardless of its IP
     labels:
       com.theimpossiblecode.expose.host: "dockerwp"
     depends_on:
       - db
     image: wordpress:latest
     # No need to map the ports anymore
     # ports:
     #  - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
volumes:
    db_data:

start it with:

 docker-compose up -d

Check the DNS lookup with dig:

dig dockerwp
; <<>> DiG 9.10.3-P4-Ubuntu <<>> dockerwp
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42941
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1280
;; QUESTION SECTION:
;dockerwp.			IN	A

;; ANSWER SECTION:
dockerwp.		0	IN	A	172.18.0.2

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sun Nov 19 15:59:37 UTC 2017
;; MSG SIZE  rcvd: 53

You can also open your browser now at http://dockerwp

Now bring down this setup with:

docker-compose down

And try again the DNS lookup to make sure nothing is found:

dig dockerwp
; <<>> DiG 9.10.3-P4-Ubuntu <<>> dockerwp
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 26781
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1280
;; QUESTION SECTION:
;dockerwp.			IN	A

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sun Nov 19 16:04:58 UTC 2017
;; MSG SIZE  rcvd: 37

Summary

  • This is a simple way to both selectively and automatically add, by name, small docker services to your system for whatever needs you have. This adds a very powerful tool to your DevOps  toolbox.
  • I’m interested to know if you found an easier way to cope with these requirements or if you find a problem with this setup. Please share.

See you next time,
Sagi.

Sagi Zeevi

20 years writing code and still loving it!

You may also like...

2 Responses

  1. Hi Sagi,

    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?

    Thank you for your replay –
    D. Schreier, Germany

Leave a Reply

Scroll Up