Estoy ejecutando un servidor de equilibrio de carga HAProxy para equilibrar la carga a múltiples servidores Apache. Necesito volver a cargar HAProxy en cualquier momento para cambiar el algoritmo de equilibrio de carga.
Todo esto funciona bien, excepto por el hecho de que tengo que volver a cargar el servidor sin perder un solo paquete (en este momento, una recarga me está dando un 99.76% de éxito en promedio, con 1000 solicitudes por segundo durante 5 segundos). He investigado muchas horas sobre esto y he encontrado el siguiente comando para "recargar con gracia" el servidor HAProxy:
haproxy -D -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)
Sin embargo, esto tiene poco o ningún efecto en comparación con el viejo service haproxy reload
, todavía cae 0.24% en promedio.
¿Hay alguna forma de volver a cargar el archivo de configuración HAProxy sin un solo paquete descartado de ningún usuario?
Respuestas:
De acuerdo con https://github.com/aws/opsworks-cookbooks/pull/40 y, en consecuencia, http://www.mail-archive.com/[email protected]/msg06885.html puede:
fuente
iptables v1.4.14: invalid port/service
--syn 'especificado`$PORT
con el puerto real quehaproxy
está escuchando. Si haproxy está escuchando en varios puertos, escritura reemplazar--dport $PORT
con--dports $PORTS_SEPARATED_BY_COMMAS
, por ejemplo,--dports 80,443
.Yelp compartió un enfoque más sofisticado basado en pruebas meticulosas. El artículo del blog es una inmersión profunda, y vale la pena la inversión de tiempo para apreciarlo completamente.
Recarga de HAProxy con verdadero tiempo de inactividad cero
tl; dr usa Linux tc (control de tráfico) e iptables para poner en cola temporalmente los paquetes SYN mientras HAProxy se está recargando y tiene dos pids conectados al mismo puerto (
SO_REUSEPORT
).No me siento cómodo volviendo a publicar el artículo completo en ServerFault; Sin embargo, aquí hay algunos extractos para despertar su interés:
Gist: https://gist.github.com/jolynch/97e3505a1e92e35de2c0
Saludos a Yelp por compartir ideas tan sorprendentes.
fuente
Hay otra forma mucho más simple de recargar haproxy con un tiempo de inactividad verdadero cero: se llama volteo de iptables (el artículo es en realidad respuesta Unbounce a la solución de Yelp). Es más limpio que la respuesta aceptada ya que no hay necesidad de descartar ningún paquete que pueda causar problemas con recargas prolongadas.
Brevemente, la solución consta de los siguientes pasos:
iptable
comandos simples .Además, la solución se puede adoptar para cualquier tipo de servicio (nginx, apache, etc.) y es más tolerante a fallas, ya que puede probar la configuración en espera antes de que se ponga en línea.
fuente
Editar: Mi respuesta supone que el kernel solo envía tráfico al puerto más reciente para que se abra con SO_REUSEPORT, mientras que en realidad envía tráfico a todos los procesos como se describe en uno de los comentarios. En otras palabras, todavía se requiere el baile de iptables. :(
Si está en un núcleo que admite SO_REUSEPORT, entonces este problema no debería suceder.
El proceso que toma haproxy cuando se reinicia es:
1) Intente configurar SO_REUSEPORT al abrir el puerto ( https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5d5fb838e120f1b0e9361a92f8/src/proto_tcp.c#L792-L798 )
2) Intente abrir el puerto (tendrá éxito con SO_REUSEPORT)
3) Si no tuvo éxito, indique al proceso anterior que cierre su puerto, espere 10 ms y vuelva a intentarlo todo. ( https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5d5fb838e120f1b0e9361a92f8/src/haproxy.c#L1554-L1577 )
Primero fue compatible con el kernel Linux 3.9, pero algunas distribuciones lo han soportado. Por ejemplo, los núcleos EL6 de 2.6.32-417.el6 lo admiten.
fuente
SO_REUSEPORT
algún escenario particular, especialmente con mucho tráfico. Cuando SYN se envía al antiguo proceso de haproxy y en el mismo momento se cierra el socket de escucha que da como resultado RST. Consulte el artículo de Yelp mencionado en otra respuesta anterior.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:
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í:
El conjunto de reglas de IPtables se puede integrar fácilmente en un script de inicio / detención:
fuente