Explicaré mi configuración y cómo resolví las recargas graciosas:
Tengo una configuración típica con 2 nodos que ejecutan HAproxy y keepalived. Keepalived rastrea la interfaz dummy0, por lo que puedo hacer un "ifconfig dummy0 down" para forzar el cambio.
El verdadero problema es que, no sé por qué, una "recarga de haproxy" todavía deja caer todas las conexiones ESTABLECIDAS :( Intenté el "volteo de iptables" propuesto por gertas, pero encontré algunos problemas porque realiza un NAT en el destino Dirección IP, que no es una solución adecuada en algunos escenarios.
En cambio, decidí usar un truco sucio CONNMARK para marcar paquetes que pertenecen a NUEVAS conexiones, y luego redirigir esos paquetes marcados al otro nodo.
Aquí está el conjunto de reglas de 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
Las primeras dos reglas marcan los paquetes que pertenecen a los nuevos flujos (123.123.123.123 es el VIP de keepalived utilizado en el haproxy para enlazar las interfaces).
Las reglas tercera y cuarta marcan los paquetes FIN / RST. (No sé por qué, el objetivo TEE "ignora" los paquetes FIN / RST).
La quinta regla envía un duplicado de todos los paquetes marcados al otro HAproxy (192.168.0.2).
La sexta regla descarta los paquetes que pertenecen a nuevos flujos para evitar llegar a su destino original.
Recuerde deshabilitar rp_filter en las interfaces o el núcleo descartará esos paquetes marcianos.
Y por último, pero no menos importante, ¡cuidado con los paquetes que regresan! En mi caso hay enrutamiento asimétrico (las solicitudes llegan al cliente -> haproxy1 -> haproxy2 -> servidor web, y las respuestas van desde el servidor web -> haproxy1 -> cliente), pero no afecta. Funciona bien.
Sé que la solución más elegante sería usar iproute2 para hacer el desvío, pero solo funcionó para el primer paquete SYN. Cuando recibió el ACK (tercer paquete del apretón de manos de 3 vías), no lo marcó :( No pude pasar mucho tiempo para investigar, tan pronto como vi que funciona con el objetivo TEE, lo dejó allí. Por supuesto, siéntase libre de probarlo con iproute2.
Básicamente, la "recarga elegante" funciona así:
- Habilito el conjunto de reglas de iptables e inmediatamente veo las nuevas conexiones que van al otro HAproxy.
- Mantengo un ojo en "netstat -an | grep ESTABLECIDO | wc -l" para supervisar el proceso de "drenaje".
- Una vez que haya solo unas pocas (o cero) conexiones, "ifconfig dummy0 down" para forzar el mantenimiento de la conmutación por error, por lo que todo el tráfico irá al otro HAproxy.
- Elimino el conjunto de reglas de iptables
- (Solo para la configuración de keepalive "sin preferencia") "ifconfig dummy0 up".
El conjunto de reglas de IPtables se puede integrar fácilmente en un script de inicio / detención:
#!/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