Les sessions persistantes épinglent un client à un serveur backend précis pour la durée d’une session logique. Parfois c’est nécessaire (uploads avec état, reconnexions WebSocket vers le même nœud, applications héritées sans magasin de session partagé) ; souvent c’est un contournement pour combler l’absence d’une couche d’état partagée. Cet article couvre les trois mécanismes de persistance de HAProxy et les mises en garde de production pour chacun.
Comment vérifier
Pour une config existante, l’API d’exécution expose les stick tables actives et les cookies que HAProxy insère.
echo "show table be_app" | sudo socat /run/haproxy/admin.sock -
echo "show servers state" | sudo socat /run/haproxy/admin.sock -
curl -v http://localhost/ 2>&1 | grep -i 'set-cookie'
curl -v -b "SRVID=app1" http://localhost/ 2>&1 | grep -i 'x-served-by\|cookie'
La sortie show table liste chaque clé que la table contient actuellement (IP source, valeur de cookie) avec le serveur vers lequel elle pointe. Le curl verbeux confirme si HAProxy insère le cookie et vers quel serveur il a routé le client.
Ce qui se passe
HAProxy supporte trois approches :
- Directive
cookie(HTTP uniquement) — HAProxy insère ou réécrit un cookie identifiant le serveur backend. Les requêtes suivantes du même client portent le cookie et HAProxy route en conséquence. stick-table+stick on(HTTP ou TCP) — HAProxy maintient une table en mémoire indexée par un attribut de la requête (IP source, valeur de cookie, valeur d’en-tête) qui pointe vers un serveur. Survit à travers plusieurs requêtes du même clé.balance source(TCP ou HTTP) — un hash de l’IP client choisit le serveur. Pas d’état ; rééquilibre naturellement quand des serveurs joignent ou partent.
cookie est le défaut de production pour HTTP parce qu’il survit aux changements d’IP du client (réseaux mobiles, NAT) et est explicite — vous voyez le cookie dans DevTools. stick-table est ce qu’on utilise quand on ne peut pas insérer un cookie (frontends TCP, clients tiers qui retirent les cookies). balance source est le repli quand on n’a aucun des deux.
Le gros compromis : la persistance épingle un client à un serveur, ce qui signifie qu’un serveur malade continue de recevoir les requêtes de ses clients épinglés jusqu’à passer DOWN. L’option redispatch existe précisément pour ça ; sans elle, une session persistante attachée à un serveur mort renvoie des erreurs pour toujours.
La procédure
-
Persistance par cookie. Insérez un cookie nommant le serveur, scope-le au chemin, et laissez HAProxy réécrire la sélection du serveur :
backend be_app balance roundrobin cookie SRVID insert indirect nocache httponly secure option httpchk http-check send meth GET uri /healthz http-check expect status 200 server app1 10.0.1.11:8080 check cookie app1 server app2 10.0.1.12:8080 check cookie app2Le
cookie app1sur la ligne server est la valeur que HAProxy écrit dans le cookieSRVIDquand ce serveur est choisi. Les flagsinsert indirect nocache httponly securesont la combinaison de production : insert signifie que HAProxy ajoute le cookie, indirect signifie que HAProxy le retire de la requête avant de la transmettre (le backend ne le voit pas), nocache empêche la survie en caches, httponly empêche l’accès JS, secure exige HTTPS. -
Stick-table par IP source. Pour les frontends TCP ou quand l’insertion de cookie n’est pas possible :
backend be_app balance leastconn stick-table type ip size 100k expire 30m stick on src server app1 10.0.1.11:8080 check server app2 10.0.1.12:8080 checksize 100kest le nombre maximum d’entrées ;expire 30mest le timeout d’inactivité par entrée. La table vit en mémoire seulement — voir Stick tables HAProxy pour la persistance à travers les reloads et Protocole peer HAProxy pour la réplication entre instances. -
Stick sur une valeur d’en-tête. Quand l’identité du client est dans un en-tête personnalisé :
backend be_app stick-table type string len 64 size 50k expire 1h stick on hdr(x-tenant-id) server app1 10.0.1.11:8080 check server app2 10.0.1.12:8080 checktype string len 64alloue 64 octets par clé ; le dimensionnement compte parce que la table est préallouée. -
balance source — sans état. Un hash de l’IP source choisit le serveur :
backend be_app balance source hash-type consistent server app1 10.0.1.11:8080 check server app2 10.0.1.12:8080 checkhash-type consistentutilise un anneau de type Karger afin qu’ajouter ou retirer un serveur ne mélange que ~1/N des clients. Sansconsistent, chaque client est mélangé quand le pool change. -
Combiner cookie et redispatch. Le motif sûr en production : épingler les clients à un serveur, mais redispatcher si ce serveur est en panne :
defaults option redispatch retries 3 backend be_app cookie SRVID insert indirect nocache httponly secure option httpchk http-check send meth GET uri /healthz http-check expect status 200 server app1 10.0.1.11:8080 check cookie app1 server app2 10.0.1.12:8080 check cookie app2option redispatchest ce qui vous sauve quand un client coincé essaie d’atteindre une épingle morte : HAProxy abandonne après les retries et route vers un serveur sain. -
Persister la stick-table à travers les reloads. Sans persistance, un
systemctl reload haproxyvide la table :backend be_app stick-table type ip size 100k expire 30m peers cluster_peers stick on src server app1 10.0.1.11:8080 check server app2 10.0.1.12:8080 checkLa clause
peers cluster_peersréplique la table vers les peers. Au reload, HAProxy tire l’état depuis un peer.
Pièges courants
- Une session persistante sans
option redispatchcontinue d’essayer le serveur mort pour toujours ; le client voit des erreurs jusqu’à l’expiration du cookie. - La persistance par cookie avec
insertréécrit l’en-têteSet-Cookie— si le backend définit déjàSRVIDpour ses propres besoins, vous entrez en collision. Utilisez un nom de cookie unique. balance sourcesanshash-type consistentmélange tous les clients à chaque échange de serveur. Avec l’autoscaling, c’est une horde tonitruante contre les caches froids.- Les stick tables sont par processus — sans
peers, un cluster HAProxy multi-instances a N tables indépendantes. Le client dont l’état vit sur l’instance A mais qui touche l’instance B à la prochaine requête est mélangé. - Le cookie que HAProxy insère est HttpOnly et Secure par bonne pratique, mais si l’app lit explicitement son propre cookie de session et que HAProxy en insère aussi un, vos logs de debug se rempliront du mauvais cookie. Utilisez des noms distincts.
Stack Harbor recommande de passer à un magasin de session partagé (Redis, base de données, JWT) en première option et de traiter les sessions persistantes comme le repli pour les applications héritées. Quand la persistance est nécessaire, on déploie avec la réplication peers afin qu’un reload ou la perte d’une instance n’unsticke pas tout le monde d’un coup. Cette continuité fait partie de la façon dont on opère les Environnements en grappe.