Pour qu'un Traefik 3.7.15 reste fiable face à un backend lent, mort ou bavard, bornez la connexion et la réponse du backend dans un transport de serveur, allongez le délai d'écriture du point d'entrée pour les flux, ajoutez une sonde de santé et ne laissez pas un retry rejouer une écriture. Une sonde de santé est une vérification périodique de chaque backend ; un retry est une nouvelle tentative automatique, qui peut compter deux fois une action quand elle est mal cadrée. Cette leçon s'adresse à qui publie déjà un service derrière Traefik et découvre qu'un réglage qui paraît raisonnable peut produire des pannes silencieuses : un flux d'événements coupé à heure fixe, un doublon que le client ne voit pas. Elle applique à Traefik les principes de la leçon sur les flux longs et repose sur des mesures rejouées sur la version 3.7.15.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »La leçon va du placement des délais jusqu'à la preuve, réglage par réglage.
- Placer chaque délai sur le tronçon qu'il surveille : client vers Traefik, ou Traefik vers backend.
- Borner un backend muet sans casser une réponse lente mais légitime.
- Expliquer pourquoi un flux SSE actif est coupé par le délai d'écriture.
- Régler une sonde de santé et prévoir le temps qu'elle met à agir.
- Empêcher qu'une réponse perdue fasse compter deux fois une action.
- Prouver chaque réglage par une mesure faite depuis un client.
Prérequis
Section intitulée « Prérequis »La leçon suppose que vous savez écrire une route Traefik et lire un code HTTP. Le lab fournit la machine, les quatre backends et les clients de mesure.
- Le modèle de Traefik (point d'entrée, router, service) : Traefik : entrypoints, routers, services et middlewares.
- La publication de plusieurs instances d'un même service et l'idée de sonde : Traefik avec Docker.
- Le concept de flux long, de borne d'inactivité et de battement : Reconnaître les besoins des flux longs et des protocoles applicatifs.
- Écrire un fichier de routes dynamique et le recharger : le fournisseur fichier.
Où Traefik pose-t-il ses bornes de temps ?
Section intitulée « Où Traefik pose-t-il ses bornes de temps ? »Traefik surveille deux tronçons avec deux familles de réglages : le tronçon client vers Traefik se règle sur le point d'entrée, le tronçon Traefik vers backend se règle dans un transport de serveur. Un backend est le serveur d'application derrière le proxy. Un délai (timeout) est une durée au-delà de laquelle Traefik abandonne et répond lui-même par une erreur, au lieu d'attendre sans fin.
Le transport de serveur (serversTransport) est un objet de configuration
dynamique qu'un service cite par son nom. Il porte les forwardingTimeouts :
dialTimeout pour établir la connexion réseau (TCP) vers le backend, et
responseHeaderTimeout pour attendre le début de la réponse. Le point
d'entrée (entrypoint), lui, est le port d'écoute de Traefik. Sa configuration
est statique, lue au démarrage, et porte transport.respondingTimeouts avec
notamment writeTimeout, dont la valeur d'origine est 0s (aucun délai) selon la
documentation de Traefik 3.7. Les deux autres délais de ce bloc, readTimeout
(lecture de la requête entière, 60s d'origine) et idleTimeout (connexion
inactive entre deux requêtes, 180s d'origine), ne sont pas traités ici.
Cette différence de nature a une conséquence pratique. Le fichier dynamique est
relu à chaud par Traefik. Le fichier statique ne l'est pas : un changement de
writeTimeout demande de redémarrer Traefik, ce que le lab rappelle dans
son README. La page Dépanner un point d'entrée Traefik
cite déjà les valeurs d'origine de deux de ces délais : 30 secondes pour
dialTimeout et aucun délai pour responseHeaderTimeout.
Regardez le schéma de gauche à droite : les deux bornes du transport ne couvrent
que leur étape, alors que writeTimeout couvre toute la ligne. Ce détail
décide de tout ce qui suit, y compris du fait que les deux familles de réglages
doivent se choisir ensemble.
Comment borner un backend qui ne répond jamais sans couper une lenteur légitime ?
Section intitulée « Comment borner un backend qui ne répond jamais sans couper une lenteur légitime ? »Il faut fixer dialTimeout et responseHeaderTimeout dans le transport de
serveur, au-dessus de la réponse légitime la plus lente et en dessous de ce que
le client supporte. Sans responseHeaderTimeout, un backend qui accepte la
connexion puis se tait laisse le client attendre indéfiniment : Traefik n'a
aucune raison d'abandonner. Le lab part d'un transport nommé lent, réglé à 30
secondes sur les deux délais : un utilisateur devant un backend muet attend donc
jusqu'à une demi-minute avant de voir une erreur.
# FRAGMENT : à placer dans le fichier dynamique, services et routers non montréshttp: serversTransports: borne: forwardingTimeouts: dialTimeout: 3s responseHeaderTimeout: 5s services: api: loadBalancer: serversTransport: borneLe bloc déclare un transport nommé borne, puis le service api le cite dans
serversTransport. Les valeurs de 3 et 5 secondes sont celles de la mesure
ci-dessous, pas une recommandation : choisissez-les en face de votre réponse
légitime la plus lente, avec une marge. Un transport s'applique aux services qui
le citent, et à eux seuls : dans le lab, le service qui sert le flux SSE n'en
cite aucun et n'est donc pas touché.
Mesuré sur Traefik 3.7.15, avec ce transport : une réponse légitime de 1,5
seconde rend 200 en 1,50 s ; un backend qui dort rend 504 en 5,00 s,
exactement la borne ; une réponse qui prend 7 secondes rend aussi 504 en 5,00
s. Le 504 (Gateway Timeout) est le code que Traefik produit lui-même quand
le backend n'a pas répondu à temps.
Retenez la portée de responseHeaderTimeout : il mesure, une fois la
requête entièrement envoyée au backend, le temps jusqu'au début de la réponse,
c'est-à-dire ses en-têtes. Un backend qui envoie ses
en-têtes tout de suite puis distille le corps pendant des minutes n'est donc pas
visé par lui : ce n'est pas une borne d'inactivité sur le corps (un délai
qui repart à zéro à chaque octet reçu). Le cas d'un flux servi derrière un
responseHeaderTimeout court n'a pas été mesuré ici.
Pourquoi le délai d'écriture du point d'entrée coupe-t-il un flux SSE actif ?
Section intitulée « Pourquoi le délai d'écriture du point d'entrée coupe-t-il un flux SSE actif ? »writeTimeout est une durée totale accordée à l'écriture d'une réponse, pas
une borne d'inactivité : un flux SSE qui émet un événement par seconde est coupé
quand même, à l'échéance. Un SSE (Server-Sent Events) est une réponse
HTTP qui ne se termine pas et dans laquelle le serveur écrit des événements au
fil du temps. L'échéance est le moment où la durée accordée est écoulée. La
leçon sur les flux longs pose
qu'une borne de lecture repart à chaque octet, et qu'un battement (un petit
message envoyé à intervalle régulier pour montrer que la liaison vit) ne
protège pas d'une borne de durée totale. La première règle est vraie des bornes
d'inactivité, mais writeTimeout n'en est pas une, et c'est la différence
la plus trompeuse de cette leçon : la mesure ci-dessous est celle que la leçon
sur les flux longs cite.
Voici ce qui a été mesuré, avec un backend qui émet un événement par seconde, sans aucune inactivité.
writeTimeout du point d'entrée | Fenêtre d'observation | Événements reçus |
|---|---|---|
3s (réglage de départ du lab) | 8 s | 3 |
20s | 25 s | 20 |
0s (aucun délai) | 8 s | 8 |
Dans les deux premières lignes, le nombre d'événements est celui des secondes
écoulées jusqu'à l'échéance : le flux est fermé à writeTimeout, alors qu'il
n'a jamais été silencieux. Un battement placé dans le flux n'y change rien, car
il ne remet pas l'horloge à zéro. Dans la mesure, seule la valeur 0s, qui
supprime la protection, a laissé le flux vivre. Le scénario du lab parle d'une coupure « après une
courte période d'inactivité » ; la mesure montre une coupure à durée fixe.
Cette horloge unique a une conséquence sur les bornes du transport.
writeTimeout couvre aussi l'attente du backend, puisque l'horloge court
depuis la fin de la lecture des en-têtes de la requête jusqu'à la fin de
l'écriture de la réponse (c'est la définition de la documentation de Traefik).
Avec un writeTimeout de 3 secondes et un transport qui abandonne à 5, un
backend muet ne rend donc aucun 504 : le client voit la connexion se
fermer sans réponse (curl affiche le code 000 au bout de 5,00 s). Avec
ces mêmes réglages, une réponse légitime de 4 secondes subit le même sort, alors
que celle de 2 secondes passe. Les bornes du transport ne produisent plus leur
504 dès que writeTimeout est plus court : il faut les régler ensemble,
avec un writeTimeout supérieur à la plus longue attente que vous acceptez.
# FRAGMENT : fichier STATIQUE de Traefik, redémarrage nécessaireentryPoints: web: address: ":80" transport: respondingTimeouts: writeTimeout: 0sLe bloc supprime la borne pour le point d'entrée web. C'est le réglage qui a
laissé passer les huit événements sur huit dans la mesure, mais il retire aussi
la borne qui protège, entre autres, contre un client qui lit trop lentement (cet
effet n'a pas été mesuré). Fixer une valeur élevée
plutôt que zéro garde une protection, au prix d'un flux qui se ferme à
l'échéance : le client doit alors se reconnecter. Ce compromis est un
jugement, pas une règle : il dépend de la durée de vie que vous attendez d'un
flux. Comme writeTimeout se pose sur le point d'entrée, il vaut pour toutes les
routes qui l'empruntent ; sortir les flux sur un point d'entrée dédié est une
piste à envisager, que le lab ne met pas en œuvre.
Le WebSocket, une connexion HTTP qui bascule en canal où client et serveur
s'écrivent à tout moment, se comporte autrement. Après la réponse 101 (celle qui
accepte le changement de protocole), la connexion devient un tunnel : le
serveur HTTP de Go, sur lequel repose Traefik, efface les échéances de la
connexion au moment où Traefik la lui reprend (opération que Go nomme hijack).
Mesuré avec un writeTimeout de 3 secondes, un client
WebSocket qui attend 6 secondes avant d'envoyer son message reçoit bien son écho.
Dans cette mesure, writeTimeout n'a pas coupé le tunnel. Ne généralisez pas à
d'autres délais du point d'entrée, que cette leçon n'a pas mesurés.
Que mesure la sonde de santé, et à quelle vitesse retire-t-elle un backend ?
Section intitulée « Que mesure la sonde de santé, et à quelle vitesse retire-t-elle un backend ? »La sonde de santé (healthCheck) interroge périodiquement une route de chaque
backend et retire de la rotation (l'ensemble des backends entre lesquels Traefik répartit
les requêtes) celui qui ne répond pas correctement, mais pas avant son prochain
passage. Sans sonde, un backend malade continue d'en faire partie et
reçoit sa part de trafic. Dans le lab, l'un des deux backends API devient malade
quand on supprime un fichier : avec deux backends répartis à parts égales,
10 requêtes sur 20 échouent, alors que son pair est parfaitement sain.
# FRAGMENT : à placer sous loadBalancer, à côté de servershealthCheck: path: /health interval: 1s timeout: 1sLa sonde envoie une requête sur path, tous les interval, et attend la réponse
pendant timeout au plus. Sans réglage de status, tout code de 200 à 399
vaut « sain » (la documentation dit « entre 2XX et 3XX ») ; le 503 du backend
malade du lab est consigné par Traefik comme un échec (checking HTTP health: received error status code: 503). L'intervalle d'origine est de 30
secondes et le timeout d'origine de 5 secondes (documentation de Traefik
3.7) ; la mesure du lab concorde : deux échecs successifs apparaissent dans le
journal à 30 secondes d'écart. Le réglage par défaut laisse donc un backend malade en
rotation jusqu'à une demi-minute.
La fenêtre de détection se mesure en comptant les échecs juste après la panne : on envoie 20 requêtes courtes à un moment donné après la perte du backend, et on compte celles qui échouent.
| Réglage de la sonde | Moment de la mesure | Requêtes en échec sur 20 |
|---|---|---|
| aucune sonde | 2 s après la panne | 10 |
interval: 10s | 1 s après la panne | 10 |
interval: 10s | 12 s après la panne | 0 |
interval: 1s, timeout: 1s | 2 s après la panne | 0 |
Lisez la ligne de 10 secondes : tant que le prochain passage n'a pas eu lieu, le
backend est encore dans la rotation. Raccourcir l'intervalle rétrécit la fenêtre,
au prix de plus de requêtes de sonde vers chaque backend ; c'est un compromis de
charge, pas une règle absolue. La leçon Traefik avec Docker
explique la différence avec l'état unhealthy que Docker communique, et rappelle
qu'une sonde seule ne supprime pas l'erreur pendant la fenêtre.
Deux limites à garder en tête. La sonde ne voit que la route de santé, jamais
les routes métier : un backend dont /health répond 200 alors que ses autres
routes échouent reste dans la rotation. Elle ne redémarre pas non plus le
backend : elle ne modifie que le routage. Dans le lab, les routes de santé et
métier dépendent du même fichier, ce qui rend la mesure lisible mais ne prouve
rien sur un backend réel.
Comment empêcher qu'un retry compte une action deux fois ?
Section intitulée « Comment empêcher qu'un retry compte une action deux fois ? »Un middleware de retry (retry) rejoue une requête quand le backend n'a pas
répondu, et il ne doit pas rejouer une action qui écrit, à moins que cette action
soit idempotente. Une opération idempotente donne le même état final qu'on
l'exécute une fois ou dix. Une lecture l'est. Un POST qui incrémente un
compteur ne l'est pas, et son rejeu produit un doublon. Le retry (nouvelle
tentative) est utile contre une panne passagère, mais il ne sait pas si
l'écriture a déjà eu lieu avant la coupure.
Le lab recrée le piège. Le backend d'action incrémente un compteur, puis ferme
la connexion sans répondre : l'écriture est faite, la réponse est perdue. Avec le
réglage de départ du lab, qui contient retryNonIdempotentMethod: true, Traefik prend le
silence pour une panne et rejoue le POST. Mesuré : le compteur finit à 2
pour une seule action, et le client reçoit un 200. Le doublon est donc
invisible pour lui : seul le compteur du backend le révèle.
# FRAGMENT : middleware de retry sans le drapeau qui rejoue les écritureshttp: middlewares: retry-lecture: retry: attempts: 3Avec ce middleware, qui ne contient plus retryNonIdempotentMethod, la même
expérience rend un 502 au client et laisse le compteur à 1 : le POST
n'est pas rejoué. Le client voit une erreur honnête, au lieu d'un succès qui
cache un doublon. Une autre voie consiste à ne pas attacher le middleware au
router d'action : il ne rejouera alors rien de cette route. Le choix dépend du
fait que d'autres routes du même router méritent un retry, ce que le lab ne
mesure pas.
Un 502 reste pourtant une demi-réponse : le client ne sait pas plus
que Traefik si l'action a eu lieu. Pour qu'il puisse rejouer sans danger, c'est
l'application qui doit reconnaître une requête déjà traitée, par exemple avec
une clé d'idempotence fournie par le client. Cela relève du service, pas du
proxy, tout comme le battement d'un flux, que la leçon sur les
flux longs attribue au service.
Comment prouver chaque réglage depuis un client ?
Section intitulée « Comment prouver chaque réglage depuis un client ? »On prouve un réglage de délai en chronométrant une requête qui doit échouer ou
réussir, pas en relisant le fichier de configuration. Le code seul ne suffit
pas : un 000 de curl signifie qu'aucune réponse HTTP n'a été reçue (connexion
fermée, ou --max-time écoulé), et c'est un échec différent d'un 504. L'option -w de curl affiche à la fois le
code et la durée.
curl -s -o /dev/null -w '%{http_code} %{time_total}s\n' --max-time 40 \ -H 'Host: api.flux.lab.test' http://127.0.0.1/bloqueLa commande envoie la requête vers la route qui ne répond jamais, avec
l'en-tête Host (le nom de site demandé, que le router utilise pour reconnaître
la route). Avec le transport borné décrit plus haut et un writeTimeout assez
large, la sortie doit montrer 504 et une durée proche
de 5. Un 000 obtenu avant l'échéance de --max-time trahit au contraire, très
probablement, une coupure par le point d'entrée : Traefik n'a plus pu écrire sa
réponse (c'est une déduction de la sémantique de writeTimeout, seul le 000
est mesuré). Le journal d'accès, décrit dans
Observer Traefik, donne le même
renseignement dans son champ Duration.
| Ce que vous voulez prouver | Mesure | Lecture |
|---|---|---|
| Une lenteur légitime passe | /lent?ms=1500, --max-time 4 | 200 en environ 1,5 s |
| Un backend muet échoue vite | /bloque, --max-time 10 | 504 (ou 502 si le backend coupe la connexion) avant 10 s |
| Un flux vit au-delà de l'échéance | curl -N --max-time 5 sur /events, comptez les lignes data: | environ un événement par seconde |
| Un backend malade est retiré | 20 requêtes courtes après la panne | 0 échec |
Un POST n'est pas rejoué | GET /compteur après une action perdue | 1 |
Pour la ligne du flux, curl -N désactive la mise en tampon côté client : sans
elle, c'est curl qui retarderait l'affichage et accuserait à tort Traefik. Une
mesure qui passe une fois ne prouve pas la robustesse : pour juger un
writeTimeout, laissez le flux ouvert plus longtemps que l'échéance et comptez.
Que valide le lab, et que laisse-t-il à votre jugement ?
Section intitulée « Que valide le lab, et que laisse-t-il à votre jugement ? »Le lab valide des comportements mesurés depuis le client, pas des directives
précises. Plusieurs vérifications sont lancées sur la machine du lab : une attente
légitime de 1,5 seconde réussit en moins de 4 secondes ; un backend muet échoue
par un 502 ou un 504 en moins de 10 secondes ; le flux SSE délivre assez
d'événements sur une fenêtre de cinq secondes ; le WebSocket renvoie son écho ;
aucune des 20 requêtes courtes n'échoue pendant qu'un backend est malade ; et un
POST dont la réponse est perdue ne compte qu'une fois.
Ce que le lab ne mesure pas compte autant. Il n'exige aucune valeur précise de
délai, ne vérifie pas que vous gardez une protection plutôt que de supprimer
writeTimeout, et n'attend pas le WebSocket après une période d'inactivité : son
client est lancé sans attente. Il ne dit rien non plus de la meilleure
organisation entre un seul point d'entrée et un point d'entrée dédié aux flux.
Cette part est la vôtre : relisez vos valeurs en vous demandant si elles
tiendraient face à votre trafic réel, pas seulement face au test.
Mettre en pratique
Section intitulée « Mettre en pratique »Le lab vous donne un Traefik dont quatre réglages sont faux : un transport trop généreux, un délai d'écriture trop court pour un flux, une absence de sonde de santé et un retry qui enveloppe une action non idempotente. Vous corrigez le fichier de routes et le fichier statique, redémarrez Traefik quand c'est nécessaire, puis laissez la validation envoyer ses requêtes depuis un client. Elle vérifie les comportements décrits dans la section précédente, pas la forme de votre configuration.
À retenir
Section intitulée « À retenir »Un Traefik fiable se règle tronçon par tronçon et se prouve par la mesure.
- Les bornes Traefik vers backend se posent dans un
serversTransport; celles du client vers Traefik dans le point d'entrée, en configuration statique. responseHeaderTimeoutmesure l'attente des en-têtes, pas celle du corps ; un backend muet se borne avec lui, et rend un504à l'échéance.writeTimeoutest une durée totale, pas une inactivité : un flux SSE actif est coupé à l'échéance, et la valeur0ssupprime la borne.- Un
writeTimeoutplus court que les bornes du transport empêche Traefik de produire son504: le client reçoit une connexion fermée. - Une sonde de santé n'agit qu'à son prochain passage : l'intervalle d'origine est de 30 secondes, et l'intervalle fixe la fenêtre de détection. Elle ne voit que sa route de santé et ne redémarre rien.
retryNonIdempotentMethod: truerejoue unPOSTdont la réponse s'est perdue : le compteur monte à 2 alors que le client reçoit un200. Sans ce drapeau, le client reçoit un502et le compteur reste à 1.- Dans la mesure faite, un WebSocket n'est pas coupé par
writeTimeoutaprès la réponse101, ce qui ne se généralise pas aux autres délais du point d'entrée. - Un réglage se prouve avec
curl -w '%{http_code} %{time_total}s', pas en relisant la configuration.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Ces questions portent sur la portée de chaque délai, sur la fenêtre de détection d'une sonde et sur le rejeu d'une écriture.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Protéger le point d'entrée Traefik
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications