À quoi ça sert

Un reverse proxy est un serveur placé devant tes applications. Il reçoit les requêtes des utilisateurs et les redirige vers le bon service interne selon des règles : nom de domaine, chemin d'URL, en-tête… Il sert aussi à terminer le HTTPS, à équilibrer la charge entre plusieurs instances, à appliquer de l'authentification, à logger…

Traefik (prononcé « traffic ») est un reverse proxy open source écrit en Go, dont la particularité est d'être dynamique : il se reconfigure tout seul quand tes services apparaissent ou disparaissent. Tu lui donnes accès à Docker (ou Kubernetes), tu colles quelques labels sur tes conteneurs, et il s'occupe du reste.

Ses points forts pour un projet MLOps :

  • Autodiscovery — pas besoin de toucher à la config Traefik quand tu ajoutes un service, ce sont les labels du service qui parlent.
  • HTTPS automatique — Traefik demande, renouvelle et installe les certificats Let's Encrypt tout seul.
  • Dashboard intégré — une UI web qui montre en temps réel les routes et services connus.
  • Multi-providers — Docker, Kubernetes, fichier YAML, Consul… On peut mélanger.
En une phrase

Traefik = un reverse proxy qui se reconfigure tout seul à mesure que tes conteneurs apparaissent et disparaissent, avec HTTPS Let's Encrypt automatique.

Traefik vs nginx

Avec nginx, chaque ajout de service implique d'éditer un fichier de config et de relancer nginx. C'est très bien pour des architectures stables. Avec Traefik, tu ne touches plus à la config du proxy après l'install : les services se déclarent eux-mêmes via leurs labels Docker. C'est ce qui le rend particulièrement adapté aux environnements où les conteneurs vont et viennent (CI, dev, microservices, déploiements progressifs).

Un exemple d'usage

Tu as trois applis dans Docker — une API FastAPI, un dashboard Streamlit, une UI MLflow — chacune sur un port différent, et tu veux les exposer sous trois sous-domaines en HTTPS sans écrire 50 lignes de config.

Avec Traefik, c'est un docker-compose.yml qui démarre Traefik plus tes services, et chaque service ajoute deux labels pour annoncer « voici mon hostname et mon port interne » :

