This page looks best with JavaScript enabled

Introduction to Docker and Traefik!

 ·  ☕ 6 min read  ·  🤖 Javy de Koning

Why Docker and Traefik

Recently I decided to redeploy my home-server. It was still running Ubuntu 12.04 and the configuration drifted ever since it was setup. Time for a revamp. I opted to use Docker and Traefik because I wanted a setup that was easy to update and easy to maintain.

All the applications I use are available on Docker Hub. Keeping all configuration in a single GIT repo using docker-compose makes it easy to maintain.

I also decided to introduce a reverse proxy, this allows all my applications to be available by entering a domain name (no more remembering ports!). Docker and Traefik combined make that easy. On top of that it’s also very easy to add SSL due to Traefik’s the Let’s Encrypt integration.

In this blog post we will be building this using Docker and Traefik:

flowchart LR
    isp(("router")) --80, 443 to traefik--> traefik
    subgraph host["Linux docker host"]
      traefik["proxy - traefik"]
      c1[Container_a]
      c2[Container_b]
      c3[container_x]
    end

    traefik --app_a.your.domain--> c1
    traefik --app_b.your.domain--> c2
    traefik --app_x.your.domain--> c3
    traefik --redirect http to https --> isp

What distribution to choose

For my home-server I opted to use Alpine Linux because it’s lightweight and an excellent docker host. For this blog post I’ll be using Amazon Linux so it’s easy for you to follow along. However, you can use any other distro that you are happy with.

Docker host and firewall rules

We will start by spinning up a new host OS, next we will setup firewall rules. Depending on what you’re using instructions might slightly differ. The instructions below are for AWS.

Start a new host-os, I’m using Amazon Linux:

