Accessing Docker containers with Traefik

Simple, secure and scalable

  • 4th March 2019

I've recently been playing with Docker, getting to grips with what it can do, and increasingly trying to adapt all my side projects to make full use of it. I have a Docker swarm running on my increasingly growing collection of Raspberry Pi's, and each of my apps managed through a docker-compose.yml file.

One of the initial issues I had, was that I had to access each of these apps on different ports. Since this was running within my home network, I also had to open the port on my router and forward the traffic to the Pi's IP on my network. This didn't feel very secure, and it also meant I couldn't add an SSL certificate.

Then I stumbled across Traefik. At its core, Traefik is just a reverse HTTP proxy and load balancer, but what makes it really powerful is that it can integrate with other infrastructure components, such as Kubernetes, Amazon ECS, and in my case, Docker (including Swarm mode).

Traefik Diagram

Traefik can automatically watch your Docker processes, when it finds one with the necessary labels, it'll automatically setup the routing for it, and can provision an SSL certificate for the domain, or use a wildcard certificate if it's been generated already. If you're running if swarm mode, it can load balance all the traffic between the nodes in a variety of methods, and automatically take a node out of the pool if it becomes unhealthy.

Traefik has made my setup much more secure, now I can set up a wildcard DNS record to point all subdomains to my machine, and I just need the 2 ports open on my router, 80 and 443, with a letsencrypt certificate securing it.


Setup

The config for Traefik is relatively simple, a basic docker-compose.yml file like the following is enough to get it up and running:

version: '3'

services:
  traefik:
    image: traefik:latest
    restart: always
    environment:
      - DO_AUTH_TOKEN=secret_token
    ports:
      - 80:80
      - 443:443
    networks:
      - home_proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik.toml:/traefik.toml
      - ${PWD}/acme.json:/acme.json
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.hostname == HomeServer1
      labels:
        traefik.backend: "traefik"
        traefik.enable: "true"
        traefik.frontend.rule: "Host:proxy.myhomedomain.com"
        traefik.port: "9002"
        traefik.docker.network: "home_proxy"

# This bit is important, all Docker containers must be on the same network to be accessible to Traefik.
networks:
  home_proxy:
    external: true

and the following traefik.toml file manages the configuration:

logLevel = "INFO"
defaultEntryPoints = ["https","http"] # Accept traffic over both protocols

[file]
[entryPoints]
  [entryPoints.http]
  address = ":80" # Listen on 80 for HTTP traffic
    [entryPoints.http.redirect]
    entryPoint = "https"
  [entryPoints.https]
  address = ":443" # Listen on 443 for HTTPS traffic
  [entryPoints.https.tls]
  [entryPoints.traefik]
  address = ":9002" # Listen on 9002 for the Traefik dashboard UI

[retry]

[docker]
endpoint = "unix:///var/run/docker.sock" # Use this socket to monitor the docker processes
domain = "myhomedomain.com" # The domain this swarm is running on
watch = true # Watch for changes
swarmMode = true # Use Docker Swarm
exposedByDefault = false # Don't add all processes by default, only those with the enable label

[acme]
email = "myemail@address.com" # Email address to send letsencrypt notifications to
storage = "acme.json" # Store the certificates in this file
entryPoint = "https"
onHostRule = true
[acme.dnsChallenge]
  provider = "digitalocean" # Use digitalocean as the DNS provider
[[acme.domains]]
  main = "*.myhomedomain.com" # Generate a wildcard certificate for this domain

[api]
entryPoint = "traefik"

To enable a docker process for use in Traefik, you just need to add the following labels to your compose file:

version: '3'

services:
 minio:
  image: minio/minio:RELEASE.2019-02-20T22-44-29Z
  restart: always
  labels:
    - "traefik.enable=true" # Enable in Traefik
    - "traefik.frontend.rule=Host:minio.myhomedomain.com" # Route traffic from this hostname
    - "traefik.port=9000" # To this container port
  networks:
    - home_proxy

networks:
  home_proxy:
    external: true

Bonus

Here's a quick list of some of the things I have running on my swarm network:

  • Dozzle - Real time docker logs via the web of all the containers running in my swarm
  • Docker Swarm Visualizer - Visualise all of the containers on each node in the swarm
  • Minio - An S3 compatible API, used as a destination for backing up my data with Arq.