yaml (extrait)
services:
  api:
    image: monorg/api:latest
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.maboite.fr`)"
      - "traefik.http.services.api.loadbalancer.server.port=8000"

Pas besoin de redémarrer Traefik ni de toucher au moindre fichier de config : quand le service démarre, Traefik le voit et la route devient active. Quand tu coupes le conteneur, la route disparaît.

How-to : installer et utiliser Traefik

  1. Lancer Traefik dans Docker

    Le scénario le plus courant en formation. Crée un docker-compose.yml minimal :

    yaml
    services:
      traefik:
        image: traefik:v3.1
        command:
          - "--api.insecure=true"             # dashboard sans auth (dev only)
          - "--providers.docker=true"
          - "--providers.docker.exposedbydefault=false"
          - "--entrypoints.web.address=:80"
        ports:
          - "80:80"      # trafic HTTP
          - "8080:8080"  # dashboard
        volumes:
          - "/var/run/docker.sock:/var/run/docker.sock:ro"

    Lance avec docker compose up -d. Le dashboard est accessible sur http://localhost:8080.

    Le socket Docker

    Le volume /var/run/docker.sock est ce qui permet à Traefik de voir tes conteneurs en temps réel. C'est puissant mais sensible : un conteneur qui a accès au socket peut piloter le démon Docker. En prod, il y a des patterns plus sûrs (proxy de socket en lecture seule). En dev, on accepte le risque.

  2. Comprendre les 4 concepts clés

    Toute la doc Traefik tourne autour de ces quatre mots — apprends-les une fois, tu comprendras tous les exemples :

    • EntryPoint — le port d'entrée (web = 80, websecure = 443).
    • Router — la règle de matching qui décide quelle requête va vers quel service (par hostname, chemin, header…).
    • Service — la cible : le conteneur (ou plusieurs, en load balancing) qui va traiter la requête.
    • Middleware (optionnel) — une transformation appliquée entre le router et le service : auth, redirection, modif d'header, rate limit…

    Le flux d'une requête : EntryPointRouter (qui matche) → Middlewares éventuels → Service.

  3. Exposer un service via labels Docker

    On a configuré Traefik avec exposedbydefault=false : par défaut aucun conteneur n'est exposé. C'est volontaire (tu choisis ce que tu publies). Pour exposer un service, tu ajoutes 3 labels minimum :

    yaml
      monservice:
        image: monorg/monservice
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.mon.rule=Host(`mon.localhost`)"
          - "traefik.http.services.mon.loadbalancer.server.port=3000"

    Décrypté : le router nommé mon matche les requêtes dont l'hôte est mon.localhost, et les envoie vers le service mon sur le port interne 3000 du conteneur.

  4. HTTPS Let's Encrypt automatique (en prod)

    Pour la prod, Traefik peut obtenir et renouveler des certificats TLS tout seul. Il suffit d'ajouter un certResolver dans la config et un label sur le router. On en montre le concept ici, l'exercice en bas reste en HTTP local pour ne pas dépendre d'un domaine public :

    yaml (concept prod)
    command:
      - "--certificatesresolvers.le.acme.email=toi@maboite.fr"
      - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.le.acme.tlschallenge=true"
      - "--entrypoints.websecure.address=:443"
    
    # sur le service :
    labels:
      - "traefik.http.routers.mon.entrypoints=websecure"
      - "traefik.http.routers.mon.tls.certresolver=le"

    Au premier démarrage, Traefik négocie le certificat avec Let's Encrypt et l'utilise. Le renouvellement (tous les 90 jours) se fait sans rien faire.

Exercice détaillé : démo de présentation

Cet exercice est conçu pour une présentation de 15-20 minutes. Tu déroules les 7 étapes, chacune ajoute une fonctionnalité visible à l'écran. Le projet final tient dans un seul docker-compose.yml.

Pré-requis pour le live : Docker + Docker Compose installés, ports 80 et 8080 libres, navigateur ouvert. Sur Linux/macOS, *.localhost résout automatiquement vers 127.0.0.1 (sur Windows récent aussi). Aucun ajout dans /etc/hosts nécessaire.

Conseil de présentation

Avant la démo, lance une fois le projet final pour télécharger les images Docker. Le jour J, tu n'attendras pas pendant le téléchargement devant l'auditoire. Ensuite, fais docker compose down et reprends à l'étape 1.

  1. Étape 1 — Démarrer Traefik seul

    Crée un dossier traefik-demo avec ce docker-compose.yml :

    yaml
    services:
      traefik:
        image: traefik:v3.1
        command:
          - "--api.insecure=true"
          - "--providers.docker=true"
          - "--providers.docker.exposedbydefault=false"
          - "--entrypoints.web.address=:80"
          - "--log.level=INFO"
        ports:
          - "80:80"
          - "8080:8080"
        volumes:
          - "/var/run/docker.sock:/var/run/docker.sock:ro"
    bash
    docker compose up -d
    docker compose logs -f traefik

    Ce que tu montres : ouvrir http://localhost:8080. Le dashboard est vide : Traefik tourne, mais aucun service n'est encore exposé. Mets bien en avant exposedbydefault=false dans le commentaire — c'est le détail qui surprend les débutants en suivant des tutos où tout est exposé par défaut.

  2. Étape 2 — Ajouter un premier service

    On utilise traefik/whoami, une image officielle minuscule qui répond sur HTTP en affichant les en-têtes reçues. Idéale pour démontrer le routage : tu vois directement quel hôte la requête a traversé.

    yaml (à ajouter sous "services:")
      app1:
        image: traefik/whoami
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.app1.rule=Host(`app1.localhost`)"
          - "traefik.http.routers.app1.entrypoints=web"
    bash
    docker compose up -d
    curl http://app1.localhost

    Ce que tu montres : (1) dans le dashboard, le router app1 apparaît tout seul — Traefik a détecté le nouveau conteneur sans qu'on touche à sa config ; (2) curl renvoie la liste des en-têtes vues par whoami, dont X-Forwarded-Host: app1.localhost. Le routage par hôte fonctionne.

    Pourquoi pas de label "port"

    L'image whoami n'expose qu'un seul port (80). Quand le conteneur n'a qu'un port exposé, Traefik le devine. Si plusieurs ports sont exposés, il faut préciser avec traefik.http.services.app1.loadbalancer.server.port=….

  3. Étape 3 — Routage par hostname avec deux services

    Pour montrer la valeur d'un reverse proxy, on ajoute un deuxième service identique mais sous un autre hôte :

    yaml (à ajouter)
      app2:
        image: traefik/whoami
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.app2.rule=Host(`app2.localhost`)"
          - "traefik.http.routers.app2.entrypoints=web"
    bash
    docker compose up -d
    curl http://app1.localhost
    curl http://app2.localhost

    Ce que tu montres : deux services, un seul port d'entrée (80), zéro modif de la config Traefik. Mets en avant que ça marcherait avec n'importe quel nombre de services. Le dashboard montre les deux routers en parallèle.

  4. Étape 4 — Routage par chemin (PathPrefix)

    Le routage par hôte est le plus courant, mais Traefik sait aussi router par chemin d'URL. Pratique quand tout doit vivre derrière le même domaine. Modifie le router de app2 :

    yaml
      app2:
        image: traefik/whoami
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.app2.rule=Host(`app1.localhost`) && PathPrefix(`/v2`)"
          - "traefik.http.routers.app2.entrypoints=web"
    bash
    docker compose up -d
    curl http://app1.localhost           # va vers app1
    curl http://app1.localhost/v2/abc    # va vers app2

    Ce que tu montres : la règle combine deux conditions avec &&. C'est ce qui permet à Traefik de servir une API et son front au même endroit (par exemple /api/* vers le backend, le reste vers le front).

  5. Étape 5 — Ajouter un middleware (BasicAuth)

    Les middlewares s'intercalent entre le router et le service. Démontrons-en un : protéger app2 par un mot de passe. D'abord, génère le hash :

    bash
    docker run --rm httpd:alpine htpasswd -nb admin secret
    # renvoie quelque chose comme : admin:$apr1$XXX$YYYYYYY

    Puis ajoute deux labels au service :

    yaml
          - "traefik.http.middlewares.auth-app2.basicauth.users=admin:$$apr1$$XXX$$YYYYYYY"
          - "traefik.http.routers.app2.middlewares=auth-app2"
    Attention au double dollar

    Dans un fichier docker-compose.yml, les $ sont interprétés comme variables d'environnement. Il faut les doubler ($$) pour qu'ils arrivent intacts à Traefik. C'est le piège classique.

    bash
    docker compose up -d
    curl http://app1.localhost/v2/abc                    # 401
    curl -u admin:secret http://app1.localhost/v2/abc   # 200

    Ce que tu montres : la composition. Le middleware est défini puis attaché à un router. Le même middleware peut être attaché à plusieurs routers — c'est le pattern « auth centralisée » en une ligne.

  6. Étape 6 — Scaler un service (load balancing)

    Le mot « loadbalancer » qu'on a vu dans les labels n'est pas décoratif : si plusieurs conteneurs partagent le même router, Traefik répartit les requêtes entre eux en round-robin. Démontre-le en démarrant 3 instances de app1 :

    bash
    docker compose up -d --scale app1=3
    for i in 1 2 3 4 5 6; do curl -s http://app1.localhost | grep Hostname; done

    Ce que tu montres : les requêtes alternent entre 3 hostnames de conteneurs différents. Pas de config supplémentaire — Traefik a découvert les 3 instances via Docker, et a constaté qu'elles partagent les mêmes labels donc le même service côté Traefik.

  7. Étape 7 — Synthèse à l'oral

    Conclus en 3 messages clés (à dérouler à l'oral, dashboard à l'écran) :

    1. On n'a jamais redémarré Traefik. Toute la config est venue des labels des services.
    2. Ça marche pour 1 ou 100 services. Le pattern est identique : un service = quelques labels.
    3. En prod, on ajoute le certResolver Let's Encrypt et un entrypoints.websecure sur 443. Tout le reste reste pareil.

    Pour ranger : docker compose down dans le dossier traefik-demo.

Aide-mémoire

yaml — labels Docker
# Activer Traefik sur ce conteneur
- "traefik.enable=true"

# Router (matching) — quelques règles courantes
- "traefik.http.routers.X.rule=Host(`a.localhost`)"
- "traefik.http.routers.X.rule=Host(`a.fr`) && PathPrefix(`/api`)"
- "traefik.http.routers.X.rule=Headers(`X-Tenant`, `acme`)"

# EntryPoint (web=80, websecure=443)
- "traefik.http.routers.X.entrypoints=web"

# Service (port interne, si plusieurs ports exposés)
- "traefik.http.services.X.loadbalancer.server.port=8000"

# Middlewares
- "traefik.http.middlewares.auth.basicauth.users=admin:$$..."
- "traefik.http.middlewares.strip.stripprefix.prefixes=/api"
- "traefik.http.routers.X.middlewares=auth,strip"

# HTTPS (en prod)
- "traefik.http.routers.X.entrypoints=websecure"
- "traefik.http.routers.X.tls.certresolver=le"
bash — commandes utiles
docker compose up -d                        # démarrer
docker compose logs -f traefik              # suivre les logs
docker compose up -d --scale app1=3        # 3 instances
docker compose down                          # tout arrêter

# Générer un hash BasicAuth
docker run --rm httpd:alpine htpasswd -nb admin secret

Pour aller plus loin