In Docker you expose functionality by specifying a port mapping and then you can address the application on the physical host with the mapped port.

I am using Docker quite some time now and this was getting a little bit too tedious because each mapped port needs to be unique and then you need to remember the port numbers to be able to start your application. So I wanted to simpler way by using speaking host names. E.g. http://svnclient.docker.local should display the svnclient and this should work from all my clients in my local network.

It took me a full day to put all the pieces together into a working solution. Therefore I am writing this down in the hope that it might prevent someone else to fall into the same traps. Here is the complete solution:

Treafik Overview

Traefik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy. It provides some predefined “providers” and Docker is one of them.

1. Network

Treafik automatically sets up the routing from frontends to backends. Therefore is is necessary that the Treafik container and the Application containers are on the same Docker Network so that they can communicate with each other!.

Therefore I created my docker network first with

    docker network create web

2. Treafik Coniguration File

I am using the following traefik.toml configuration file:

################################################################
# Global configuration
################################################################

# Enable debug mode
#
# Optional
# Default: false
#
# debug = true

# Log level
#
# Optional
# Default: "ERROR"
#
# logLevel = "DEBUG"

# Entrypoints to be used by frontends that do not specify any entrypoint.
# Each frontend can specify its own entrypoints.
#
# Optional
# Default: ["http"]
#
# defaultEntryPoints = ["http", "https"]

################################################################
# Entrypoints configuration
################################################################

# Entrypoints definition
#
# Optional
# Default:
[entryPoints]
    [entryPoints.http]
    address = ":80"

################################################################
# Traefik logs configuration
################################################################

# Traefik logs
# Enabled by default and log to stdout
#
# Optional
#
# [traefikLog]

# Sets the filepath for the traefik log. If not specified, stdout will be used.
# Intermediate directories are created if necessary.
#
# Optional
# Default: os.Stdout
#
# filePath = "log/traefik.log"

# Format is either "json" or "common".
#
# Optional
# Default: "common"
#
# format = "common"

################################################################
# Access logs configuration
################################################################

# Enable access logs
# By default it will write to stdout and produce logs in the textual
# Common Log Format (CLF), extended with additional fields.
#
# Optional
#
# [accessLog]

# Sets the file path for the access log. If not specified, stdout will be used.
# Intermediate directories are created if necessary.
#
# Optional
# Default: os.Stdout
#
# filePath = "/path/to/log/log.txt"

# Format is either "json" or "common".
#
# Optional
# Default: "common"
#
# format = "common"

################################################################
# API and dashboard configuration
################################################################

# Enable API and dashboard
[api]

  # Name of the related entry point
  #
  # Optional
  # Default: "traefik"
  #
  # entryPoint = "traefik"

  # Enabled Dashboard
  #
  # Optional
  # Default: true
  #
  # dashboard = false

################################################################
# Ping configuration
################################################################

# Enable ping
[ping]

  # Name of the related entry point
  #
  # Optional
  # Default: "traefik"
  #
  # entryPoint = "traefik"

################################################################
# Docker Provider
################################################################

# Enable Docker Provider.
[docker]

# Docker server endpoint. Can be a tcp or a unix socket endpoint.
#
# Required
#
endpoint = "unix:///var/run/docker.sock"

# Default base domain used for the frontend rules.
# Can be overridden by setting the "traefik.domain" label on a container.
#
# Optional
#
domain = "docker.local"

# Enable watch docker changes.
#
# Optional
#
watch = true

# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "docker.tmpl"

# Override template version
# For advanced users :)
#
# Optional
# - "1": previous template version (must be used only with older custom templates, see "filename")
# - "2": current template version (must be used to force template version when "filename" is used)
#
# templateVersion = 2

# Expose containers by default in Traefik.
# If set to false, containers that don't have `traefik.enable=true` will be ignored.
#
# Optional
# Default: true
#
exposedByDefault = false

# Use the IP address from the binded port instead of the inner network one.
#
# In case no IP address is attached to the binded port (or in case 
# there is no bind), the inner network one will be used as a fallback.     
#
# Optional
# Default: false
#
usebindportip = true

# Use Swarm Mode services as data provider.
#
# Optional
# Default: false
#
swarmMode = false

# Polling interval (in seconds) for Swarm Mode.
#
# Optional
# Default: 15
#
swarmModeRefreshSeconds = 15

# Define a default docker network to use for connections to all containers.
# Can be overridden by the traefik.docker.network label.
#
# Optional
#
network = "web"

# Enable docker TLS connection.
#
# Optional
#
#  [docker.tls]
#  ca = "/etc/ssl/ca.crt"
#  cert = "/etc/ssl/docker.crt"
#  key = "/etc/ssl/docker.key"
#  insecureSkipVerify = true

