Dia 13: Load Balancing — HAProxy, Nginx e LVS em Alta Disponibilidade
No Dia 13 do curso de redes, abordamos uma das peças fundamentais de qualquer infra-estrutura moderna: o balanceamento de carga. Quando um único servidor já não dá conta do tráfego, a resposta não é comprar uma máquina maior — é distribuir o trabalho entre várias. O HAProxy, o Nginx e o LVS são três ferramentas que fazem exactamente isso, cada uma com o seu nível de operação e caso de uso ideal.
Neste artigo:
- 1. Introdução ao Load Balancing
- 2. HAProxy — L4 e L7
- 3. Nginx como Load Balancer
- 4. LVS — Linux Virtual Server
- 5. Algoritmos de Distribuição
- 6. Health Checks e Session Persistence
- 7. Erros Comuns e Lista de Verificação
1. Introdução ao Load Balancing
Load balancing é a técnica de distribuir tráfego de rede entre múltiplos servidores de destino, de forma transparente para o cliente. O cliente liga-se a um endereço — normalmente um IP virtual (VIP) — e o balanceador de carga decide qual servidor de backend vai processar o pedido.
Existem dois níveis principais de operação:
| Nível | Designação | Funciona com | Exemplos |
|---|---|---|---|
| L4 | Transporte (TCP/UDP) | Portas e IPs — não inspecciona conteúdo | LVS, HAProxy em modo tcp |
| L7 | Aplicação (HTTP/HTTPS) | Headers, URLs, cookies, conteúdo | HAProxy em modo http, Nginx |
Um balanceador de carga de L4 é mais rápido e consome menos recursos — não desencapsula a carga útil. Um balanceador de carga de L7 é mais inteligente: pode encaminhar /api/* para um backend e /static/* para outro, com base no caminho do URL.
ℹ O balanceamento de carga distribui tráfego entre múltiplos servidores — aumenta disponibilidade e permite escalar horizontalmente sem indisponibilidade.
Os três actores principais que vamos cobrir são o HAProxy (dedicado, L4/L7), o Nginx (servidor web que também faz proxy inverso e balanceamento de carga L7) e o LVS (Linux Virtual Server — balanceamento de carga L4 no kernel). No Dia 11 cobrimos firewalls como pfSense e OPNsense; o balanceamento de carga é a peça complementar para escala.
2. HAProxy — L4 e L7
O HAProxy é o balanceador de carga código aberto mais usado em ambientes Linux. É dedicado — não serve conteúdo, apenas distribui ligações. Suporta os modos L4 (mode tcp) e L7 (mode http), com configuração em ficheiro único (/etc/haproxy/haproxy.cfg).
A estrutura do HAProxy divide-se em quatro secções: global (parâmetros do processo), defaults (valores herdados por todas as secções), frontend (ponto de entrada — onde os clientes se ligam) e backend (conjunto de servidores de destino).
Exemplo de configuração L7 com verificações de saúde e round-robin:
# HAProxy — frontend + backend
frontend web_front
bind *:80
default_backend web_servers
backend web_servers
balance roundrobin
option httpchk GET /health
server web1 192.168.1.10:80 check
server web2 192.168.1.11:80 check
Neste exemplo, o frontend escuta na porta 80 e envia todo o tráfego para o backend web_servers. O option httpchk faz um GET a /health em cada servidor; se um não responder, o HAProxy remove-o automaticamente da rotação. A directiva check em cada server activa a verificação de saúde para esse servidor.
Para operar em L4 (TCP puro, por exemplo para uma base de dados ou serviço não-HTTP), basta trocar mode http por mode tcp no frontend e no backend, removendo o option httpchk.
O HAProxy destaca-se pela sua página de estatísticas embutida. Basta adicionar um listen stats com stats enable e stats uri /haproxy?stats para ter um painel em tempo real com ligações activas, taxa de pedidos, erros e estado de cada servidor. Para alta disponibilidade do próprio balanceador de carga, o HAProxy é tipicamente combinado com Keepalived usando VRRP para comutação pós-falha entre dois nós HAProxy.
3. Nginx como Load Balancer
O Nginx é principalmente um servidor web, mas funciona excelentemente como proxy inverso e balanceador de carga L7. Ao contrário do HAProxy, o Nginx pode servir conteúdo estático directamente e fazer proxy de pedidos dinâmicos para os backends — tudo no mesmo processo.
A configuração de balanceamento de carga no Nginx gira em torno da directiva upstream, que define o conjunto de servidores de backend, e do proxy_pass dentro de um location, que encaminha o tráfego para esse upstream.
# Nginx — upstream
upstream backend {
least_conn;
server 192.168.1.10:80;
server 192.168.1.11:80;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
Neste exemplo, least_conn define o algoritmo de distribuição (menor número de ligações activas). Sem qualquer directiva de algoritmo, o Nginx usa round-robin por omissão. O proxy_pass envia cada pedido para o upstream backend, que por sua vez escolhe o servidor segundo o algoritmo configurado.
O Nginx suporta verificações de saúde passivos por omissão — se um servidor devolver erros 5xx ou tempo limite, o Nginx marca-o temporariamente como indisponível e deixa de lhe enviar pedidos durante fail_timeout segundos. Para verificações de saúde activos (o Nginx faz sondas periódicas mesmo sem tráfego), é necessário o módulo nginx_upstream_check_module ou o Nginx Plus (versão comercial).
Para preservar sessões (persistência de sessão), o Nginx oferece ip_hash — o mesmo IP de cliente vai sempre para o mesmo servidor. Alternativamente, pode-se usar cookies com sticky cookie no Nginx Plus.
| Característica | HAProxy | Nginx |
|---|---|---|
| Nível de operação | L4 e L7 | L7 (principalmente) |
| Serve conteúdo estático | Não | Sim |
| Health checks activos | Nativos | Requer módulo/Plus |
| Painel de estatísticas | Embutido | Requer módulo de terceiros |
| Foco principal | Load balancing puro | Servidor web + proxy |
4. LVS — Linux Virtual Server
O LVS (Linux Virtual Server) é balanceamento de carga de L4 implementado directamente no kernel do Linux, através do módulo IPVS (IP Virtual Server). É mais rápido e tem menos sobrecarga do que o HAProxy ou o Nginx, porque opera ao nível do IP e não inspecciona o conteúdo dos pacotes — mas também é menos flexível.
O LVS suporta três modos de encaminhamento:
- NAT (Network Address Translation): o LVS reescreve o IP de destino, o servidor responde ao LVS e este reescreve o IP de origem de volta para o cliente. O servidor não precisa de configurar nada de especial, mas o LVS é um gargalo porque todo o tráfego passa por ele em ambos os sentidos.
- DR (Direct Routing): o LVS reencaminha o pacote ao servidor modificando o MAC frame, mas o servidor responde directamente ao cliente. O LVS só processa o tráfego de entrada — muito mais eficiente. O servidor precisa de configurar o VIP na interface de loopback.
- TUN (Túnel IP): similar ao DR, mas o pacote é encapsulado num túnel IP. Permite que os servidores estejam em redes diferentes — útil em centros de dados distribuídos.
A ferramenta de linha de comando para gerir o LVS é o ipvsadm. O exemplo seguinte cria um serviço virtual na porta 80 com algoritmo round-robin e adiciona dois servidores reais em modo DR (Direct Routing, flag -g):
# LVS com ipvsadm
ipvsadm -A -t 192.168.1.100:80 -s rr
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.10:80 -g
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.11:80 -g
ipvsadm -L -n
As flags -A adiciona um serviço virtual, -a adiciona um servidor real, -t indica TCP (UDP seria -u), -s rr define o algoritmo round-robin e -g selecciona o modo gateway (DR). O -L -n lista a tabela IPVS actual com resolução numérica.
O LVS é frequentemente combinado com Keepalived — que gere o comutação pós-falha entre nós LVS usando VRRP e também faz verificações de saúde dos servidores reais, adicionando ou removendo-os da tabela IPVS conforme a sua disponibilidade. É a base do modelo de alta disponibilidade usado em muitos ISPs e centros de dados.
5. Algoritmos de Distribuição
O algoritmo de distribuição determina como o balanceador de carga escolhe o servidor de destino para cada ligação ou pedido. A escolha do algoritmo correcto tem impacto directo no desempenho e na equidade da distribuição.
| Algoritmo | Como funciona | Quando usar |
|---|---|---|
| Round-robin | Distribui pedidos em sequência circular: 1, 2, 3, 1, 2, 3… | Servidores com capacidade igual |
| Least-conn | Escolhe o servidor com menos ligações activas | Servidores com capacidade diferente ou pedidos de duração variável |
| Source hash | Hash do IP de origem — mesmo cliente vai sempre ao mesmo servidor | Sessões que precisam de persistência (sem cookies) |
No HAProxy, o algoritmo define-se com balance roundrobin, balance leastconn ou balance source. No Nginx, usa-se least_conn, ip_hash ou nada (round-robin por omissão). No LVS, as opções são -s rr (round-robin), -s lc (least-connection) e -s sh (source hashing).
Algoritmos avançados incluem weighted round-robin (atribui pesos a servidores — servidores mais potentes recebem mais pedidos), uri hash (HAProxy balance uri — mesmo URL vai ao mesmo servidor, útil para caches) e random (distribuição aleatória, útil em ambientes onde a sobrecarga de controlo de estado é indesejável).
6. Health Checks e Session Persistence
Health checks são verificações periódicas que o balanceador de carga faz a cada servidor de backend para determinar se está capaz de receber tráfego. Sem verificações de saúde, o balanceador de carga não tem forma de saber se um servidor caiu — continua a enviar-lhe pedidos, que falham.
⚠ Sem verificações de saúde, o balanceador de carga continua a enviar tráfego para servidores empanados — sempre configurar verificações de saúde activas.
Existem dois tipos de verificações de saúde:
- Activos: o balanceador de carga faz sondagens periódicas independentes (ex: GET a
/healthde 5 em 5 segundos). No HAProxy:option httpchk GET /health. No LVS com Keepalived: configurarTCP_CHECKouHTTP_GETno ficheirokeepalived.conf. - Passivos: o balanceador de carga observa as respostas aos pedidos reais. Se um servidor devolver 5xx ou tempo limite, é marcado temporariamente indisponível. O Nginx faz isto por omissão com
max_failsefail_timeout.
Session persistence (ou sessões fixas) garante que um cliente é sempre encaminhado para o mesmo servidor. É necessária quando a aplicação guarda estado localmente (cesto de compras, sessão em ficheiro) e não pode funcionar se o pedido for para um servidor diferente.
As três abordagens principais são:
- IP hash: o balanceador de carga calcula um hash do IP de origem do cliente. Simples mas problemático — todos os clientes atrás do mesmo NAT (uma empresa inteira) vão para o mesmo servidor, causando desequilíbrio.
- Cookie sticky: o balanceador de carga injecta um cookie no primeiro pedido (ex:
SERVERID=web1) e usa o cookie nos pedidos seguintes para encaminhar para o mesmo servidor. No HAProxy:cookie SERVERID insert indirect nocachecomcookie web1em cada servidor. - URL parameter: usa um parâmetro no URL para identificar o servidor. Menos comum, mas útil quando cookies não são viáveis.
A melhor prática é evitar persistência de sessão sempre que possível — tornar a aplicação sem estado e guardar o estado numa base de dados partilhada ou cache (ex: Redis). Isto permite que qualquer servidor atenda qualquer cliente, maximizando a flexibilidade do balanceador de carga.
7. Erros Comuns e Lista de Verificação
A configuração de balanceamento de carga tem armadilhas frequentes. Abaixo estão os erros mais comuns e uma lista de verificação para os evitar.
| Erro | Causa | Solução |
|---|---|---|
| Tráfego enviado para servidor caído | Health checks não configurados | Configurar option httpchk no HAProxy ou TCP_CHECK no Keepalived |
| Desequilíbrio de carga | Round-robin com servidores de capacidade diferente | Usar leastconn ou weighted round-robin |
| Sessões perdidas | Sem persistência de sessão em aplicação com estado | Configurar cookie sticky ou tornar a aplicação sem estado |
| Load balancer é ponto único de falha | Apenas um nó balanceador de carga | Dois nós em activo-espera com Keepalived/VRRP |
| Cliente atrás de NAT sobrecarrega um servidor | ip_hash com muitos clientes num só IP |
Usar cookie sticky em vez de IP hash |
| Lento em modo NAT | LVS em modo NAT — todo o tráfego passa pelo director | Mudar para modo DR (Direct Routing) |
Lista de verificação para produção:
- ✓ Health checks activos configurados em todos os backends
- ✓ Página de
/healthou equivalente implementada na aplicação - ✓ Tempo limite de verificação de saúde adequado (não demasiado curto — falsos negativos; nem demasiado longo — detecção lenta)
- ✓ Algoritmo de distribuição adequado ao padrão de tráfego
- ✓ Session persistence configurada se a aplicação for stateful
- ✓ Dois nós de balanceador de carga em activo-espera com comutação pós-falha automática (Keepalived/VRRP)
- ✓ Painel de estatísticas acessível e monitorizada
- ✓ Registos centralizados do balanceador de carga (syslog ou journald)
- ✓ Teste de comutação pós-falha: desligar um backend e verificar que o tráfego é redistribuído sem erros visíveis
- ✓ Teste de recuperação: voltar a ligar o backend e verificar que reentra na rotação
No próximo dia continuamos a explorar redes de alta disponibilidade. Para revisar conceitos de firewalls e segurança de perímetro, consulte o Dia 11 — Firewalls: pfSense e OPNsense, e para um exemplo prático de HAProxy com Keepalived, veja o artigo sobre HAProxy + Keepalived para Alta Disponibilidade.