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:
iptables -t mangle -A PREROUTING -i eth1 -d 123.123.123.123/32 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
iptables -t mangle -A PREROUTING -i eth1 -m mark --mark 1 -j DROP
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:
- J'active le jeu de règles iptables et vois immédiatement les nouvelles connexions passer à l'autre HAproxy.
- Je garde un œil sur "netstat -an | grep ESTABLISHED | wc -l" pour superviser le processus de "vidange".
- Une fois qu'il n'y a plus que quelques (ou zéro) connexions, "ifconfig dummy0 down" pour forcer Keepalived à basculer, de sorte que tout le trafic passe sur l'autre HAproxy.
- Je supprime le jeu de règles iptables
- (Uniquement pour la configuration keepalive "non-préemptive") "ifconfig dummy0 up".
Le jeu de règles IPtables peut être facilement intégré dans un script de démarrage / arrêt:
#!/bin/sh
case $1 in
start)
echo Redirection for new sessions is enabled
# echo 0 > /proc/sys/net/ipv4/tcp_fwmark_accept
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $f; done
iptables -t mangle -A PREROUTING -i eth1 ! -d 123.123.123.123 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
iptables -t mangle -A PREROUTING -i eth1 -m mark --mark 1 -j DROP
;;
stop)
iptables -t mangle -D PREROUTING -i eth1 -m mark --mark 1 -j DROP
iptables -t mangle -D PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
iptables -t mangle -D PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
iptables -t mangle -D PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
iptables -t mangle -D PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -D PREROUTING -i eth1 ! -d 123.123.123.123 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
echo Redirection for new sessions is disabled
;;
esac