My beginnings with Traefik

2020-04-29T00:08:08+02:00 | 9 minute read | Updated at 2020-04-29T00:08:08+02:00

@
My beginnings with Traefik

I’m sharing with you my beginnings with the “Edge Router” Traefik one its 2.X version.

Introduction

Traefik is an open source reverse-proxy written in Go, which is also named as a Edge Router.
Its logo proves it : Traefik logo

In version 1, it only rerouted HTTP (S) requests. Since version 2, it works for all TCP type applications.

Writing an article on Traefik is not the easiest … That’s why I’m going to do it in parts (as I learn about the solution). In order to do my best to present its usage, we will deploy some Nginx containers which will be accessible via Traefik. Before that, we will launch Traefik with its dashboard secured with basic authentication. This will allow us to understand the concepts of Traefik.

[!WARNING] This article does not cover the HTTPS part.

I advise you to follow this tutorial on a Linux machine with a web browser. To complicate my life, I give a try on Lubuntu 20.04.

Preparation

To be able to run the examples in this tutorial, you need to add some entries in your /etc/hosts file.

sudo cp /etc/hosts /etc/hosts.bak # we do a backup !
echo "#### <Traefik tests> ####" | sudo tee -a /etc/hosts
echo "127.0.0.1  traefik.example.com" | sudo tee -a /etc/hosts
echo "127.0.0.1  pgo.example.com" | sudo tee -a /etc/hosts
echo "127.0.0.1  hommell.example.com" | sudo tee -a /etc/hosts
echo "127.0.0.1  site-audi.example.com" | sudo tee -a /etc/hosts
echo "127.0.0.1  bmw-site.example.com" | sudo tee -a /etc/hosts
echo "127.0.0.1  trucexterne.example.com" | sudo tee -a /etc/hosts
echo "#### </Traefik tests> ####" | sudo tee -a /etc/hosts

🚗 These names refer to car brands (except for “trucexterne” which refer to “someting external” in french…). 🚗
We check our new entries :

for i in traefik pgo hommell site-audi bmw-site; do ping -c 1 ${i}.example.com &> /dev/null && echo OK || echo FAIL $i; done

Of course, you need Docker and also Docker Compose :

sudo apt update
sudo apt install docker.io docker-compose
systemctl start docker
systemctl enable docker # activate Docker on boot

Traefik

First launch

Traefik requests access to the Docker socket, so it will be necessary to launch the docker-compose as root.

I work in the following environment :

sudo -i # we switch to root
mkdir -p docker/traefik
cd docker/traefik

So let’s create our docker-compose.yml as in the quick-start of the official documentation :

version: '3'

services:
  reverse-proxy:
    # The official v2 Traefik docker image
    image: traefik:v2.2
    # Enables the web UI and tells Traefik to listen to docker
    command: --api.insecure=true --providers.docker
    ports:
      # The HTTP port
      - "80:80"
      # The Web UI (enabled by --api.insecure=true)
      - "8080:8080"
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock

So we launch our docker-compose (docker-compose up -d).
We then browse on the port 8080 (http://localhost:8080) which give us unsecured connexion to the dashboard.
Port 80 doesn’t give anything because we don’t have any service routed to it yet.

If it works, you are ready to get down to business.
So we take a Panadol and we destroy our container with a docker-compose down.

Authenticated Dashboard

Concepts

The solution can be configured in several ways. You will find three methods :

  • Static configuration
  • Dynamic configuration with files
  • Dynamic configuration with labels (Docker)

Traefik’s strength lies in its dynamic feature : once properly configured and launched, no need to reload it each time new services are added (no systemctl apache reload for example).

Here, we will cover the three ways of doing those in the following respective way :

  • Traefik configuration
  • Authenticated Dashboard configuration
  • Additional services configuration (next part)

Application

We modify our docker-compose.yml as follows :

version: '3'

services:
  reverse-proxy:
    # Traefik v2.2 officiel
    image: traefik:v2.2
    ports:
      # The HTTP port
      - "80:80"
      # Dashboard
      - "8080:8080"
    volumes:
      # Mapping static configuration
      - ./traefik.yml:/etc/traefik/traefik.yml
      # Mapping dynamic configuration folder
      - ./dyn_traefik/:/etc/dyn_traefik/
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock

We therefore simplify this file by removing the command: directive. The relative information will be specified in the traefik.yml file.
The dyn_traefik folder will contain configuration file for dynamic config : only one in our case, that of the dashboard which represents our first service.

So, we create the file traefik.yml in our working folder with the following content :

entryPoints:
    web:
      address: ':80'
    dashboard:
      address: ':8080'
log:
  # Default: "ERROR"
  level: INFO
api:
  dashboard: true
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    # Expose containers by default in traefik
    exposedByDefault: true
  # Enable the file provider to define routers / middlewares / services in file
  file:
    directory: /etc/dyn_traefik/
    watch: true

You often have this file in TOML format, I preferred to do it in YAML to remain consistent with the other files already in this language.
I put the logs at “INFO” level to have a little more info just in case.
The option watch: true tells Traefik to listen to the updates of this folder.

We then create the folder dyn_traefik. In it, I created a dashboard.yml as follow :

http:
    routers:
      api:
        rule: "Host(`traefik.example.com`)"
        service: api@internal
        middlewares:
          - auth
    middlewares:
      auth:
        basicAuth:
          users:
            - "admin:$2y$10$sqgVdrmQxXQgYIpg3BycVuZ2J8sFSHheVnmd728KkE7fznFSlxA0u" 
            - "test:$2y$10$mPcvlMccO2mXjA2ye.uBWuhO36x46K/3b.g1AjJ1YllmKCSAuS.vG"

With this configuration, the dashboard is reachabled with the URL traefik.example.com and will benefit of a basic authentication with the accounts “admin” and “test” which passwords are respectively “admin” and “test”.

To generate hashes of your passwords, you can learn french and check another article.

Hit a docker-compose up -d and test the dashboard access.
Cool right ? 😎

Adding containers “internal”

We will now add two services to our Docker Compose file.
These two services will simply be Nginx servers with specific web pages to identify them properly.

So we add the following content to our file :

  pgo:
    image: nginx:alpine
    volumes:
      - ./index-pgo.html:/usr/share/nginx/html/index.html
    labels:
      - traefik.enable=true
      - traefik.port=80
      - traefik.http.routers.pgo.rule=Host(`pgo.example.com`)

  hommell:
    image: nginx:alpine
    volumes:
      - ./index-hommell.html:/usr/share/nginx/html/index.html
    labels:
      - traefik.enable=true
      - traefik.port=80
      - traefik.http.routers.hommell.rule=Host(`hommell.example.com`)

Those “labels” are there to dynamically configure Traefik.
The less obvious line is :

traefik.http.routers.<container_name>.rule=Host(`<some_url>`)

Write as above, it’s more meaningful, isn’t it?

So, we create the home pages for our websites :

echo "<h1>Pr&eacute;v&ocirc;t : Gilles and Olivier</h1>" > index-pgo.html
echo "<h1>Automobiles Michel Hommel</h1>" > index-hommell.html

Thus, we update our Docker Compose (no need to stop or destroy it) with the usual command.

The dashboard may show new entries in the router section the URLs (pgo.example.com & hommell.example.com) should show their respective website.

You can notice that we didn’t have to map 80 ports of our containers. It’s normal because the containers were create in the same network as Traefik (because we are in the same docker-compose.yml). Thereby As a result, it is indeed our routing agent Traefik who performs the routing.

[!IMPORTANT] So I have to put all my container definitions in the same Docker Compose ?

The answer and no and this is what we will see in the next part.

Adding containers “external”

Updating Docker Compose file

To be able to connect other containers to our reverse-proxy, we are going to use networks within Docker. We create a autoroute network definition in bridge called motorway (you will understand why after 😉).
This feature requires is introduced in version 3.5 of Docker Compose. So I also changed the version of my file to the last one I can: 3.7 since I have version 1.25 of Docker Compose.

We destroy our current stack with a docker-compose down and we modify the file.

So here is my updated file :

version: '3.7'

services:
  reverse-proxy:
    # Traefik v2.2 officiel
    image: traefik:v2.2
    ports:
      # The HTTP port
      - "80:80"
      # Dashboard
      - "8080:8080"
    volumes:
      # Mapping static configuration
      - ./traefik.yml:/etc/traefik/traefik.yml
      # Mapping dynamic configuration folder
      - ./dyn_traefik/:/etc/dyn_traefik/
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - autoroute

  pgo:
    image: nginx:alpine
    volumes:
      - ./index-pgo.html:/usr/share/nginx/html/index.html
    labels:
      - traefik.http.routers.pgo.rule=Host(`pgo.example.com`)
      - traefik.port=80
      - traefik.enable=true
    networks:
      - autoroute

  hommell:
    image: nginx:alpine
    volumes:
      - ./index-hommell.html:/usr/share/nginx/html/index.html
    labels:
      - traefik.http.routers.hommell.rule=Host(`hommell.example.com`)
      - traefik.port=80
      - traefik.enable=true
    networks:
      - autoroute

networks:
  autoroute:
    driver: bridge
    name: motorway

We “up” all this and we check that our three services are still accessible before moving on.

Creating another Docker Compose

We position ourselves in another folder and we create the files for Audi and BMW like those for PGO and Hommel:

mkdir -p ../german && cd ../german

echo "<h1>Auto Union Deutsches Industrie</h1>" > index-audi.html
echo "<h1>Bayerische Motoren Werke</h1>" > index-bmw.html

And the docker-compose.yml file :

version: '3.7'

services:

  audi:
    image: nginx:alpine
    volumes:
      - ./index-audi.html:/usr/share/nginx/html/index.html
    labels:
      - traefik.enable=true
      - traefik.port=80
      - traefik.http.routers.audi.rule=Host(`site-audi.example.com`)
    networks:
      - autobahn

  bmw:
    image: nginx:alpine
    volumes:
      - ./index-bmw.html:/usr/share/nginx/html/index.html
    labels:
      - traefik.enable=true
      - traefik.port=80
      - traefik.http.routers.bmw.rule=Host(`bmw-site.example.com`)

networks:
  autobahn:
    external:
      name: motorway

After launching this Docker Compose, we test the accessibility of German car websites and we realize that BMW’s does not work !
This is due to the fact that BMW is not in the “autobahn” (that the container is not in the defined network). I take this example a little too seriously…

[!TIP] Containous (the company behind Traefik), PGO and Hommell are french (“autoroute” is the french for motorway) while Audi and BMW are german.
The two docker-composers represent France and Germany ! Hence the network names in their respective official language and a common network name (“highway”) in the international language.

So you understand that, by this example, it is no longer necessary to touch the Traefik container. The ideal would therefore surely be to have a composition with Traefik alone.

Adding external services

Finally, we are going to reverse-proxy the content of an external server.

So I launched an Nginx on another server with a simple docker run :

docker run --name tteesstt -d -p 999:80 nginx:alpine

I then checked that I have access to the content from the server where Traefik is installed by browsing to the IP address of the server and the specified port.

So, we create a new dynamic configuration file in the dyn_traefik folder.

I called mine azerty.yml with the following content :

http:
  services:
    administration:
      loadBalancer:
        servers:
          - url: "http://192.168.92.17:999/"
  routers:
    administration:
      rule: "Host(`trucexterne.example.com`)"
      service: administration@file

I referenced this app under the name administration because I run out of idea. The @file specify that we use the provider “file” (cf. traefik.yml).

Destruction

Docker

On the external server :

docker stop tteesstt
docker system prune --all --volumes # Removes everything that is not used

On the server where Traefik and launched :

# By positioning myself in the `docker` folder
cd german && docker-compose down && cd ..
cd traefik && docker-compose down && cd ..
docker system prune --all --volumes

In case you want to delete the configuration files : rm -rf ./*

Don’t forget to delete the entries from your /etc/hosts file. If you did it like me:

mv /etc/hosts /etc/hosts.old && mv /etc/hosts.bak /etc/hosts
rm /etc/hosts.old

Conclusion

This article shows (in my own way) how to use Traefik. This tool is very powerful and it’s not very user firendly… Moreover, the official documentation doesn’t really help.

A next tutorial on Traefik (shorter I hope) will present Traefik in HTTPS via Let’s Encrypt certificates with probably other routing options.

© 2017 - 2024 Scrample

All rights reserved

About

Website create with the Hugo framework as a replacement of Wordpress.
The transition was made using the exitwp-for-hugo tool.

The theme used is Dream made by Yue Yang.

The logo was made using FreeLogoDesign.

Article illustrations use royalty-free images sourced from :

Comments use the GitHub ticket system through the Utterances application.

I use Umami to collect some frequentations metrics of this blog. My configuration respect the Do Not Track setting. You can view this data here.

The why and how of this site is explained on my first post.