I would like to highlight the following settings:

  • domain = “docker.local”: This defines the suffix for the host name
  • exposedByDefault = false: I think it is good practise that I can decide which applications to expose
  • network = “web”: This is the network name that I created in step 1. If you are not happy with this name you can change it here.

3. Docker Compose for Treafik

I am using docker compose to start my containers. So here is my docker-compose.yml:

    version: '3.4'

    services:
        traefik:
          image: traefik:alpine
          container_name: traefik
          ports:
            - "80:80"
            - "8080:8080"
          volumes:
            - /var/run/docker.sock:/var/run/docker.sock
            - /srv/traefik/traefik.toml:/traefik.toml
          labels:
            - traefik.enable=true
            - traefik.port=8080
            - traefik.frontend.rule=Host:traefik.docker.local
          restart: always
          networks:
          - web
          - sparknet

    networks:
      web:
        external:
          name: web
      sparknet:
        external:
          name: sparknet

I started with a default network (as explained in the next chapter) but I changed the network setup to explicitly enumerate the networks that should be used to communicate with the applications.
I will explain the labels in the next chapter because I just treat Treafik like any other Application

4. Docker Compose for the Subversion Container

Here is the docker-compose.yml for my demo application. I am using a dockerized svnserve and websvn as web application to access the content of Subversion:

    version: '3.4'
    services:
        svnserve:
            image: pschatzmann/svnserve
            container_name: svnserve
            ports:
                - "3690:3690"
            environment:
                - SVN_REPONAME=my-repo
            volumes:
                - /srv/svn:/srv/svn
            restart: always

        websvn:
            image: pschatzmann/websvn
            container_name: websvn
            ports:
                - "8003:80"
            environment:
                - repository=svn://my-secret
                - user=me
                - password=i-dont-tell
            labels:
                - traefik.enable=true
                - traefik.frontend.rule=Host:websvn.docker.local
            restart: always

    networks:
      default:
        external:
          name: web

The key settings for the support of Traefik are the following:

  • traefik.enable=true: to Activate the support of Traefik for the web Gui
  • The Default network which is making sure that Treafik can reach this application
  • traefik.frontend.rule=Host:websvn.docker.local (Optional) I did not like the automatically generated address so I just replaced it with my shorter more speaking one
  • You could enter a label to specifiy the port: traefik.port=80. But this is only necessary if treafik can not pick the right port automatically.

We are ready to start both applications with

    docker-compose up -d

Both application should now have an entry in the list of frontend and backend in the Traefik Dashboard which is available with http://your_host:8080.

5. Name Resolution

So with the current setup the usual tutorials stop and demo that the solution is working by using curl in the following way:

    curl -H Host:websvn.docker.local http://docker.local

But I wanted that the websvn.docker.local address is working in my Browser which actually does not work (yet).
So far I have identified the following solution approaches:

  • Use a Proxy in your Browser: e.g. websvn.docker.local SwitchyOmega: This solution approach depends on your Web Browser and need to be set up on each client separately
  • Use DNSMASQ: There are quite a few tutorials available (which are dependent on your Operating System) and usually this is easy to setup – unless you are on a recent version of Ubuntu. Unfortunately I was using Ubuntu and I gave this one up because it was getting too messy.
  • Use a DNS Server and setup a wildcard host address.

To setup a local Bind Server was always a low priority item on my todo list. So I went for this solution which I will describe next. Fortunately webmin has some easy to use Web GUI which made the process quite painless:
1. set other DNS to point to my Zystel router which provides DHCP
2. create a new master zone: local
3. create a new address: docker.local with ip address 10.147.17.177
4. create a new address: nuc.local with ip address 10.147.17.177 (which is the physical server)
5. add reverse address: ip address to docker.local to 10.147.17.177
6. create name alias: *.docker.local with docker.local
7. Start of restart Bind DNS Server
8. Change Network Settings in my local McBook to use 10.147.17.177 as DNS.

This was generating the following entries in /var/lib/bind/local.hosts

    $ttl 38400
    local.  IN  SOA nuc.local. phil\.schatzmann.gmail.com. (
                1547743979
                10800
                3600
                604800
                38400 )
    local.  IN  NS  nuc.local.
    docker.local.   IN  A   10.147.17.177
    nuc.local.  IN  A   10.147.17.177
    10.147.17.177.local.    IN  PTR docker
    *.docker.local. IN  CNAME   docker.local.

So finally I can test if the resolution is working correctly by

    ping websvn.docker.local
    PING docker.local (10.147.17.177): 56 data bytes
    64 bytes from 10.147.17.177: icmp_seq=0 ttl=64 time=8.144 ms
    64 bytes from 10.147.17.177: icmp_seq=1 ttl=64 time=6.181 ms
    64 bytes from 10.147.17.177: icmp_seq=2 ttl=64 time=1.893 ms

And finally when I enter http://websvn.docker.local in my browser I get the expected result:


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *