SLB é um balanceador de carga sem sessão para tráfego UDP e resolve problemas inerentes ao uso de balanceadores de carga tradicionais (ricos em recursos) para esse tráfego.
Para protocolos UDP simples e sem estado, não há vantagem em tentar manter a "afinidade" (também conhecida como "sessões") entre clientes e instâncias de back-end. Os balanceadores de carga tradicionais presumem que a afinidade é útil e, portanto, tentarão rotear pacotes de um cliente para um servidor back-end consistente. Por outro lado, o SLB distribui uniformemente (aleatoriamente) os pacotes um por um em todos os back-ends disponíveis. Isso resulta em carregamento uniforme de back-ends e maior robustez quando uma instância de back-end falha (haverá um aumento na perda de pacotes para todos os clientes, em vez de uma perda total de tráfego para alguns clientes).
Por padrão, o SLB escutará nas portas 1812
e 1813
a entrada de pacotes UDP e os retransmitirá para destinos de back-end aleatórios que ele conhece. As portas nas quais ele escuta podem ter a opção --server-port-range
, que aceita uma única porta (por exemplo, 541
) ou um intervalo de portas (por exemplo, 4000-5000
).
Para conscientizar o SLB dos back-ends, é necessário enviar pacotes "watchdog" (também conhecidos como "keep alive") para a porta de administração (mais sobre isso abaixo). Por padrão, a porta admin é 1111
, mas pode ser configurada usando a opção --admin-port
. Se várias placas de rede estiverem presentes em seu sistema, você poderá especificar o IP usando a opção --admin-ip
. Se o IP especificado com --admin-ip
estiver no intervalo CIDR multicast ( 244.0.0.0/4
), o SLB ingressará automaticamente nesse grupo multicast (mais sobre isso abaixo).
Outras opções estão descritas na ajuda do 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
Os back-ends não são configurados na linha de comando. Em vez disso, eles são registrados e cancelados dinamicamente usando pacotes UDP periódicos enviados para a porta admin ( --admin-port
). O conteúdo desses pacotes pode diferir dependendo de como você usa o SLB em seu ambiente.
Se você estiver executando um único servidor SLB, os back-ends poderão ser configurados para enviar pacotes para esse IP e na porta de administração. Este é o cenário mais simples. Cada backend enviará mensagens com dois "bytes mágicos" para indicar o "registro de backend" do conteúdo:
0x11 0x11
O SLB interpretará tal pacote como "registrar o remetente como back-end". Opcionalmente, as mensagens podem conter um ou dois bytes adicionais (peso e ID de grupo) cuja finalidade será discutida com mais detalhes a seguir.
0x11 0x11 [X] [X]
^ ^
| |
| one byte for group id
|
one byte for weight
Em alguns ambientes, os pacotes de registro não serão enviados dos próprios back-ends, e o SLB oferece suporte a esses casos de uso. Quando um pacote de registro é enviado de um “terceiro”, o conteúdo precisará incluir o endereço IP do backend que está sendo registrado:
0x11 0x11 X X X X [X] [X]
^ ^ ^
| | |
| | one byte for group id
| |
| one byte for weight
|
four bytes for ip to add
Novamente, os bytes de peso e ID de grupo podem ser opcionalmente anexados.
Quando é necessária uma implantação de HA mais robusta com vários SLBs, a comunicação entre backends e SLB pode ser simplificada usando um IP de grupo multicast. Isso é útil porque cada SLB deve estar ciente de cada back-end. Nesse caso, os servidores SLB devem usar a opção --admin-ip
para especificar um endereço multicast que fará com que os SLBs se juntem ao grupo multicast e, portanto, todos recebam qualquer mensagem enviada para esse IP. Os back-ends podem ser configurados com esse único IP, minimizando sua carga de trabalho e simplificando sua configuração (especialmente quando SLBs são alternados dentro e fora de serviço devido ao escalonamento automático e/ou ao uso de instâncias spot).
Observe que o uso de um IP multicast requer um switch que suporte multicast ou (mais provavelmente) executado em uma AWS VPC configurada com um domínio multicast.
Os formatos dos pacotes admin são muito simples a partir da versão 2.0. No caso de uso mais simples de SLB único, um pacote de registro de um back-end pode consistir em nada mais do que dois bytes mágicos ( 0x11
0x11
). Opcionalmente, os pacotes podem vir de uma fonte diferente (por exemplo, um servidor de gerenciamento) e incluir quatro bytes para especificar o endereço IPv4 de um backend. Em ambos os casos, dois bytes opcionais adicionais para o "peso" do tráfego em relação a outros back-ends e para o "grupo" atribuir ao back-end podem ser anexados (mais sobre grupos abaixo). Na 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 remover imediatamente um alvo, envie um pacote com 0x86
como primeiro byte em vez de 0x11
(se enviado de um servidor de gerenciamento, anexe o IP do backend para remover):
0x86 0x11 [X X X X]
^
|
four bytes for ip to remove
Os pesos são usados para controlar a quantidade relativa de tráfego entregue a cada back-end. Se nenhum peso for especificado, o valor padrão de 100 (configurável com --default-target-weight
) será aplicado ao backend e cada um receberá o mesmo volume de pacotes. Dito isso, é esperado (e aconselhável) que os back-ends ajustem o valor do peso em seus pacotes de administração com base em sua capacidade de lidar com o tráfego (talvez reduzido quando a CPU estiver alta, atualizações estiverem sendo aplicadas, etc.). Por exemplo:
100
, 50
e 50
, respectivamente, o primeiro receberá 50% do tráfego e o segundo e o terceiro receberão 25% cada.31
e 31
, respectivamente, cada um receberá 50% do tráfego.Ao usar grupos, os pesos relativos são avaliados em relação a outros back-ends no mesmo grupo (não em todos os grupos).
É importante enviar pacotes administrativos de forma confiável e com cadência suficiente. Cada vez que um pacote é recebido pelo SLB, a hora da “última visualização” do backend é atualizada. Se passarem 30 segundos (configuráveis com
--target-timeout
) sem que um back-end seja visto, ele será removido e nenhum tráfego adicional será enviado a ele.
Por padrão, todos os back-ends serão usados para atender todas as portas atendidas pelo balanceador de carga.
No entanto, é possível atribuir portas individuais a subconjuntos de back-ends usando mensagens de atribuição de portas SLB e fornecendo IDs de grupo em mensagens de registro. Considere, por exemplo, que você gostaria de ter tráfego de balanceamento de carga SLB para as portas 1812-1813, mas atribuir o tráfego que chega a cada porta a um conjunto diferente de servidores. Para fazer isso:
x66 x11
) com um número de porta (dois bytes) e um ID de grupo (um byte). Essas mensagens não precisam ser repetidas e podem ser enviadas quando uma alteração nas atribuições de grupos de portas for desejada (no entanto, não há mal nenhum em repeti-las, o que pode ser conveniente para garantir que as portas sejam atribuídas corretamente aos grupos após a reinicialização do serviço). 0x66 0x11 X X X
^ ^
| |
| one byte for group ID
|
two bytes for port number, litten endian
Usando o Linux bash
é simples enviar pacotes de administração. Isso pode ser feito usando o comando netcat
(também conhecido como. nc
) ou o sistema de arquivos /dev/udp
. Por exemplo, se o seu balanceador de carga estiver escutando na porta de administração padrão 1111
e você quiser adicionar um destino com o 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
Como pode ser entediante enviar manualmente esses pacotes para manter um conjunto de alvos registrados, você pode criar um pequeno script de shell, como 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
E então use o comando watch
para chamar esse script a cada poucos segundos:
$ watch -n10 ./lb.sh
Binários pré-construídos para Linux e Windows x64 e Linux ARM estão disponíveis como "Versões" do GitHub. Este é um projeto .Net 8.0 muito simples, portanto, para construí-lo, execute (supondo que você tenha o dotnet-sdk-8.0 instalado):
dotnet build
Você provavelmente desejará gerar um executável binário nativo, o que é conveniente e oferece alguns benefícios de desempenho.
Para Linux:
dotnet publish -o ./ -c Release -r linux-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true --self-contained
Para Windows:
dotnet publish -o ./ -c Release -r win10-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true --self-contained
Da mesma forma, é simples executar usando dotnet run
no diretório do projeto:
$ dotnet run
Ou, se você criou um executável nativo:
$ ./SimplestLoadBalancer
Sinta-se à vontade para criar problemas aqui no GitHub para perguntas e relatórios de bugs. Não há nenhum modelo ou requisitos fornecidos, mas tente ser o mais descritivo possível - isso ajudará a garantir que possamos responder de maneira sensata. Veja também, contribuindo.
Aproveitar!