IPVS: Um Load balancer poderoso que existe em seu kernel!

Wilson Júnior
5 min readApr 14, 2019

--

Olá, meu nome é Wilson Júnior, sou programador desde 2007, quando comecei a minha carreira eu programava sistemas que rodariam em apenas uma máquina, então meu desafio era apenas pensar em subir uma aplicação em um único servidor que possuía um único endereço de IP, expor uma porta para os usuários começarem a acessar.

Novos desafios: Necessidade de alta-disponibilidade e escalabilidade

Como sabemos um servidor possui recursos limitados (CPU, Memória, Disco e Rede), em ocasiões de alta audiência esses recursos podem não ser suficientes para atender os usuários, sendo necessário possuir a mesma aplicação em diversas máquinas. Também olhando para o critério de ponto de falhas, ter apenas uma máquina pode ocasionar em indisponibilidade em momentos de manutenção.

E como fazemos para dividir a carga entre estes servidores? precisamos de um load-balancer!

Tipos de Load-balancer

Load-balancer Físicos

São equipamentos extremamente especializados em fazer load-balancing, sendo necessário a aquisição e instalação no data-center.

Load-balancer como serviço

São serviços de load balancing que geralmente são oferecidos por cloud públicas (AWS, Google Cloud, Digital ocean, etc), possuem uma configuração simples ao usuário final. São cobrados por uso.

Load-balancers L4 e L7 via user-space

São load-balancers que rodam como aplicações na user-space do sistema operacional, recebem conexões e abrem diversas conexões com os serviços destinos, exemplo de aplicações que atuam como load-balancers: nginx, haproxy, vulcand, hipache, planb, traefic e galeb.

Load-balancer L4 via kernel-space: IPVS

É um load-balancer layer 4 com baíxissimo overhead que está disponível no kernel linux desde 2004, roda como um módulo do netfilter, recentemente o Kubernetes ganhou suporte nativo a IPVS para dividir a carga de seus serviços.

Algoritmos de balanceamento disponíveis via IPVS

Round robin (rr)

É um algoritmo que faz um escalonamento circular para cada nova conexão, exemplo: tenho 3 servidores (S1, S2 e S3), caso receba 7 conexões , teremos 7 conexões estabelecidas na seguinte ordem:[S1, S2, S3, S1, S2, S3, S1].

Weighted Round Robin (wrr)

Nem sempre possuimos máquinas com o poder de processamento homogeneo para serem balanceadas. Ex: podemos ter 2 máquinas novas com poder de processamento superior e duas máquinas mais antigas, se aplicarmos o round-robin comum as máquinas novas podem ficar sub-utilizadas e as antigas sobre-carregadas.

Exemplo prático:

Dado os servidores: 1 servidor com o peso 3 (S1), 2 servidores com o peso 2 (S2 e S3), 1 servidor com o peso 1 (S4).

Variáveis utilizadas no algoritmo:

mw: Maximum weight, peso máximo dos servidores, para este caso é 3
di: Weight step, maior divisor comum de todos os pesos, para este caso é 1

devemos selecionar os servidores onde mw ≥ peso do servidor, diminuir o mw com o di, até que mw =di.

mw = 3, [S1]
mw-di = 2, wm = 2, [S1, S2 e S3]
mw-di = 1, wm = 1, [S1, S2, S3 e S4]

Resultando em um round roubin na ordem: [S1, S1, S2, S3, S1, S2, S3 e S4], assim o servidor S1 receberá 3x carga que o S4, o S2 e S3 receberá 2x carga que o S4.

Least Connection (lc)

É um algoritmo que sempre escolhe o servidor que possui menos conexões a fim de deixar todos equalizados.

Exemplo: Imagine que temos 2 servers (A com 100 conexões e B com 98 conexões) e o load-balancer receberá 3 conexões, no final teremos um dos servidores com 101 conexões e o outro com 100.

Weighted Least-Connection (wlc)

É um algoritmo que também olha para o número de conexões, que também é levado em conta o peso de cada servidor, assim mantendo a proporção de conexões de acordo com o peso configurado.

Source Hashing (sh)

É um algoritmo que utiliza hashs baseados no IP de origem para definir em qual servidor se conectará. Assim cada cliente sempre conectará no mesmo server.

Maglev (mh)

É um algoritmo que também utiliza hashs, nasceu dentro do Google, a grande vantagem dele é manter a consistência de conexões (source ip e destination ip) até quando servidores são adicionados ou removidos dinamicamente. Mais detalhes em: https://www.usenix.org/system/files/conference/nsdi16/nsdi16-paper-eisenbud.pdf

Overflow-Connection (ovf)

É um algoritmo que cada servidor recebe carga até o limite definido e as conexões que exceder irá para o próximo servidor.

Exemplo

Server 1 (limite de 100 conexões, estado atual: 90 conexões)
Server 2(limite de 100 conexões, estado atual: 0 conexões)

se recebermos mais 20 conexões ficaremos no seguinte estado:

Server 1 (limite de 100 conexões, estado atual: 100 conexões)
Server 2(limite de 100 conexões, estado atual: 10 conexões)

O IPVS possui outros algoritmos e o código fonte está disponível em: https://github.com/torvalds/linux/tree/master/net/netfilter/ipvs

Mão na massa

  1. Iremos instalar o ipvsadm, é uma ferramenta para escrever as regras de load-balancing no kernel
apt install ipvsadm # para ubuntu ou debian
pacman -Sy ipvsadm # para archlinux

2. Precisaremos de uma interface de rede para atribuir o IPVS, para esta demonstração criaremos uma dummy interface.

ip link add eth-dummy type dummy
ip link set eth-dummy up

3. É necessario escolher um IP Virtual e atribuir a interface de rede, neste caso usaremos o IP: 1.2.3.4 para atuar como load-balancer.

ip a add 1.2.3.4/32 dev eth-dummy

4. Criaremos uma regra de IPVS para ter um load-balancer. utilizando o algoritmo de round-robin (rr).

ipvsadm -A -t 1.2.3.4:80 -s rr

5. Adicionaremos os destinos deste load-balancer, após a execução teremos dois serviços dividindo a carga para 1.2.3.4:80

ipvsadm -a -t 1.2.3.4:80 -r 127.0.0.1:3000 -m
ipvsadm -a -t 1.2.3.4:80 -r 127.0.0.1:3001 -m

6. Criaremos dois servidores HTTPs simples para a demonstração, execute cada bloco em um terminal diferente.

mkdir -p /tmp/service-a
echo 'This is a' > /tmp/service-a/index.html
python -m http.server 3000 --directory /tmp/service-a

mkdir -p /tmp/service-b
echo 'This is b' > /tmp/service-b/index.html
python -m http.server 3001 --directory /tmp/service-b

7. Finalmente =D

$ curl http://1.2.3.4:80
This is a
$ curl http://1.2.3.4:80
This is b
$ curl http://1.2.3.4:80
This is a
$ curl http://1.2.3.4:80
This is b

Mais detalhes:

--

--