Pour qu'HAProxy 3.4 serve une API avec des flux longs sans les couper ni rejouer une écriture, donnez à chaque phase lente sa propre borne de temps, réglez le silence toléré sur le battement de l'application, collez les sessions par cookie et limitez les réessais aux requêtes qui n'ont jamais atteint le serveur. Cette leçon s'adresse à qui publie déjà un service derrière HAProxy et veut qu'à trois heures du matin, le temps d'une erreur dise quelle phase est lente. Elle ne redit ni le routage, ni la santé des serveurs, ni les limites par client : elle traite les délais, les flux longs, l'affinité de session et le rejeu.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »La leçon part d'un proxy dont toutes les bornes valent la même chose, et arrive à un proxy dont chaque durée a un sens.
- Distinguer les quatre bornes
timeout connect,timeout queue,timeout serverettimeout tunnel, et la phase que chacune surveille. - Régler des bornes différentes pour que le temps écoulé désigne la phase.
- Tenir un flux d'événements SSE et un WebSocket à travers les silences.
- Choisir une politique de session et dire ce qu'elle ne garantit pas.
- Empêcher qu'une réponse perdue fasse écrire deux fois la même donnée, sans priver les lectures de leurs réessais.
Prérequis
Section intitulée « Prérequis »La leçon suppose un HAProxy 3.4 qui tourne et un compte sudo. Les
versions de référence sont celles du lab (HAProxy 3.4.7, Debian 13). Les
extraits de configuration de cette page montrent la forme de la correction, pas
une configuration à recopier telle quelle : à vous d'adapter les valeurs.
- Lire une configuration HAProxy (
defaults,frontend,backend) : Installer HAProxy et lire ses sections de configuration. - Router vers plusieurs backends par ACL : Router par noms et chemins avec ACL et maps.
- Savoir ce qu'est une sonde de santé : Choisir la répartition et mesurer les sondes.
- Interroger le socket d'administration, car le lab retire un serveur du service par ce socket : Modifier l'état de HAProxy à chaud avec des droits limités.
Que traite cette leçon, et que laisse-t-elle aux leçons voisines ?
Section intitulée « Que traite cette leçon, et que laisse-t-elle aux leçons voisines ? »Cette leçon règle le temps, la durée de vie des flux et le rejeu ; les leçons voisines gardent le reste. Les limites de débit par client avec les stick-tables vivent dans Limiter les abus avec les stick-tables de HAProxy. La répartition, les sondes et le drainage d'un serveur sans couper les flux sont dans Choisir la répartition et mesurer les sondes, et la perte d'un nœud d'entrée dans Tester la disponibilité du point d'entrée HAProxy. La lecture des codes de terminaison du journal est dans Diagnostiquer HAProxy sans masquer la cause.
Ici, une affinité de session (faire revenir un client sur la même instance) ne sert qu'à un point : montrer ce qu'elle ne remplace pas. Le protocole gRPC, qui demande d'autres réglages, n'est pas traité. Pour voir les mêmes flux longs côté Nginx, lisez Nginx : publier WebSocket, SSE et gRPC sans coupure.
De quoi part-on, et que corrige-t-on ?
Section intitulée « De quoi part-on, et que corrige-t-on ? »Le lab part d'une API à deux instances, a et b, derrière un HAProxy dont
quatre choses sont fausses. Les deux instances gardent leurs sessions dans
leur propre mémoire. Un flux d'événements SSE (Server-Sent Events, un flux
HTTP où le serveur pousse des événements au fil de l'eau) et un WebSocket
(une connexion HTTP « basculée » en canal bidirectionnel, à la demande du
client par l'en-tête Upgrade) complètent
l'application. Le battement du flux, c'est-à-dire l'intervalle entre deux
événements, est volontairement lent : 12 secondes.
Les quatre défauts : toutes les bornes valent 10 secondes et il n'y a pas de
borne de file ; dix secondes d'inactivité coupent le flux et le WebSocket,
faute de borne de tunnel ; retry-on all-retryable-errors rejoue tout ce qui
peut l'être ; aucune politique de session n'existe, donc une requête sur
deux change d'instance. Un fichier recette.txt donne les valeurs attendues
(2, 10, 20 et 60 secondes), sans dire quelles directives écrire.
Comment une borne par phase dit-elle quelle phase est lente ?
Section intitulée « Comment une borne par phase dit-elle quelle phase est lente ? »Quand chaque phase a une borne différente, la durée d'une erreur identifie la
phase en cause, sans lire le moindre journal. Une requête traverse trois
phases lentes en série. Elle attend d'abord une place dans la file du
backend, bornée par timeout queue. Elle établit ensuite la connexion TCP
vers le serveur, bornée par timeout connect. Elle attend enfin la réponse,
bornée par timeout server.
Regardez le schéma de gauche à droite : chaque boîte porte la directive
qui borne la phase, et le tunnel n'existe qu'après un Upgrade accepté. Si les
trois phases partagent la même valeur, une adresse qui ne répond pas, une file
pleine et une réponse lente rendent à peu près le même signal au bout du
même temps, de l'ordre de dix secondes. Sans timeout queue, HAProxy
applique d'ailleurs à la file la borne de timeout connect, ce qui confond une
file pleine et un serveur injoignable.
La recette du lab fixe donc timeout connect 2s, timeout queue 10s et
timeout server 20s, avec deux tentatives de connexion, soit cinq
secondes environ pour une adresse injoignable, 10 s pour une file pleine
et 20 s pour une réponse trop lente. Ces valeurs sont celles de l'exercice,
pas des recommandations pour votre trafic.
defaults mode http timeout connect 2s timeout queue 10s timeout server 20s timeout client 30s timeout tunnel 60s retries 1Le bloc rassemble les bornes du lab. Avec retries 1, HAProxy tente la
connexion une fois, puis une fois de plus : deux tentatives. Le total n'est
pourtant pas le double de la borne, car le manuel ajoute entre deux essais sur
le même serveur une pause valant min(timeout connect, 1 s). Ici, cela fait
2 s, puis 1 s d'attente, puis 2 s : cinq secondes et non quatre. C'est le
genre d'écart qu'on ne retrouve qu'en chronométrant, jamais en lisant la
configuration. La valeur de timeout client (30 s ici), qui borne
l'inactivité côté client, est un exemple que le lab ne mesure pas. Pour vérifier, forcez chaque phase l'une
après l'autre et chronométrez, ici pour la connexion :
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' --max-time 40 \ http://<proxy>/injection/connexionLa sortie affiche le code HTTP puis la durée en secondes, qui doit approcher cinq. Le test du lab, lui, se contente d'exiger que les trois durées soient séparées d'au moins trois secondes : c'est la distinction entre les phases qu'il mesure, pas la valeur exacte de chacune.
Le code HTTP est la seconde moitié du diagnostic. Le test du lab exige un 504 quand la réponse dépasse sa borne, ce qui dit que le serveur a renoncé côté réponse. Pour la connexion et la file, la documentation d'HAProxy décrit un 503 ; le lab ne le mesure pas et ne retient que la durée.
Comment tenir un flux SSE ou un WebSocket à travers les silences ?
Section intitulée « Comment tenir un flux SSE ou un WebSocket à travers les silences ? »timeout server mesure une inactivité, pas une durée totale : un flux SSE
survit tant que le silence entre deux événements reste plus court que cette
borne, et un WebSocket relève de timeout tunnel. La borne repart à zéro à
chaque octet reçu. Avec un battement de 12 secondes, un timeout server 10s
coupe un flux pourtant sain ; timeout server 20s le laisse vivre aussi
longtemps qu'il bat.
Le réglage se lit donc en face du battement de l'application, pas en face
de la durée du flux. Demander un flux de 30 secondes n'oblige pas à une borne de
30 secondes : il suffit que timeout server dépasse le silence le plus long
que l'application produit. C'est ce qui évite de garder ouverte, pour rien, une
connexion dont le serveur est mort.
Le WebSocket est un autre cas. Après la réponse 101 Switching Protocols, la
connexion basculée devient un tunnel : HAProxy relaie les octets sans plus
interpréter le HTTP. C'est alors timeout tunnel qui décide de la durée
d'un silence tolérable, et aucune autre directive ne la remplace. Une fenêtre de
discussion ouverte sans personne qui écrit n'échange rien pendant des minutes :
ce silence est normal, et timeout tunnel 60s le couvre au-delà des 30 secondes
du test.
Pour vérifier, le test joue les deux flux contre le proxy. Le flux SSE doit rendre plusieurs événements horodatés, espacés du battement, sur 30 secondes. Le WebSocket renvoie son premier message, reste muet 30 secondes, puis doit encore répondre. Si le dernier échange manque, c'est la borne de tunnel qui a fermé la connexion.
Un dernier point : timeout tunnel ne lève pas toutes les limites. Il ne
remplace ni timeout client, ni les bornes d'une requête HTTP ordinaire, et
une valeur trop grande garde des connexions mortes ouvertes. Fixez-la à la
durée de silence que vous acceptez vraiment.
Comment coller une session à une instance, et jusqu'où cela tient-il ?
Section intitulée « Comment coller une session à une instance, et jusqu'où cela tient-il ? »Un cookie d'affinité posé par HAProxy renvoie un client sur l'instance qui
détient sa session, mais il ne copie pas la session : retirer l'instance la
perd. Quand les sessions vivent dans la mémoire de chaque instance, un
balance roundrobin envoie la lecture suivante sur l'autre instance, qui ne
connaît pas la session. Le lab le montre : sans politique, une lecture sur deux
échoue.
La forme la plus simple tient dans le backend. HAProxy y ajoute un cookie
nommé SERVEUR, dont la valeur désigne le serveur choisi, et chaque server
porte la valeur qui lui correspond.
backend applicatif balance roundrobin cookie SERVEUR insert indirect server a 127.0.0.1:18181 check cookie a server b 127.0.0.1:18182 check cookie bLe mot insert fait écrire le cookie par HAProxy, et indirect le retire avant
de transmettre la requête à l'application, qui n'a donc rien à savoir de lui. Le
mot-clé httponly est volontairement absent : sans lui, le cookie ne porte
pas l'attribut HttpOnly (qui interdit au JavaScript de la page de lire le
cookie) et un script client peut le lire. Pour un navigateur réel,
ajoutez httponly et relisez ces attributs avec votre politique de cookies.
Pour vérifier, ouvrez une session avec curl -c biscuits puis relisez vingt fois
avec curl -b biscuits : chaque lecture doit rendre 200.
Une autre clé existe, l'adresse source (balance source). Elle tient aussi le
test, mais derrière un NAT d'entreprise tous les clients partagent une adresse
et une seule instance reçoit tout. Le cookie colle un client, l'adresse
colle un réseau.
La limite se mesure ensuite : le test retire l'instance qui détient la session
du service, avec set server applicatif/<nom> state maint sur le socket, et
relit. La réponse attendue est un 409, session inconnue, parce que
l'autre instance n'a rien. Ce n'est pas un défaut à corriger : coller n'est
pas répliquer. Chaque mise à jour tournante qui vide une instance déconnecte
ses sessions en cours. Pour les garder, il faut sortir l'état de l'instance, vers
une base ou un cache partagé. Ce choix appartient à l'application, pas au proxy.
Comment empêcher qu'une réponse perdue soit écrite deux fois ?
Section intitulée « Comment empêcher qu'une réponse perdue soit écrite deux fois ? »Il faut que HAProxy ne rejoue une requête que si elle n'a jamais atteint le
serveur : retry-on conn-failure suffit pour cela, alors que
all-retryable-errors rejoue aussi les écritures dont la réponse s'est perdue.
Un rejeu (retry) renvoie la même requête à un serveur après un échec. Pour
une lecture, c'est sans danger. Pour une écriture non idempotente, une
requête dont deux exécutions ne produisent pas le même état qu'une seule, c'est
un doublon.
Dans le lab, le point d'écriture /ecrire compte l'écriture dans son compteur
métier, puis ferme la connexion sans répondre. Le travail a eu lieu, mais le
client n'a rien reçu. Avec retry-on all-retryable-errors, HAProxy voit une
connexion fermée sans réponse, croit à une panne et renvoie la requête : le
compteur passe à deux. Le client, lui, ne peut pas le savoir : seul le backend
dit combien de fois le travail a eu lieu.
defaults retries 1 retry-on conn-failureconn-failure est la valeur par défaut de retry-on : elle ne rejoue qu'une
connexion qui a échoué avant d'atteindre le serveur, donc avant tout travail.
On peut aussi dire la règle par la méthode avec
http-request disable-l7-retry if METH_POST, qui interdit le rejeu d'un POST
au niveau applicatif. Les deux gestes ne s'excluent pas.
La moitié qui compte vient après. Restreindre les réessais ne revient pas à
les supprimer. Dans le backend bascule, un serveur est mort et un autre est
sain, avec option redispatch. Une lecture dont la connexion échoue doit
encore atteindre le serveur qui répond ; c'est ce que le test vérifie sur six
lectures. Une configuration qui supprimerait tout rejeu gagnerait en sûreté en
perdant sa tolérance aux pannes.
Pour vérifier l'écriture, comptez avant et après une seule tentative, par les compteurs métier des deux instances : la différence doit valoir exactement un.
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, pas des directives ni le fond d'un texte. C'est pourquoi une page honnête dit aussi ce qu'il ne mesure pas.
- Il mesure trois durées jouées séparément (connexion, file, réponse), distinctes de trois secondes au moins et proches de leur borne, plus le code 504 pour la réponse lente.
- Il mesure le flux SSE sur 30 secondes, et le WebSocket qui répond encore après 30 secondes de silence.
- Il mesure vingt lectures de session réussies, puis le 409 une fois l'instance retirée.
- Il mesure un compteur d'écriture égal à un après une tentative, et six lectures réussies sur le backend à serveur mort.
- Il ne mesure pas la valeur exacte de
timeout tunnelou detimeout client, ni le code d'erreur de la file ou de la connexion, ni quelle directive vous employez pour coller la session. - Il lit le dossier de limites par recherche de mots-clés, avec une taille minimale : c'est un garde-fou, pas une preuve de compréhension. Écrivez-le pour un exploitant qui fera une mise à jour tournante demain.
Mettre en pratique
Section intitulée « Mettre en pratique »Le lab vous donne le proxy fautif et vous demande de le corriger : bornes distinctes pour la connexion, la file et la réponse, tenue du flux SSE et du WebSocket, cookie de session, puis réessais restreints sans perdre ceux des lectures. Vous rédigez aussi un court dossier qui dit ce qu'il advient d'une session quand son instance quitte le service. La validation porte sur le comportement : elle chronomètre les trois phases, rejoue les flux, relit vingt fois la session avant de retirer son instance, compte les écritures d'une tentative et relit le backend à serveur mort. La rédaction du dossier n'est vérifiée que par des mots-clés.
À retenir
Section intitulée « À retenir »- Trois phases lentes, trois bornes différentes : le temps écoulé désigne la
phase, la connexion (
timeout connect), la file (timeout queue) ou la réponse (timeout server). - Sans
timeout queue, l'attente en file retombe sur la borne de connexion et se confond avec un serveur injoignable. timeout serverest une borne d'inactivité : réglez-la au-dessus du battement de l'application, pas de la durée du flux.- Un WebSocket basculé relève de
timeout tunnel, qu'aucune autre directive ne remplace. - Un cookie d'affinité colle un client à une instance ; coller n'est pas répliquer, et retirer l'instance perd la session.
retry-on all-retryable-errorsrejoue une écriture dont la réponse s'est perdue ;conn-failurene rejoue que ce qui n'a pas atteint le serveur.- Restreindre les réessais n'est pas les supprimer : une lecture doit encore atteindre le serveur sain.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Ces questions portent sur la phase que chaque borne surveille et sur ce qu'un rejeu fait à une écriture. Savoir citer les quatre directives ne suffit pas : il faut prévoir ce que le proxy fera avant de le mesurer.
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 : Modifier l'état de HAProxy à chaud
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications