đ Deploiement de Traefik
Présentation de Traefik
Traefik est un "edge router" open source. il est principalement utilisé en tant que reverse-proxy et load-balancer. C'est justement l'utilisation que je vais en faire.
Edge router
Un "edge router" (ou routeur de périphérie) est un dispositif de réseau qui se situe à la frontiÚre entre un réseau interne (tel qu'un réseau d'entreprise ou un réseau domestique) et un réseau externe, comme l'Internet.
Les raisons qui m'ont poussé à choisir Traefik :
- LĂ©ger et performant
- Une interface web
- Gestion du HTTP/S et génération de certificats
- Gestion du HTTP/2 et HTTP/3
- Collection Crowdsec
- Exposition des métriques vers Prometheus
- Prise en charge du proxy protocol
- Configuration dynamique
Traefik s'appuie sur les éléments suivants :
- Les providers : câest le ou les Ă©lĂ©ment(s) qui vont servir de source Ă traefik pour rĂ©cupĂ©rer sa configuration, donc la socket docker par exemple. Les providers permettent de dĂ©tecter oĂč se trouvent les services.
- Les entrypoints : qui sont les points dâĂ©coute, donc un port, un proto ( TCP / UDP / HTTP ) pour constituer un point dâentrĂ©e pour la requĂȘte.
- Les routers : analysent la requĂȘte pour savoir oĂč la rediriger et Ă©ventuellement lui appliquer un ou des middlewares.
- Les middlewares : permettent de changer la requĂȘte avec par exemple l'ajout dâentĂȘte, dâauthentification, de chemin dans lâURL etcâŠ
- Les services : dĂ©finition de la destination de la requĂȘte et load balancing vers cette derniĂšre
- Les certificates resolvers : permettent de gĂ©nĂ©rer les certificats via Letâs Encrypt : le choix du challenge, la taille de la clĂ© etcâŠ
Principe de fonctionnement
Toutes les requĂȘtes Ă destination d'une des applications exposĂ©es par Traefik passera d'abord par HAProxy. La requĂȘte sera ensuite dirigĂ©e vers un des noeuds du cluster Docker Swarm. Elle passera par le rĂ©seau ingress pour ĂȘtre routĂ©e vers Traefik. Ce dernier routera alors la requĂȘte Ă travers un autre rĂ©seau de type overlay vers le conteneur faisant tourner l'application. Ce dernier rĂ©seau sera nommĂ© ici "web" et sera crĂ©Ă© par nous.
Préparation de Traefik
Création des répertoires
olivier@ds01:/mnt/nfsdatas$ sudo mkdir traefik
olivier@ds01:/mnt/nfsdatas$ sudo chown olivier: traefik
olivier@ds01:/mnt/nfsdatas$ mkdir -p traefik/config/dyn_traefik
olivier@ds01:/mnt/nfsdatas$ mkdir traefik/{log,secrets}
Configuration du service Traefik
Nous allons commencer par configurer le service Traefik à travers le fichier docker-stack.yml avec un accÚs sécurisé au dashboard. Pour sécuriser l'accÚs au dashboard, nous mettrons en place une authentification user/password. Il faut pour cela préparer un hash du password.
olivier@ds01:/mnt/nfsdatas$ sudo apt install apache-utils -y
olivier@ds01:/mnt/nfsdatas$ echo $(htpasswd -nbB olivier "UN_SUPER_MOT_DE_PASSE") | sed -e s/\\$/\\$\\$/g
olivier@ds01:/mnt/nfsdatas$ olivier:$$2y$$05$$0n7VDgZCB4LZwTL5nWCK1.1yOn13yNTbGfkLzAatEQYEdN24lUGAe
- htpasswd : Un utilitaire fourni par le paquet apache-utils pour Debian pour gérer les fichiers contenant des mots de passe pour l'authentification HTTP Apache.
- -n : Affiche le résultat sur la sortie standard au lieu de l'écrire dans un fichier.
- -b : Utilise le mode batch, permettant de spĂ©cifier le mot de passe en ligne de commande (plutĂŽt que d'ĂȘtre invitĂ© Ă le saisir).
- -B : Utilise l'algorithme bcrypt pour hacher le mot de passe.
- olivier : Le nom d'utilisateur pour lequel le mot de passe est haché.
- "UN_SUPER_MOT_DE_PASSE" : Le mot de passe en clair Ă hacher.
Dans le cas d'utilisation du mot de passe dans notre fichier de configuration du service, nous devons Ă©chapper le symbole "$" en remplaçant ce dernier par "$$" d'oĂč l'utilisation de la commande sed pour le faire de façon automatique.
Passons au fichier de configuration du service.
olivier@ds01:/mnt/nfsdatas$ cd traefik
olivier@ds01:/mnt/nfsdatas/traefik$ vim docker-stack.yml
secrets:
ovh_endpoint:
file: "./config/secrets/ovh_endpoint.secret"
ovh_application_key:
file: "./config/secrets/ovh_application_key.secret"
ovh_application_secret:
file: "./config/secrets/ovh_application_secret.secret"
ovh_consumer_key:
file: "./config/secrets/ovh_consumer_key.secret"
version: "3.3"
services:
traefik:
# La version de Traefik utilisée est la version 3 qui est sortie récemment.
image: "traefik:v3.0"
environment:
- TZ=Europe/Paris
networks:
- web
secrets:
- "ovh_endpoint"
- "ovh_application_key"
- "ovh_application_secret"
- "ovh_consumer_key"
environment:
- "OVH_ENDPOINT_FILE=/run/secrets/ovh_endpoint"
- "OVH_APPLICATION_KEY_FILE=/run/secrets/ovh_application_key"
- "OVH_APPLICATION_SECRET_FILE=/run/secrets/ovh_application_secret"
- "OVH_CONSUMER_KEY_FILE=/run/secrets/ovh_consumer_key"
volumes:
# Mapping sur le socket interne de Docker
- '/var/run/docker.sock:/var/run/docker.sock:ro'
# Mapping du fichier de configuration statique
- './traefik/traefik.yml:/traefik.yml'
# Mapping du dossier contenant la configuration dynamique
- './traefik/dyn_traefik/:/dyn_traefik/'
# Mapping du fichier de stockage des certificats
# Le fichier initial doit ĂȘtre crĂ©er manuellement.
- './traefik/acme.json:/acme.json'
# Mapping du dossier contenant les logs des accĂšs et de traefik
- "./traefik/log:/var/log"
ports:
- "80:80"
- "443:443"
deploy:
placement:
constraints:
# La seule condition requise pour que Traefik fonctionne avec le mode Swarm
# est qu'il doit s'exĂ©cuter sur un nĆud manager depuis que
# l'API Swarm n'est exposĂ©e que sur les nĆuds manager.
- node.role == manager
restart_policy:
condition: on-failure
labels:
# Paramétrages pour accéder au dashboard de Traefik
- "traefik.enable=true"
- "traefik.http.routers.traefik-dashboard.rule=Host(`d.dev.quercylibre.fr`)"
- "traefik.http.routers.traefik-dashboard.service=api@internal"
- "traefik.http.routers.traefik-dashboard.entrypoints=websecure"
- "traefik.http.routers.traefik-dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.traefik-dashboard.middlewares=traefik-dashboard-auth"
- "traefik.http.middlewares.traefik-dashboard-auth.basicauth.users=olivier:$$2y$$05$$0n7VDgZCB4LZwTL5nWCK1.1yOn13yNTbGfkLzAatEQYEdN24lUGAe"
- "traefik.http.services.traefik-dashboard-service.loadbalancer.server.port=8080"
networks:
# RĂ©seau de type overlay qui permettra Ă Traefik de communiquer avec les autres conteneurs
web:
# external permet de connecter d'autre conteneur Ă cette stack
# sans qu'ils soient définis dans celle-ci.
external: true
Explication des labels permettant l'accĂšs au dashboard de Traefik :
- Activation de Traefik pour ce service
- traefik.enable=true : Cette étiquette indique à Traefik de gérer ce conteneur. Si cette étiquette est absente ou définie sur false, Traefik ignorera ce conteneur.
- DĂ©finition du routeur HTTP
- traefik.http.routers.traefik-dashboard.rule=Host(
d.dev.quercylibre.fr
) : Cette rĂšgle spĂ©cifie que le routeur (router) doit diriger les requĂȘtes HTTP vers ce service si l'hĂŽte de la requĂȘte est d.dev.quercylibre.fr. - traefik.http.routers.traefik-dashboard.service=api@internal : Cette Ă©tiquette indique que le service utilisĂ© par ce routeur est api@internal, qui est un service intĂ©grĂ© de Traefik pour accĂ©der Ă son propre tableau de bord et API.
- traefik.http.routers.traefik-dashboard.entrypoints=websecure : Ce label spécifie que le routeur doit utiliser le point d'entrée (entrypoint) nommé websecure que nous définirons plus tard dans le fichier de configuration statique traefik.yml. Les points d'entrée définissent les ports sur lesquels Traefik écoute (par exemple, 80 pour HTTP et 443 pour HTTPS).
- traefik.http.routers.traefik-dashboard.tls.certresolver=letsencrypt : Ce label indique que le routeur doit utiliser letsencrypt pour obtenir des certificats TLS.
- traefik.http.routers.traefik-dashboard.rule=Host(
- Middleware pour l'authentification
- traefik.http.routers.traefik-dashboard.middlewares=traefik-dashboard-auth : Ce label assigne le middleware nommé auth au routeur, ajoutant ainsi une couche d'authentification.
- traefik.http.middlewares.traefik-dashboard-auth.basicauth.users=olivier:$$2y$$05$$0n7VDgZCB4LZwTL5nWCK1.1yOn13yNTbGfkLzAatEQYEdN24lUGAe : Ce label configure le middleware traefik-dashboard-auth pour utiliser l'authentification basique.
- Service de backend
- traefik.http.services.traefik-dashboard-service.loadbalancer.server.port=8080 : Ce label configure le service Traefik pour Ă©quilibrer la charge sur le port 8080 du conteneur. Cela signifie que les requĂȘtes dirigĂ©es vers ce service seront envoyĂ©es au port 8080 du conteneur Docker.
Le choix de traefik-dashboard, traefik-dashboard-auth et traefik-dashboard-service contenus dans les labels est purement arbitraire. Je dirai qu'il doit ĂȘtre le plus parlant possible puisque visible plus tard dans le dashboard.
Passons au fichier de configuration statique de Traefik.
Configuration de Traefik
olivier@ds01:/mnt/nfsdatas/traefik$ vim config/traefik.yml
api:
dashboard: true
providers:
swarm:
# Il faut avoir mappé le volume dans le docker-compose.yml
# Ex : '/var/run/docker.sock:/var/run/docker.sock:ro'
endpoint: "unix:///var/run/docker.sock"
# Les conteneurs sont par défaut via Traefik. Si cette option est définie sur false,
# les conteneurs qui n'ont alors pas de label "traefik.enable=true" sont ignorés
# de la configuration de routage résultante.
exposedByDefault: false
# Fichier permettant de passer des options de configuration définies dans un fichier et
# chargées de maniÚre dynamique sans devoir redémarrer Traekif.
file:
directory: /dyn_traefik/
watch: true
# Points dâentrĂ©e sur lesquels Traefik Ă©coute pour prendre en compte
# les requĂȘtes entrantes.
entryPoints:
web: # Nom arbitraire
address: ":80"
# Etant derriÚre deux load-balancer, il faut définir leurs IP
# dans les options forwardedHeaders et de proxyProtocol afin
# d'obtenir la véritable IP source du client.
forwardedHeaders:
trustedIPs:
- "127.0.0.1/32"
- "10.0.0.0/24" # adressage IP du réseau docker "ingress"
- "IP_HAPROXY" # adresse IP cÎté LAN du serveur HAProxy
proxyProtocol:
trustedIPs:
- "127.0.0.1/32"
- "10.0.0.0/24"
- "IP_HAPROXY"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure: # Nom arbitraire
address: ":443"
forwardedHeaders:
trustedIPs:
- "127.0.0.1/32"
- "10.0.0.0/24"
- "IP_HAPROXY"
proxyProtocol:
trustedIPs:
- "127.0.0.1/32"
- "10.0.0.0/24"
- "IP_HAPROXY"
# Génération du certif HTTPS par challenge DNS avec OVH
certificatesResolvers:
letsencrypt:
acme:
# En mode production
caServer: "https://acme-v02.api.letsencrypt.org/directory"
# En mode staging. Recommandé lors des premiers lancements de Traefik.
#caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
email: "olivier@monemail.fr"
storage: "/acme.json"
dnsChallenge:
provider: ovh
delayBeforeCheck: 10
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
log:
filePath: "/var/log/traefik.log"
format: json
level: INFO
# Le level peut ĂȘtre positionnĂ© sur DEBUG lors du premier lancement du service
# level: DEBUG
accessLog:
filePath: "/var/log/access.log"
format: json
# Paramétrage pour prise en compte de la TZ Europe/Paris
fields:
names:
StartUTC: drop
Configuration pour Let's Encrypt
# Remplacer <OVH_...> par vos valeurs.
olivier@ds01:/mnt/nfsdatas/traefik$ echo "<OVH_ENDPOINT>" > config/secrets/ovh_endpoint.secret
olivier@ds01:/mnt/nfsdatas/traefik$ echo "<OVH_APPLICATION_KEY>" > config/secrets/ovh_consumer_key.secret
olivier@ds01:/mnt/nfsdatas/traefik$ echo "<OVH_APPLICATION_SECRET" > config/secrets/ovh_application_secret.secret
olivier@ds01:/mnt/nfsdatas/traefik$ echo "<OVH_CONSUMER_KEY>" > config/secrets/ovh_application_key.secret
olivier@ds01:/mnt/nfsdatas/traefik$ touch config/acme.json
olivier@ds01:/mnt/nfsdatas/traefik$ chmod 600 config/acme.json
Si vous utilisez OVH comme registrar de votre domaine, rendez-vous alors sur https://www.ovh.com/auth/api/createApp pour créer et récupérer les informations de connexion à leur API. Pour rappel, ces infos vont servir lors du challenge DNS utilisé par Let's Encrypt.
Une fois le challenge réussi, les certificats seront stockés dans le fichier acme.json.
Arborescence finale
olivier@ds01:/mnt/nfsdatas# tree traefik/
traefik/
âââ docker-stack.yml
âââ config
âââ acme.json
âââ dyn_traefik
âââ traefik.yml
âââ log
âââ access.log
âââ traefik.log
Configuration de HAProxy
(...)
frontend http
bind *:80
mode http
option http-keep-alive
# DĂ©claration des ACL permettant de router vers Traefik
acl acl_traefik-dashboard hdr(host) -i d.dev.quercylibre.fr
acl acl_traefik-cobalt hdr(host) -i cobalt.dev.quercylibre.fr
acl acl_traefik-cobalt-api hdr(host) -i cobalt-api.dev.quercylibre.fr
use_backend TRAEFIK_HTTP_BACKEND if acl_traefik-dashboard
use_backend TRAEFIK_HTTP_BACKEND if acl_traefik-cobalt
use_backend TRAEFIK_HTTP_BACKEND if acl_traefik-cobalt-api
default_backend DEFAULT_HTTP_BACKEND
frontend https
bind *:443
mode tcp
option tcplog
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
use_backend TRAEFIK_HTTPS_BACKEND if { req_ssl_sni -i d.dev.quercylibre.fr }
use_backend TRAEFIK_HTTPS_BACKEND if { req_ssl_sni -i w.dev.quercylibre.fr }
use_backend TRAEFIK_HTTPS_BACKEND if { req_ssl_sni -i cobalt.dev.quercylibre.fr }
use_backend TRAEFIK_HTTPS_BACKEND if { req_ssl_sni -i cobalt-api.dev.quercylibre.fr }
default_backend DEFAULT_HTTPS_BACKEND
## Note : Remplacer IP_DS0X par l'adresse IP du noeud en question.
backend TRAEFIK_HTTPS_BACKEND
mode tcp
option ssl-hello-chk
server TRAEFIK_HTTPS_BACKEND01 IP_DS01:443 send-proxy-v2 check
server TRAEFIK_HTTPS_BACKEND02 IP_DS02:443 send-proxy-v2 check
server TRAEFIK_HTTPS_BACKEND03 IP_DS03:443 send-proxy-v2 check
server TRAEFIK_HTTPS_BACKEND04 IP_DS04:443 send-proxy-v2 check
server TRAEFIK_HTTPS_BACKEND05 IP_DS05:443 send-proxy-v2 check
server TRAEFIK_HTTPS_BACKEND06 IP_DS06:443 send-proxy-v2 check
backend TRAEFIK_HTTP_BACKEND
mode http
balance source
stick-table type ip size 50k expire 30m
stick on src
http-reuse safe
server TRAEFIK_HTTP_BACKEND01 IP_DS01:80 send-proxy-v2
server TRAEFIK_HTTP_BACKEND02 IP_DS02:80 send-proxy-v2
server TRAEFIK_HTTP_BACKEND03 IP_DS03:80 send-proxy-v2
server TRAEFIK_HTTP_BACKEND04 IP_DS04:80 send-proxy-v2
server TRAEFIK_HTTP_BACKEND05 IP_DS05:80 send-proxy-v2
server TRAEFIK_HTTP_BACKEND06 IP_DS06:80 send-proxy-v2
(...)
Lancement de Traefik
Avant de lancer le service Traefik, il nous faut créer le réseau nommé "web". Pour rappel, c'est le réseau qui fera le lien entre Traefik et les conteneurs faisant tourner les applications. C'est un réseau de type overlay, c'est-à -dire qui s'étend à l'ensemble des noeuds présents dans le cluster Docker Swarm.
olivier@ds01:/mnt/nfsdatas/traefik$ docker network create --driver=overlay web
olivier@ds01:/mnt/nfsdatas/traefik$ docker network ls
NETWORK ID NAME DRIVER SCOPE
b7ce22097876 bridge bridge local
36c82b48f1f0 docker_gwbridge bridge local
6a3a351c8f6c host host local
18pqm0dz52z9 ingress overlay swarm
4be461a86c93 none null local
vhfy8nx6v5ak web overlay swarm
Maintenant que tout est prĂȘt nous pouvons lancer le service Traefik et suivre l'Ă©tat de la tĂąche :
olivier@ds01:/mnt/nfsdatas/traefik$ docker stack deploy -c docker-stack.yml -d traefik
Creating service traefik_traefik
olivier@ds01:/mnt/nfsdatas/traefik$ docker stack ps traefik
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xv2qyaptol8l traefik_traefik.1 traefik:v3.0.0 ds01 Running Running 3 hours ago
olivier@ds01:/mnt/nfsdatas/traefik$ tail -f log/traefik.log
{"level":"info","time":"2024-05-20T13:23:05+02:00","message":"\nStats collection is disabled.\nHelp us improve Traefik by turning this feature on :)\nMore details on: https://doc.traefik.io/traefik/contributing/data-collection/\n"}
{"level":"info","entryPointName":"web","time":"2024-05-20T13:23:05+02:00","message":"Enabling ProxyProtocol for trusted IPs [127.0.0.1/32 10.0.0.0/24 IP_HAPROXY]"}
{"level":"info","entryPointName":"websecure","time":"2024-05-20T13:23:05+02:00","message":"Enabling ProxyProtocol for trusted IPs [127.0.0.1/32 10.0.0.0/24 IP_HAPROXY]"}
{"level":"info","time":"2024-05-20T13:23:05+02:00","message":"Starting provider aggregator aggregator.ProviderAggregator"}
{"level":"info","time":"2024-05-20T13:23:05+02:00","message":"Starting provider *file.Provider"}
{"level":"info","time":"2024-05-20T13:23:05+02:00","message":"Starting provider *traefik.Provider"}
{"level":"info","time":"2024-05-20T13:23:05+02:00","message":"Starting provider *docker.SwarmProvider"}
{"level":"info","time":"2024-05-20T13:23:05+02:00","message":"Starting provider *acme.ChallengeTLSALPN"}
{"level":"info","time":"2024-05-20T13:23:05+02:00","message":"Starting provider *acme.Provider"}
{"level":"info","providerName":"letsencrypt.acme","acmeCA":"https://acme-v02.api.letsencrypt.org/directory","time":"2024-05-20T13:23:05+02:00","message":"Testing certificate renew..."}
Connexion au dashboard
Il en vous reste plus qu'à se connecter au dashboard. Cela vous demandera de vous authentifier avec le user et le mot de passe créés précédemment.
Mise Ă jour de l'article
- 22/06/2024 :
- Mise Ă jour de l'arborescence.
- Utilisation de Secrets au lieu des variables d'env pour le challenge DNS.