Sommaire

Mes débuts avec Traefik

Introduction

Traefik est un reverse-proxy open source écrit en Go, on le qualifie aussi de Edge Router.
Son logo nous le prouve :

/img/2020/2020-04-29-mes-debuts-avec-traefik/traefik.logo.png
Logo de Traefik

Dans sa version 1, il reroutait seulement les requêtes HTTP(S). Depuis la version 2, cela fonctionne pour toutes applications de type TCP.

Faire un article sur Traefik n’est pas des plus simple… C’est pour cela que je vais le faire en plusieurs parties (au fur et à mesure de mon apprentissage sur la solution).
Afin ce vous exposer au mieux le fonctionnement, on va déployer quelques conteneurs Nginx qui seront accessibles via Traefik.
Avant cela, nous allons lancer Traefik avec son dashboard équipé d’une authentification basique. Cela nous permettra de comprendre les concepts de Traefik.

Avertissement
Cet article ne traite pas la partie HTTPS.

Je vous conseille de suivre ce tutoriel sur une machine Linux avec un navigateur Web. Pour me compliquer la vie, je l’ai fait sur une Lubuntu 20.04.

Préparation

Pour pouvoir exécuter les exemples de ce tutoriel, il faut que vous ajoutiez quelques entrées dans votre fichier /etc/hosts.

1
2
3
4
5
6
7
8
9
sudo cp /etc/hosts /etc/hosts.bak # on fait une 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

🚗 Ces noms font référence à des marques de voitures (à part “trucexterne”…). 🚗
On vérifie nos nouvelles entrées :

1
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

Bien sur, il vous faut Docker ainsi que Docker Compose :

1
2
3
4
sudo apt update
sudo apt install docker.io docker-compose
systemctl start docker
systemctl enable docker # active le lancement de Docker au boot

Traefik

Premier lancement

Traefik demande l’accès au socket Docker, il faudra donc lancer le docker-compose en tant que root.

Je travaille dans l’environnement suivant :

1
2
3
sudo -i # on passe en root
mkdir -p docker/traefik
cd docker/traefik

Créons donc notre docker-compose.yml à l’image du quick-start de la documentation officiel :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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