We should open the following ports in the firewall:

  • 22 (to SSH into the machine, you should trim this down to your own IP).
  • 80 (for HTTP traffic, required for requesting the SSL Certificate from Let’s Encrypt.
  • 443 (for HTTPS)

Creating the DNS record set

We want our Docker apps to be available over the internet and we are going to setup HTTPS. Hence, we need to own a DNS domain. In order for Traefik to request certificates for the domain, we need to set up DNS first. We can either create A-Records, or use a CNAME. I will use the following two CNAME records.

  • *.dockerdemo.yourdomain This will be used by containers (e.g. app1.dockerdemo.javydekoning.com)
  • dockerdemo.yourdomain This will be used by Traefik (This is NOT required, we could also use traefik.dockerdemo.javydekoning.com)

From the instance/VM we just created, copy either the public IP (to be used in an A-Record), or the Public DNS name (to be used in a CNAME record). I will be using the public DNS name, therefore I’ll create a CNAME record.

Instructions will differ depending on your DNS provider. My zone is hosted in Route53, so we can create a record by clicking “Create Record Set” in the AWS console. We use a wildcard (*) record because we plan to create multiple sub-domains for our containers.

Configuring the Docker host

The OS only needs to run Docker and Docker-Compose all the other applications will run in containers. Let’s set that up. We’ll install docker, docker-compose and make sure the docker daemon is started on system boot.

#Install dependencies
sudo su 
yum update -y
yum install -y python-pip docker
pip install docker-compose

#Start docker and configure to start on init. 
service docker start
chkconfig docker on

#create files and directories
mkdir /dockerdemo/traefik -p
touch /dockerdemo/docker-compose.yml \
      /dockerdemo/traefik/traefik.toml \
      /dockerdemo/traefik/acme.json
chmod 600 /dockerdemo/traefik/acme.json

Next, we will create the required files and folders. That will look like this:

/dockerdemo
├── /traefik
│   ├── /traefik.toml    <-- config
│   └── /acme.json       <-- certificate store
└── docker-compose.yml   <-- container infrastructure

Create and set permissions:

#create files and directories
mkdir /dockerdemo/traefik -p
touch /dockerdemo/docker-compose.yml \
      /dockerdemo/traefik/traefik.toml \
      /dockerdemo/traefik/acme.json
chmod 600 acme.json

Creating a Traefik container

First we setup our Traefik container. This container will act as a reverse proxy and redirect the traffic to the correct container. For this we will have to set up two files:

  1. /dockerdemo/docker-compose.yml This will tell docker what containers to run
  2. /dockerdemo/traefik/traefik.toml This will hold the Traefik config

traefik.toml

We will start with the traefik config file. We want to configure the Traefik webinterface [web] and re-direct all http traffic to https to encrypt all our traffic. Finally, when we create a new docker container we don’t want to reconfigure Traefik, hence we tell Traefik to watch Docker.

Open traefik.toml in the editor of your choice and edit as follows:

#Comment out when done.
logLevel = "DEBUG"

defaultEntryPoints = ["http","https"]

[web]
  #Run Traefik info page at 8080.
  address = ":8080"

[entryPoints]
  [entryPoints.http]
    #redirect ALL http traffic to https 443
    address = ":80"
    [entryPoints.http.redirect]
      entryPoint = "https"
  [entryPoints.https]
    address = ":443"
    [entryPoints.https.tls]

#Let's encrypt setup
[acme]
  email = "info@javydekoning.com"
  storage = "acme.json"
  entryPoint = "https"
  #When new host is created, request certificate.
  onHostRule = true
  [acme.httpChallenge]
    entryPoint = "http"

#Watch Docker, when new containers are created with label create mapping.
[docker]
  endpoint = "unix:///var/run/docker.sock"
  domain = "dockerdemo.javydekoning.com"
  watch = true

docker-compose.yml

Next we will setup our docker-compose.yml file. We will start with just the Traefik container.

version: '3'
services:
  traefik:
    image: traefik
    container_name: traefik
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "./traefik/traefik.toml:/traefik.toml"
      - "./traefik/acme.json:/acme.json"
    labels:
      - "traefik.port=8080"
      - "traefik.frontend.rule=Host:dockerdemo.javydekoning.com"

Some key pointers:

  1. docker.sock we map the sock file from the host container, so Traefik can monitor changes in the docker environment.
  2. ./traefik/* maps the configuration file and certificate store from our host to our Traefik container.
  3. traefik.port tells traefik to which backend port traffic needs to be redirected.
  4. traefik.frontend.rule tells traefik which ‘Host header’ should be redirected to this container.

Testing

Make sure you are in the directory of your docker-compose.yml and run

/usr/local/bin/docker-compose up -d

Now go to the IP of your container host to see if it works. You should see:

Don’t worry about the 404. This is what we expect! Remember, we told Traefik ONLY to redirect traffic to our container if the http host header is ‘dockerdemo.yourdomain’. Let’s test that as well, you should be redirected to HTTPS and see:

If you are presented with an invalid certificate, troubleshoot using:

docker logs traefik

Adding two more containers

Finally we will introduce two more containers.

  1. Portainer lightweight management UI for your docker environment.
  2. jwilder/whoami simple HTTP docker service that prints it’s container ID.

For the whoami container we will also add a form of authentication. You can skip this if you don’t want that, or you can use the hash I generated (for testing only).

We will generate a secure Bcrypt hash. On your local machine install the apache2 utils and run:

htpasswd -nbB -C 13 username YouShouldNotUseThisPassword!

This should return:

username:$2y$13$dSXnrizi74xsfKH740Axt..tIiCL16GGpQn4hpX6Wtph15XGVS08u

You need to duplicate the ‘$’ in this string when adding this to your docker-compose.yml file.

Let’s complete our docker-compose.yml file:

version: '3'
services:
  traefik:
    image: traefik
    container_name: traefik
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "./traefik/traefik.toml:/traefik.toml"
      - "./traefik/acme.json:/acme.json"
    labels:
      - "traefik.port=8080"
      - "traefik.frontend.rule=Host:dockerdemo.javydekoning.com"

  portainer:
    container_name: portainer
    image: portainer/portainer:latest
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - ./portainer:/data
    labels:
      - "traefik.port=9000"
      - "traefik.frontend.rule=Host:portainer.dockerdemo.javydekoning.com"

  whoami:
    container_name: whoami
    image: jwilder/whoami
    labels:
      - "traefik.port=8000"
      - "traefik.frontend.rule=Host:app1.dockerdemo.javydekoning.com"
      - "traefik.frontend.auth.basic=username:$$2y$$13$$dSXnrizi74xsfKH740Axt..tIiCL16GGpQn4hpX6Wtph15XGVS08u"

There is one newly added label:

  1. traefik.frontend.auth.basic tells traffic to use basic authentication to authenticate a user before passing traffic on to the container.

Final Docker and Traefik test!

Make sure you are in the directory of your docker-compose.yml file again and run

/usr/local/bin/docker-compose up -d

This should bring up the two remaining containers. Now the first thing you should do is go to portainer.yourdomain and SET the initial credentials.

Next go to app1.yourdomain, and you should be asked for the credentials that you’ve created with the ‘htpasswd’ tool. If you’ve used my exact example that would be

  • user: username
  • pass: YouShouldNotUseThisPassword!

Now you should see this:

All done

Congrats, you’ve configured Docker and Traefik (reverse-proxy), Docker-Compose, ssl and authentication!


Javy de Koning
WRITTEN BY
Javy de Koning
Geek 🤓, Love sports 🏃‍♂️🏋️‍♂️, Food 🍛, Tech 💻, @Amsterdam ❌❌❌.