Num sistema que precisa aguentar tráfego real, ter um único servidor respondendo a tudo é uma escolha que funciona até um certo ponto — e esse ponto chega mais cedo do que o esperado. Balanceamento de carga é a resposta para isso: em vez de um servidor tentando absorver tudo, um intermediário distribui as requisições entre várias instâncias, de forma que nenhuma delas seja sobrecarregada e o sistema inteiro continue respondendo mesmo que uma peça falhe.
O HAProxy é há anos uma das soluções mais usadas para isso. É open source, extremamente rápido, maduro e com uma documentação densa. Também é conhecido por uma configuração que intimida na primeira leitura — mas a estrutura é mais simples do que parece.
A lógica por trás da configuração
Um arquivo de configuração do HAProxy gira em torno de dois conceitos centrais: frontend e backend.
O frontend é o ponto de entrada — define em qual porta o HAProxy vai escutar e quais regras de roteamento aplicar antes de encaminhar a requisição. O backend é o grupo de servidores que vai de fato processar essa requisição. Entre os dois, o HAProxy decide quem recebe o quê, com base no algoritmo de balanceamento configurado.
Uma configuração funcional mínima se parece com isso:
global
log stdout format raw local0
maxconn 50000
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
option redispatch
retries 3
frontend http_in
bind *:80
default_backend servidores_app
backend servidores_app
balance roundrobin
option httpchk GET /health HTTP/1.1\r\nHost:\ localhost
http-check expect status 200
server app01 10.0.0.1:8080 check inter 3s rise 2 fall 3
server app02 10.0.0.2:8080 check inter 3s rise 2 fall 3
server app03 10.0.0.3:8080 check inter 3s rise 2 fall 3
Vale entender o que cada parâmetro do server está fazendo: check ativa a verificação de saúde periódica; inter 3s define o intervalo entre checks; rise 2 significa que o servidor precisa passar em 2 checks consecutivos para ser considerado saudável depois de uma falha; fall 3 significa que 3 falhas consecutivas o marcam como indisponível. Esses valores têm impacto direto em quanto tempo o HAProxy leva para perceber que um servidor caiu — e quanto tempo leva para confiar nele novamente.
Algoritmos de balanceamento
roundrobin é o padrão e o mais simples: cada servidor recebe requisições em sequência, em rotação. Funciona bem quando os servidores são homogêneos e as requisições têm duração similar.
Mas nem sempre é a escolha certa. Algumas alternativas importantes:
leastconn — encaminha para o servidor com menos conexões ativas no momento. Ideal para requisições de longa duração, como websockets ou uploads grandes, onde roundrobin acabaria concentrando carga desproporcional em alguns servidores.
source — usa o IP de origem para determinar sempre qual servidor vai receber a requisição de um cliente específico. Útil quando a aplicação não tem sessão compartilhada entre instâncias e você precisa que um mesmo usuário sempre chegue no mesmo servidor.
uri — roteia com base na URI da requisição. Usado em cenários de cache, onde você quer que a mesma URL sempre chegue no mesmo servidor para maximizar o hit de cache.
backend api_servers
balance leastconn
# resto da configuração...
Health checks que realmente funcionam
O check padrão do HAProxy verifica apenas se consegue abrir uma conexão TCP com o servidor. Isso não é suficiente: um servidor pode aceitar conexões e ainda assim estar completamente travado internamente.
O caminho correto é verificar um endpoint HTTP real que exercite a aplicação — idealmente um que cheque dependências críticas como banco de dados:
backend servidores_app
option httpchk GET /health HTTP/1.1\r\nHost:\ localhost
http-check expect status 200
# ou, para checar uma string específica no body:
# http-check expect string "healthy"
server app01 10.0.0.1:8080 check inter 3s rise 2 fall 3
O endpoint /health da aplicação deve retornar 200 apenas quando tudo estiver funcionando. Se o banco estiver inacessível, ele deve retornar 500 — e o HAProxy vai remover esse servidor do pool automaticamente.
TLS e terminação SSL
Em produção, o tráfego externo deve ser HTTPS. O HAProxy pode fazer a terminação TLS — recebe HTTPS do cliente, faz a comunicação interna com os backends em HTTP simples:
frontend https_in
bind *:443 ssl crt /etc/haproxy/certs/exemplo.pem
redirect scheme https if !{ ssl_fc }
# Boas práticas de segurança TLS
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:!aNULL:!MD5:!DSS
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
default_backend servidores_app
O arquivo .pem precisa conter o certificado e a chave privada concatenados. Se estiver usando Let's Encrypt, o Certbot gera os arquivos separados — é necessário concatená-los: cat fullchain.pem privkey.pem > /etc/haproxy/certs/exemplo.pem.
Observabilidade
O HAProxy expõe uma página de estatísticas que dá uma visão instantânea do estado de cada backend e servidor:
frontend stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats auth admin:senha_segura
Para ambientes com Prometheus, existe o haproxy_exporter que converte essas métricas para o formato que o Prometheus entende, permitindo dashboards no Grafana com latência por backend, taxa de erros, conexões ativas e estado de cada servidor.
As métricas mais importantes para acompanhar: haproxy_backend_http_responses_total (separada por código de status), haproxy_backend_response_time_average_seconds e haproxy_server_status (0 = down, 1 = up).
O que costuma dar errado
Timeouts mal calibrados são responsáveis por boa parte dos problemas difíceis de diagnosticar. O timeout server precisa ser maior do que o tempo máximo que qualquer requisição legítima pode levar. Se uma operação pesada leva 25 segundos e o timeout está em 30s, você está operando sem margem — e usuários vão ver erros esporádicos que parecem aleatórios.
Health check no endpoint errado também é comum. Verificar / ou uma rota que só retorna HTML estático não diz nada sobre o estado real da aplicação. O endpoint de health deve ser dedicado e expressivo.
Ausência de option redispatch significa que se um servidor cair no meio de uma requisição e não havia persistência de sessão, o cliente recebe um erro em vez de ser redirecionado automaticamente para outro servidor saudável.
A documentação oficial do HAProxy é densa mas bem organizada — para qualquer parâmetro que apareça numa configuração real, ela é a referência definitiva. Para quem usa Kubernetes, vale explorar também o ingress-nginx e o HAProxy Kubernetes Ingress Controller como alternativas nativamente integradas ao ecossistema de contêineres.