La clave para escalar una capa de equilibrio de carga HTTP es agregar primero otra capa de equilibrio de carga de nivel inferior (IP o TCP). Esta capa se puede construir completamente con software de código abierto, aunque obtendrá mejores resultados si tiene enrutadores modernos.
Los flujos (sesiones TCP) se deben codificar utilizando encabezados como los puertos IP y TCP de origen / destino, para decidir a qué interfaz van. También necesita un mecanismo para asegurarse de que cuando una interfaz muere, deja de acostumbrarse.
Hay varias estrategias, voy a describir un par que he usado en producción en sitios que sirven a millones de usuarios, para que pueda entender la idea. Sería demasiado largo para explicar todo en detalle, pero espero que esta respuesta le brinde suficiente información / consejos para comenzar. Para implementar estas soluciones, necesitará a alguien que conozca realmente las redes.
Es cierto que lo que estoy describiendo aquí es mucho más difícil de implementar que lo que se describe en otras respuestas, pero este es realmente el estado del arte si tiene un sitio web de alto tráfico con grandes problemas de escalabilidad y requisitos de disponibilidad superiores al 99,9% . Siempre que ya tenga un ingeniero de redes a bordo, cuesta menos configurarlo y ejecutarlo (tanto en capex como en opex) que los dispositivos balanceadores de carga, y se puede ampliar aún más sin casi ningún costo adicional (en comparación con la compra de uno nuevo, incluso más aparato costoso cuando superas tu modelo actual).
Primera estrategia: con un firewall
Presumiblemente tiene un par de enrutadores en los que están conectados los enlaces ascendentes de su ISP. Su ISP proporciona 2 enlaces (activo / pasivo, usando VRRP). En sus enrutadores, también usa VRRP y dirige el tráfico que va a su red pública a un firewall. Los cortafuegos ( FW 1
y más FW 2
abajo) también son activos / pasivos y filtrarán el tráfico y enviarán cada flujo a un servidor frontend (sus equilibradores de carga HTTP FE 1
y más FE 2
abajo).
+ -------------- + + -------------- +
El | ISP router A | El | ISP router B |
+ -------------- + + -------------- +
El | El |
== # ====================== # == (red pública)
El | El |
+ --------------- + + --------------- +
El | Su enrutador A | El | Su enrutador B |
+ --------------- + + --------------- +
El | El |
== # ===== # ========== # ===== # == (red privada RFC 1918)
El | El | El | El |
+ ------ + + ------ + + ------ + + ------ +
El | FW 1 | El | FE 1 | El | FE 2 | El | FW 2 |
+ ------ + + ------ + + ------ + + ------ +
El objetivo es tener un flujo como este:
- El ISP dirige el tráfico que va a sus IP a su enrutador activo.
- Sus enrutadores enrutan el tráfico a un VIP que utiliza una dirección RFC 1918 . Este VIP es propiedad del firewall activo, al igual que VRRP. Si usa OpenBSD para sus necesidades de firewall, puede usar CARP , una alternativa sin patente a VRRP / HSRP.
- Su firewall aplica el filtro (por ejemplo, "solo permite que 80 / tcp y 443 / tcp vayan a esta dirección IP en particular").
- Su firewall también actúa como un enrutador y reenvía los paquetes a un frontend saludable.
- Su interfaz termina la conexión TCP.
Ahora la magia ocurre en los pasos 4 y 5, así que veamos con más detalles lo que hacen.
Su cortafuegos conoce la lista de interfaces ( FE 1
y FE 2
), y seleccionará una de ellas en función de un aspecto particular del flujo (p. Ej., Mediante el hash de la IP de origen y el puerto, entre otros encabezados). Pero también debe asegurarse de que reenvíe el tráfico a una interfaz saludable, de lo contrario, bloqueará el tráfico. Si usa OpenBSD, por ejemplo, puede usar relayd
. Quérelayd
does es simple: verifica el estado de todas sus interfaces (por ejemplo, enviándoles una solicitud HTTP de sonda), y cada vez que una interfaz está en buen estado, la agrega a una tabla que el firewall usa para seleccionar el siguiente salto de los paquetes de un flujo dado . Si un frontend falla las comprobaciones de estado, se elimina de la tabla y ya no se envían paquetes. Al reenviar un paquete a un frontend, todo lo que hace el firewall es intercambiar la dirección MAC de destino del paquete para que sea la del frontend elegido.
En el paso 5, el equilibrador de carga recibe los paquetes del usuario (ya sea Varnish, nginx o lo que sea). En este punto, el paquete todavía está destinado a su dirección IP pública, por lo que debe alias sus VIP (s) en la interfaz de bucle invertido. Esto se llama DSR (Direct Server Return), porque sus interfaces terminan la conexión TCP y el cortafuegos intermedio solo ve tráfico simple (solo paquetes entrantes). Su enrutador enrutará los paquetes salientes directamente de regreso a los enrutadores del ISP. Esto es especialmente bueno para el tráfico HTTP porque las solicitudes tienden a ser más pequeñas que las respuestas, a veces significativamente. Para que quede claro: esto no es algo específico de OpenBSD y se usa ampliamente en sitios web de alto tráfico.
Gotchas:
- Los usuarios finales se conectarán directamente a sus servidores frontend porque usted usa DSR. Tal vez ya era el caso, pero si no fuera así, debe asegurarse de que estén adecuadamente asegurados.
- Si usa OpenBSD, tenga en cuenta que el núcleo tiene un solo subproceso, por lo que el rendimiento de un solo núcleo de CPU limitará el rendimiento de un firewall. Puede ser un problema dependiendo de su tipo de NIC y la tasa de paquetes que está viendo. Hay formas de resolver este problema (más sobre esto a continuación).
Segunda estrategia: sin firewall
Esta estrategia es más eficiente pero más difícil de configurar porque depende más de los detalles de los enrutadores que tiene. La idea es evitar el firewall anterior y hacer que los enrutadores hagan todo el trabajo que los firewalls estaban haciendo.
Necesitará enrutadores que admitan ACL L3 / L4 por puerto, BGP y ECMP , y enrutamiento basado en políticas (PBR). Solo los enrutadores de alta gama admiten estas características, y a menudo tienen tarifas de licencia adicionales para usar BGP. Esto suele ser aún más barato que los equilibradores de carga de hardware, y también es mucho más fácil de escalar. Lo bueno de estos enrutadores de gama alta es que tienden a ser de velocidad de línea (p. Ej., Siempre pueden maximizar el enlace, incluso en interfaces de 10 GbE, porque todas las decisiones que toman las toman los ASIC en hardware).
En los puertos en los que tiene sus enlaces ascendentes de ISP, aplique la ACL que solía estar en el firewall (por ejemplo, "solo permita que 80 / tcp y 443 / tcp vayan a esta dirección IP en particular"). Luego, haga que cada una de sus interfaces mantenga una sesión BGP con su enrutador. Puede usar el excelente OpenBGPD (si sus interfaces están en OpenBSD) o Quagga . Su enrutador ECMP el tráfico a las interfaces que están en buen estado (porque mantienen sus sesiones de BGP). El enrutador también enrutará el tráfico adecuadamente utilizando PBR.
Refinamientos
- Con la solución de par de firewall, es bueno si puede sincronizar los estados de TCP a través de los firewalls, de modo que cuando un firewall falla, todo falla sin problemas al otro. Puedes lograr esto con
pfsync
.
- Tenga en cuenta que
pfsync
normalmente duplicará la tasa de paquetes en sus firewalls.
- HTTP es un protocolo sin estado, por lo que no es el fin del mundo si restablece todas las conexiones durante una conmutación por error del firewall porque no lo utiliza
pfsync
.
- Si supera un solo firewall, puede usar ECMP en su enrutador para enrutar su tráfico a más de un par de firewall.
- Si usa más de un par de cortafuegos, es mejor que los active o active. Puede lograr esto haciendo que los cortafuegos mantengan una sesión BGP con los enrutadores, al igual que las interfaces necesitan mantener uno en el segundo diseño sin cortafuegos.
relayd
Configuración de muestra
Vea también COMO en https://calomel.org/relayd.html
vip = "1.2.3.4" # Su dirección IP pública
# (puede tener más de uno, pero no es necesario)
fe1 = "10.1.2.101"
fe2 = "10.1.2.102"
fe3 = "10.1.2.103"
fe4 = "10.1.2.104" # Puede tener cualquier cantidad de interfaces.
int_if = "em0"
tabla <fe> {$ fe1 reintento 2, $ fe2 reintento 2, $ fe3 reintento 2, $ fe4 reintento 2}
tabla <caída> {127.0.0.1}
redirigir tráfico web {
escuchar en $ vip puerto 80
tiempo de espera de sesión 60
ruta a <fe> verifique la interfaz http "/healthcheck.html" digest "(sha1sum of healthcheck.html)" $ int_if
}