SLB es un equilibrador de carga sin sesión para tráfico UDP y resuelve los problemas inherentes al uso de equilibradores de carga tradicionales (ricos en funciones) para dicho tráfico.
Para protocolos UDP simples y sin estado, no existe ninguna ventaja en intentar mantener la "afinidad" (también conocida como "sesiones") entre los clientes y las instancias de back-end. Los balanceadores de carga tradicionales asumen que la afinidad es útil y, por lo tanto, intentarán enrutar paquetes desde un cliente a un servidor back-end consistente. Por el contrario, SLB distribuye uniformemente (aleatoriamente) los paquetes uno por uno entre todos los servidores disponibles. Esto da como resultado una carga uniforme de backends y una mayor solidez cuando falla una instancia de backend (habrá un aumento en la pérdida de paquetes para todos los clientes en lugar de una pérdida total de tráfico para algunos clientes).
De forma predeterminada, SLB escuchará en los puertos 1812
y 1813
los paquetes UDP entrantes y los transmitirá a destinos backend aleatorios que conoce. Los puertos en los que escucha pueden tener la opción --server-port-range
, que acepta un solo puerto (por ejemplo, 541
) o un rango de puertos (por ejemplo, 4000-5000
).
Para que SLB esté al tanto de los backends es necesario enviar paquetes de "vigilancia" (también conocidos como "mantener vivo") al puerto de administración (más sobre esto a continuación). De forma predeterminada, el puerto de administración es 1111
, pero se puede configurar usando la opción --admin-port
. Si hay varias tarjetas de red presentes en su sistema, puede especificar la IP usando la opción --admin-ip
. Si la IP especificada con --admin-ip
está en el rango CIDR de multidifusión ( 244.0.0.0/4
), SLB se unirá automáticamente a ese grupo de multidifusión (más sobre esto a continuación).
Otras opciones se describen en la ayuda del comando:
SimplestLoadBalancer:
Sessionless UDP Load Balancer sends packets to backends without session affinity.
Usage:
SimplestLoadBalancer [options]
Options:
--server-port-range <server-port-range> Set the ports to listen to and forward to backend targets
(default "1812-1813") [default: 1812-1813]
--admin-ip <admin-ip> Set the IP to listen on for watchdog events [default is first private IP]
--admin-port <admin-port> Set the port that targets will send watchdog events [default: 1111]
--client-timeout <client-timeout> Seconds to allow before cleaning-up idle clients [default: 30]
--target-timeout <target-timeout> Seconds to allow before removing target missing watchdog events [default: 30]
--default-target-weight <default-target-weight> Weight to apply to targets when not specified [default: 100]
--unwise Allows public IP addresses for targets [default: False]
--stats-period-ms <stats-period-ms> Sets the number of milliseconds between statistics messages printed to the
console (disable: 0, max: 65535) [default: 1000]
--default-group-id <default-group-id> Sets the group ID to assign to backends that when a registration packet doesn't
include one, and when port isn't assigned a group [default: 0]
--version Show version information
-?, -h, --help Show help and usage information
Los backends no están configurados en la línea de comando. Más bien, se registran y cancelan dinámicamente mediante paquetes UDP periódicos enviados al puerto de administración ( --admin-port
). El contenido de esos paquetes puede diferir según cómo utilice SLB en su entorno.
Si está ejecutando un único servidor SLB, los backends se pueden configurar para enviar paquetes a esa IP y al puerto de administración. Este es el escenario más simple. Cada backend enviará mensajes con dos "bytes mágicos" para indicar el "registro de backend" para el contenido:
0x11 0x11
SLB interpretará dicho paquete como "registrar al remitente como servidor". Opcionalmente, los mensajes pueden contener uno o dos bytes adicionales (peso e ID de grupo) cuyo propósito se discutirá con más detalle a continuación.
0x11 0x11 [X] [X]
^ ^
| |
| one byte for group id
|
one byte for weight
En algunos entornos, los paquetes de registro no se enviarán desde los propios servidores y SLB admite dichos casos de uso. Cuando un "tercero" envía un paquete de registro, el contenido deberá incluir la dirección IP del servidor que se está registrando:
0x11 0x11 X X X X [X] [X]
^ ^ ^
| | |
| | one byte for group id
| |
| one byte for weight
|
four bytes for ip to add
Nuevamente, opcionalmente se pueden agregar los bytes de ID de grupo y peso.
Cuando se necesita una implementación de HA más sólida con múltiples SLB, la comunicación entre los backends y SLB se puede simplificar mediante el uso de una IP de grupo de multidifusión. Esto es útil ya que cada SLB debe conocer cada backend. En tal caso, los servidores SLB deben utilizar la opción --admin-ip
para especificar una dirección de multidifusión que hará que los SLB se unan al grupo de multidifusión y, por lo tanto, todos reciban cualquier mensaje enviado a esa IP. Los backends se pueden configurar con esa única IP, minimizando su carga de trabajo y simplificando su configuración (particularmente cuando los SLB entran y salen de servicio debido al escalado automático y/o el uso de instancias puntuales).
Tenga en cuenta que el uso de una IP de multidifusión requiere un conmutador que admita multidifusión o (más probablemente) ejecutarse en una VPC de AWS configurada con un dominio de multidifusión.
Los formatos de los paquetes de administración son muy simples a partir de la versión 2.0. En el caso de uso de SLB único más simple, un paquete de registro de un backend puede consistir en nada más que dos bytes mágicos ( 0x11
0x11
). Opcionalmente, los paquetes pueden provenir de una fuente diferente (por ejemplo, un servidor de administración) e incluir cuatro bytes para especificar la dirección IPv4 de un servidor. En cualquier caso, se pueden agregar dos bytes opcionales adicionales para el "peso" del tráfico en relación con otros servidores y para que el "grupo" se asigne al servidor (más información sobre los grupos a continuación). En arte ASCII:
0x11 0x11 [X X X X] [X] [X]
^ ^ ^
| | |
| | one byte for group id
| |
| one byte for weight
|
four bytes for ip to add
Para eliminar inmediatamente un objetivo, envíe un paquete con 0x86
como primer byte en lugar de 0x11
(si se envía desde un servidor de administración, agregue la IP del servidor a eliminar):
0x86 0x11 [X X X X]
^
|
four bytes for ip to remove
Los pesos se utilizan para controlar la cantidad relativa de tráfico entregado a cada backend. Si no se especifica ningún peso, el valor predeterminado de 100 (configurable con --default-target-weight
) se aplicará al backend y cada uno recibirá el mismo volumen de paquetes. Dicho esto, se espera (y es aconsejable) que los servidores ajusten el valor de peso en sus paquetes de administración en función de su capacidad para manejar el tráfico (tal vez reducido cuando la CPU es alta, se aplican actualizaciones, etc.). Por ejemplo:
100
, 50
y 50
, respectivamente, entonces el primero recibirá el 50% del tráfico y el segundo y el tercero obtendrán cada uno el 25%.31
y 31
respectivamente, cada uno recibirá el 50% del tráfico.Cuando se utilizan grupos, los pesos relativos se evalúan frente a otros servidores del mismo grupo (no en todos los grupos).
Es importante enviar paquetes de administración de manera confiable y con una cadencia suficiente. Cada vez que SLB recibe un paquete, se actualiza la hora de "última vista" del backend. Si pasan 30 segundos (configurable con
--target-timeout
) sin que se vea un backend, se elimina y no se le enviará más tráfico.
De forma predeterminada, todos los backends se utilizarán para dar servicio a todos los puertos atendidos por el balanceador de carga.
Sin embargo, es posible asignar puertos individuales a subconjuntos de servidores mediante mensajes de asignación de puertos SLB y proporcionando ID de grupo en los mensajes de registro. Considere, por ejemplo, que le gustaría tener tráfico de equilibrio de carga SLB para los puertos 1812-1813 pero asignar el tráfico que llega a cada puerto a un conjunto diferente de servidores. Para hacerlo:
x66 x11
) con un número de puerto (dos bytes) y un ID de grupo (un byte). No es necesario repetir estos mensajes y se pueden enviar cuando se desea un cambio en las asignaciones de grupos de puertos (sin embargo, no hay nada de malo en repetirlos, lo que puede ser conveniente para garantizar que los puertos tengan grupos asignados correctamente después de que se reinicie el servicio). 0x66 0x11 X X X
^ ^
| |
| one byte for group ID
|
two bytes for port number, litten endian
Al utilizar Linux bash
es sencillo enviar paquetes de administración. Esto se puede hacer usando el comando netcat
(también conocido como nc
) o el sistema de archivos /dev/udp
. Por ejemplo, si su balanceador de carga escucha en el puerto de administración predeterminado 1111
y desea agregar un destino con la IP 192.168.1.22
:
$ echo -e $( echo " x11x11 $( echo " 192.168.1.22 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.0.0.1/1111
Dado que puede resultar tedioso enviar manualmente esos paquetes para mantener un conjunto de objetivos registrados, puede crear un pequeño script de shell, por ejemplo lb.sh
:
#! /bin/bash
echo -ne $( echo " x11x11 $( echo " 192.168.1.22 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.23 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.24 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.25 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.26 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
echo -ne $( echo " x11x11 $( echo " 192.168.1.27 " | tr " . " " n " | xargs printf ' \x%02X ' ) " ) > /dev/udp/127.1.1.1/1111
Y luego use el comando watch
para llamar a ese script cada pocos segundos:
$ watch -n10 ./lb.sh
Los binarios prediseñados para Linux y Windows x64, y Linux ARM están disponibles como "Lanzamientos" de GitHub. Este es un proyecto .Net 8.0 muy simple, así que para compilarlo ejecute (suponiendo que tenga instalado dotnet-sdk-8.0):
dotnet build
Probablemente desee generar un ejecutable binario nativo, que sea conveniente y ofrezca algunos beneficios de rendimiento.
Para Linux:
dotnet publish -o ./ -c Release -r linux-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true --self-contained
Para ventanas:
dotnet publish -o ./ -c Release -r win10-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true --self-contained
Asimismo, es sencillo de ejecutar usando dotnet run
en el directorio del proyecto:
$ dotnet run
O, si ha creado un ejecutable nativo:
$ ./SimplestLoadBalancer
No dude en crear problemas aquí en GitHub para preguntas e informes de errores. No se proporciona ninguna plantilla ni requisitos, pero intente ser lo más descriptivo posible; esto ayudará a garantizar que podamos responder de manera sensata. Véase también contribuir.
¡Disfrutar!