J'utilise un serveur d'équilibrage de charge HAProxy pour équilibrer la charge sur plusieurs serveurs Apache. Je dois recharger HAProxy à tout moment pour modifier l'algorithme d'équilibrage de charge.
Tout cela fonctionne bien, à part le fait que je dois recharger le serveur sans perdre un seul paquet (pour le moment, un rechargement me donne un taux de réussite de 99,76% en moyenne, avec 1000 demandes par seconde pendant 5 secondes). J'ai effectué de nombreuses heures de recherche à ce sujet et j'ai trouvé la commande suivante pour "recharger gracieusement" le serveur HAProxy:
haproxy -D -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)
Toutefois, cela n’a que peu ou pas d’effet par rapport à l’ancien service haproxy reload
, il reste en baisse de 0,24% en moyenne.
Existe-t-il un moyen de recharger le fichier de configuration HAProxy sans qu'un seul paquet ne soit abandonné par un utilisateur?
Réponses:
Selon https://github.com/aws/opsworks-cookbooks/pull/40 et par conséquent http://www.mail-archive.com/[email protected]/msg06885.html, vous pouvez:
la source
iptables v1.4.14: invalid port/service
--syn 'spécifié`$PORT
par le port actuelhaproxy
sur lequel vous écoutez. Si haproxy écoute sur plusieurs ports, écriture remplacer--dport $PORT
avec--dports $PORTS_SEPARATED_BY_COMMAS
, par exemple,--dports 80,443
.Yelp partageait une approche plus sophistiquée basée sur des tests méticuleux. L'article du blog est une plongée profonde, et vaut bien l'investissement de temps pour l'apprécier pleinement.
Recharges d'HAProxy avec True Zero Downtime
tl; dr utilise Linux tc (contrôle du trafic) et iptables pour mettre temporairement en file d'attente les paquets SYN pendant le rechargement de HAProxy et a deux PID attachés au même port (
SO_REUSEPORT
).Je ne suis pas à l'aise de republier l'intégralité de l'article sur ServerFault; néanmoins, voici quelques extraits pour piquer votre intérêt:
Gist: https://gist.github.com/jolynch/97e3505a1e92e35de2c0
Bravo à Yelp pour le partage de ces idées étonnantes.
la source
Il existe un autre moyen beaucoup plus simple de recharger haproxy avec un temps d'indisponibilité égal à zéro - il s'appelle iptables flipping (l'article est en fait une réponse sans rebond à la solution Yelp). Il est plus propre que la réponse acceptée car il n’est pas nécessaire de supprimer les paquets susceptibles de causer des problèmes lors de recharges longues.
En bref, la solution comprend les étapes suivantes:
iptable
commandes simples .De plus, la solution peut être adaptée à tout type de service (nginx, apache, etc.) et est plus tolérante aux pannes, car vous pouvez tester la configuration de veille avant sa mise en ligne.
la source
Edit: Ma réponse part du principe que le noyau n’envoie que du trafic au dernier port à ouvrir avec SO_REUSEPORT, alors qu’il envoie en réalité du trafic à tous les processus, comme décrit dans l’un des commentaires. En d'autres termes, la danse iptables est toujours requise. :(
Si vous utilisez un noyau prenant en charge SO_REUSEPORT, le problème ne devrait pas se produire.
Le processus suivi par haproxy lors du redémarrage est le suivant:
1) Essayez de définir SO_REUSEPORT lors de l’ouverture du port ( https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5dbf388e5fb838e120f1b0e9361a92f8/src/proto_tcp.c#L792-L798 ).
2) Essayez d’ouvrir le port (réussira avec SO_REUSEPORT)
3) Si cela n’a pas abouti, signalez à l’ancien processus de fermer son port, attendez 10 ms et essayez à nouveau. ( https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5dbf388e120f1b0e9361a92f8/src/haproxy.c#L1554-L1577 )
Il a été supporté pour la première fois dans le noyau Linux 3.9, mais certaines distributions l’ont rétroporté. Par exemple, les noyaux EL6 des versions 2.6.32-417.el6 le prennent en charge.
la source
SO_REUSEPORT
dans certains cas de figure - en particulier dans des conditions de trafic intense. Lorsque SYN est envoyé à l'ancien processus haproxy et au même moment, il ferme le socket d'écoute, ce qui entraîne RST. Voir l'article de Yelp mentionné dans l'autre réponse ci-dessus.Je vais expliquer ma configuration et comment j'ai résolu les recharges gracieuses:
J'ai une configuration typique avec 2 nœuds sous HAproxy et keepalived. Keepalived suit l'interface dummy0, afin que je puisse faire un "ifconfig dummy0 down" pour forcer le basculement.
Le vrai problème est que, je ne sais pas pourquoi, un "rechargement haproxy" laisse toujours tomber toutes les connexions ESTABLISHED :( j'ai essayé le "retournement iptables" proposé par gertas, mais j'ai trouvé quelques problèmes parce qu'il effectue un NAT sur la destination L'adresse IP, qui n'est pas une solution appropriée dans certains scénarios.
Au lieu de cela, j'ai décidé d'utiliser un hack sale CONNMARK pour marquer les paquets appartenant à de nouvelles connexions, puis de rediriger ces paquets marqués vers l'autre nœud.
Voici le jeu de règles iptables:
Les deux premières règles marquent les paquets appartenant aux nouveaux flux (123.123.123.123 est le VIP keepalived utilisé sur haproxy pour lier les interfaces).
Les troisième et quatrième règles marquent les paquets FIN / RST. (Je ne sais pas pourquoi, la cible TEE "ignore" les paquets FIN / RST).
La cinquième règle envoie une copie de tous les paquets marqués à l'autre HAproxy (192.168.0.2).
La sixième règle supprime les paquets appartenant aux nouveaux flux afin d’empêcher d’atteindre leur destination initiale.
N'oubliez pas de désactiver rp_filter sur les interfaces ou le noyau abandonnera ces paquets martiens.
Et enfin, attention aux paquets qui reviennent! Dans mon cas, il y a un routage asymétrique (les demandes arrivent au client -> haproxy1 -> haproxy2 -> serveur Web, et les réponses vont du serveur Web -> haproxy1 -> client), mais cela n’affecte pas. Ça fonctionne bien.
Je sais que la solution la plus élégante serait d’utiliser iproute2 pour faire le renvoi, mais cela ne fonctionnait que pour le premier paquet SYN. Quand il a reçu l'ACK (3ème paquet de la poignée de main à trois), il ne l'a pas marqué :( Je ne pouvais pas passer beaucoup de temps à enquêter, dès que j'ai vu que ça fonctionne avec TEE target, il l'a laissé là. Bien sûr, n'hésitez pas à l'essayer avec iproute2.
En gros, le "rechargement gracieux" fonctionne comme ceci:
Le jeu de règles IPtables peut être facilement intégré dans un script de démarrage / arrêt:
la source