On lance donc notre docker-compose (docker-compose up -d).
On navigue ensuite sur le port 8080 (http://localhost:8080) qui donne accès au dashboard de manière non sécurisé.
Le port 80 ne donne rien car nous n’avons aucun service routé dessus pour l’instant.

Si cela fonctionne, vous êtes prêt à passer aux choses sérieuses.
On prend donc un Doliprane et on détruit notre conteneur avec un docker-compose down.

Dashboard authentifié

Concepts

La solution peut se paramétrer de plusieurs façons. Vous trouverez trois méthodes :

  • Configuration statique
  • Configuration dynamique par fichier
  • Configuration dynamique par labels (Docker)

La force de Traefik réside dans son caractère dynamique : une fois bien paramétré et lancé, plus besoin de le recharger à chaque ajout de nouveaux services (pas de systemctl apache reload par exemple).

On va ici couvrir les trois façons de faire de la manière respective suivante :

  • Configuration de Traefik
  • Configuration de l’authentification du dashbord
  • Configuration de services supplémentaires (partie suivante)

Application

On modifie donc notre docker-compose.yml de la manière suivante :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: '3'

services:
  reverse-proxy:
    # Traefik v2.2 officiel
    image: traefik:v2.2
    ports:
      # Ports d'écoute
      - "80:80"
      # Dashboard
      - "8080:8080"
    volumes:
      # Mapping de la configuration statique
      - ./traefik.yml:/etc/traefik/traefik.yml
      # Mapping du dossier de conf dynamique
      - ./dyn_traefik/:/etc/dyn_traefik/
      # Pour que traefik puisse écouter les events Docker
      - /var/run/docker.sock:/var/run/docker.sock

On simplifie donc ce fichier en enlevant la directive command:. Les informations relatives seront spécifiées dans le fichier traefik.yml.
Le dossier dyn_traefik contiendra les fichiers de configuration dynamique : un seul dans notre cas, celui du dashbord qui représente notre premier service.

On crée donc le fichier traefik.yml dans notre dossier de travail avec le contenu suivant :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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

Vous verrez souvent ce fichier au format TOML, j’ai préféré le faire en YAML pour rester cohérent avec les autres fichiers déjà dans ce langage.
J’ai mis les logs au niveau “INFO” pour avoir un peu plus d’info au cas où.
L’option watch: true permets de dire à Traefik d’être à l’écoute des modifications sur le dossier.

On créer donc ensuite le dossier dyn_traefik. Dedans, j’ai crée un fichier dashboard.yml comme ceci :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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"

De par cette configuration, le dashboard sera accessible depuis l’URL traefik.example.com et bénéficiera d’une authentification basique avec les comptes “admin” et “test” dont les mots de passe sont respectivement “admin” et “test”.

Pour la génération des hashs de mots de passe, je vous renvoie sur un autre de mes articles.

Un coup de docker-compose up -d et testez l’accès à votre dashboard.
Cool non ? 😎

Ajout de conteneurs “internes”

On va maintenant ajouté deux services à notre fichier Docker Compose.
Ces deux services seront simplement des serveurs Nginx avec des pages Web spécifiques pour bien les identifier.

On ajoute donc le contenu suivant à la suite du fichier :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
  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`)

Les “labels” sont là pour configurer Traefik dynamiquement.
La ligne la moins évidente est :

1
traefik.http.routers.<nom_du_conteneur>.rule=Host(`<une_url>`)

Ecris comme ci-dessus, c’est plus parlant non ?

On crée donc les pages d’accueil pour nos sites Web :

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

Ainsi, on actualise notre Docker Compose (pas besoin de le stopper ou le détruire) avec la commande habituel.

Le dashboard devrait montrer les nouvelles entrées dans la section des routeurs et les URLs (pgo.example.com & hommell.example.com) devraient bien renvoyer sur leurs sites respectifs.

Vous remarquerez que nous n’avons pas eu à mapper les ports 80 des conteneurs. C’est normal car les conteneurs ont été créer dans le même réseau (“network”) que celui de Traefik (car on est dans le même docker-compose.yml). De ce fait, c’est bien notre aiguilleur Traefik qui effectue le routage.

Question
Du coup, je dois mettre toutes mes définitions de conteneur dans le même Docker Compose ?

La réponse et non et c’est ce que nous allons voir dans la partie suivante.

Ajout de conteneurs “externes”

Modification du Docker Compose

Pour avoir la possibilité de connecter d’autres conteneurs à notre reverse-proxy nous allons utiliser les réseaux au sein de Docker. On créer donc une définition de réseau autoroute en bridge qu’on nommera motorway (vous comprendrez pourquoi après 😉).
Cette fonctionnalité nécessite est introduite en version 3.5 de Docker Compose. J’ai donc aussi changé la version de mon fichier à la dernière que je peux : 3.7 vu que j’ai la version 1.25 de Docker Compose.

On détruit notre stack actuelle avec un docker-compose down et on modifie le fichier.

Voici donc mon fichier actualisé :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
version: '3.7'

services:
  reverse-proxy:
    # Traefik v2.2 officiel
    image: traefik:v2.2
    ports:
      # Ports d'écoute
      - "80:80"
      # Dashboard
      - "8080:8080"
    volumes:
      # Mapping de la configuration statique
      - ./traefik.yml:/etc/traefik/traefik.yml
      # Mapping du dossier de conf dynamique
      - ./dyn_traefik/:/etc/dyn_traefik/
      # Pour que traefik puisse écouter les events Docker
      - /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

On “up” tous cela et on vérifie que nos trois services sont toujours accessibles avant de passé à la suite.

Création d’un autre Docker Compose

On se positionne donc dans un autre dossier et on crée les fichiers pour Audi et BMW à l’image de ceux de PGO et Hommel :

1
2
3
4
mkdir -p ../allemandes && cd ../allemandes

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

Et le docker-compose.yml :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

Après avoir lancé ce Docker Compose, on teste l’accessibilité des sites Web des voitures allemandes et on se rend compte que celui de BMW ne fonctionne pas !
Cela est dû au fait que BMW n’est pas dans l’autobahn (que le conteneur n’est pas dans le réseau défini). Je prends cet exemple un peu trop à cœur…

Vous avez enfin compris l'univers de mon exemple ? Non... 🤬
Containous (la société derrière Traefik), PGO et Hommell sont françaises alors qu’Audi et BMW sont allemandes.
Les deux docker-compose représentent donc la France et l’Allemagne ! D’où les noms de réseau dans leur langue officielle respective et un nom de réseau commun ("motorway") dans la langue internationale.

Vous comprenez donc que, par cet exemple, qu’il n’est plus nécessaire de toucher au conteneur Traefik. L’idéal serait donc surement d’avoir une composition avec Traefik seul.

Ajout de services externe

Pour finir, nous allons faire un reverse-proxy du contenu d’un serveur externe.

J’ai donc lancé un Nginx sur un autre serveur avec un simple docker run :

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

J’ai ensuite vérifié que j’ai bien accès au contenu depuis le serveur où Traefik est installé en naviguant sur l’adresse IP du serveur et le port spécifié.

On va donc créer un nouveau fichier de configuration dynamique dans le dossier dyn_traefik.

J’ai appelé le mien azerty.yml et son contenu est le suivant :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
http:
  services:
    administration:
      loadBalancer:
        servers:
          - url: "http://192.168.92.17:999/"
  routers:
    administration:
      rule: "Host(`trucexterne.example.com`)"
      service: administration@file

J’ai référencé cette application sous le nom administration parce que j’avais pas d’idée. Le @file précise qu’on utilise le provider “file” (cf. traefik.yml).

Destruction

Docker

Sur le serveur externe :

1
2
docker stop tteesstt
docker system prune --all --volumes # Supprime tout ce qui n'est pas utilisé

Sur le serveur où Traefik et lancé :

1
2
3
4
# En me positionnant dans le dossier `docker`
cd allemandes && docker-compose down && cd ..
cd traefik && docker-compose down && cd ..
docker system prune --all --volumes

Au cas ou vous voulez supprimer les fichiers de configuration : rm -rf ./*

N’oubliez pas d’effacer les entrées de votre fichier /etc/hosts. Si vous avez fait comme moi :

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

Conclusion

Cet article montre donc (à ma façon) comment utiliser Traefik.
Cet outil est très puissant et a donc une prise en main qui n’est pas des plus simple… De plus la documentation officielle n’aide pas vraiment.

Un prochain tutoriel sur Traefik (plus court j’espère) présentera Traefik en HTTPS via des certificats Let’s Encrypt avec surement d’autres options de routages.