Quando seu produto Software-as-a-Service (SaaS) é projetado para atender a um SLA de 99.99%, a infraestrutura precisa absorver falhas automaticamente. Um SLA de 99.99% permite apenas cerca de 52 minutos de indisponibilidade por ano. Se um balanceador de carga primário falhar às 2 AM de um domingo, o sistema precisa se recuperar em segundos, muito antes de um engenheiro intervir.
Um administrador de infraestrutura Linux experiente pode usar esta stack ao fornecer TI gerenciada para SaaS que exige operações altamente disponíveis e sem interrupção. A stack combina três componentes:
- NGINX - servindo sua aplicação em dois (ou mais) servidores web server
- HAProxy - distribuindo traffic entre esses servidores web server com health checks
- Keepalived - rodando em dois nós de balanceamento, compartilhando um IP flutuante (virtual), com failover automático via VRRP
Juntos, eles fornecem failover automático e balanceamento de carga resiliente, projetados para minimizar a interrupção do serviço.
Neste guia, explicaremos como construir a stack inteira do zero. A arquitetura consiste em dois balanceadores de carga compartilhando um único IP flutuante, com failover automático entre eles, na frente de dois servidores web server.
Os comandos e os arquivos de configuração foram testados no Ubuntu 26.04 LTS ("Resolute Raccoon") e também funcionam sem alterações no Ubuntu 24.04 LTS ("Noble Numbat").
Ao final deste guia, você terá uma infraestrutura SaaS altamente disponível, projetada para minimizar o tempo de inatividade e suportar aplicações que visam um SLA de 99.99%.
Visão geral da arquitetura
Internet / Clients
│
▼
Floating IP: 192.168.0.100
│
┌──────────────┴──────────────┐
│ │
node-lb01 (MASTER) node-lb02 (BACKUP)
192.168.0.11 192.168.0.12
Keepalived + HAProxy Keepalived + HAProxy
│ │
└──────────────┬───────────────┘
▼
┌─────────────┴─────────────┐
│ │
web01 web02
192.168.0.21 (NGINX) 192.168.0.22 (NGINX)
Como funciona:
- O Keepalived roda em ambos os balanceadores e usa VRRP (Virtual Router Redundancy Protocol) para eleger um nó como MASTER. O MASTER detém o IP flutuante
192.168.0.100. - O HAProxy roda em ambos os balanceadores o tempo todo, escutando no IP flutuante, e encaminha traffic para
web01eweb02com base em health checks. - Se
node-lb01falhar (crash, queda de rede, HAProxy morre), o Keepalived emnode-lb02detecta a ausência do anúncio VRRP e se promove a MASTER, assumindo o IP flutuante em 1–3 segundos. - O NGINX em
web01eweb02serve a aplicação real. O HAProxy verifica continuamente a saúde deles e remove automaticamente um nó morto da rotação.
Topologia
Pré-requisitos
- Cinco máquinas ou VMs Ubuntu 26.04 LTS (ou 24.04 LTS) conforme a tabela acima, todas na mesma rede de camada 2 (o VRRP depende de multicast/broadcast, então os dois nós LB devem compartilhar um subnet).
- Acesso root ou sudo em todas as máquinas.
- Regras de firewall permitindo:
- Porta
80/443para o IP flutuante a partir dos clientes - Porta
80dos nós LB para os servidores web server - Protocolo
VRRP(protocolo IP 112) e endereço multicast224.0.0.18entre os dois nós LB
- Porta
- Familiaridade básica com systemd e edição de arquivos de configuração via SSH.
O Ubuntu 26.04 vem com UFW (baseado em nftables internamente), mas ele vem desabilitado, então não interferirá até que você o ative. Se quiser habilitá-lo, estas são as regras correspondentes a esta topologia. Execute nos hosts apropriados antes de habilitar o UFW, para não se trancar para fora via SSH:
# On both node-lb01 and node-lb02
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow from 192.168.0.0/24 to any port 8404 proto tcp # stats page, LAN only
sudo ufw allow from 192.168.0.11 to 224.0.0.18 proto vrrp
sudo ufw allow from 192.168.0.12 to 224.0.0.18 proto vrrp
# On both web01 and web02
sudo ufw allow 22/tcp
sudo ufw allow from 192.168.0.11 to any port 80 proto tcp
sudo ufw allow from 192.168.0.12 to any port 80 proto tcp
# Then, on each host:
sudo ufw enable
Se seus dois balanceadores forem VMs de nuvem (AWS, Azure, GCP etc.) em vez de bare metal ou VMs locais, verifique a nota sobre VRRP unicast na Parte 6 antes de prosseguir. A maioria das redes de nuvem descarta silenciosamente o tráfego multicast que o VRRP usa por padrão, e o Keepalived parecerá funcionar, mas nunca fará failover de verdade.
Parte 1: Configurar os servidores web (web01, web02)
Execute esta seção em ambos web01 e web02.
1.1 Atualizar o sistema e instalar o NGINX
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx
1.2 Confirmar que o NGINX está em execução
sudo systemctl enable --now nginx
sudo systemctl status nginx
1.3 Criar uma página de teste que diferencie os servidores
Isso permite confirmar visualmente que o HAProxy está balanceando corretamente mais adiante. Execute o comando correspondente em cada host.
Em web01:
echo "<h1>Response from web01 (192.168.0.21)</h1>" | sudo tee /var/www/html/index.html
Em web02:
echo "<h1>Response from web02 (192.168.0.22)</h1>" | sudo tee /var/www/html/index.html
1.4 Adicionar um endpoint de health check leve
O HAProxy fará polling neste caminho para determinar se um backend está saudável. Mantê-lo separado da raiz da aplicação permite retornar um 200/503 controlado independentemente da lógica da aplicação.
echo "OK" | sudo tee /var/www/html/healthz
Verifique a partir de outra máquina:
curl http://192.168.0.21/healthz
curl http://192.168.0.22/healthz
Ambos devem retornar OK.
Parte 2: Configurar o HAProxy (node-lb01, node-lb02)
Execute esta seção em ambos os nós balanceadores. A configuração é idêntica nos dois.
2.1 Instalar o HAProxy
sudo apt update && sudo apt upgrade -y
sudo apt install -y haproxy
Os repositórios padrão do Ubuntu 26.04 fornecem HAProxy 3.2 diretamente, recente o suficiente para tudo neste guia (o Ubuntu 24.04 fornece HAProxy 2.8, que também funciona bem com a config abaixo). Portanto, não é necessário PPA ou repositório de terceiros.
Verifique o que você obteve:
haproxy -v
Ou,
sudo haproxy -v
Exemplo de saída:
HAProxy version 3.2.9-1ubuntu2.2 2026/06/19 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2030.
Known bugs: http://www.haproxy.org/bugs/bugs-3.2.9.html
Running on: Linux 7.0.0-28-generic #28-Ubuntu SMP PREEMPT_DYNAMIC Sun Jun 21 01:01:36 UTC 2026 x86_64
2.2 Configurar o HAProxy
Faça backup da config padrão e substitua-a:
sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak
sudo nano /etc/haproxy/haproxy.cfg
Use esta configuração:
global
log /dev/log local0
log /dev/log local1 notice
maxconn 4096
user haproxy
group haproxy
daemon
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5s
timeout client 30s
timeout server 30s
retries 3
# Stats page for quick visibility into backend health
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats auth admin:ChangeThisPassword!
frontend http_front
bind 192.168.0.100:80
default_backend web_servers
backend web_servers
balance roundrobin
option httpchk GET /healthz
http-check expect status 200
default-server inter 2s fall 3 rise 2
server web01 192.168.0.21:80 check
server web02 192.168.0.22:80 check
Notas sobre esta configuração:
bind 192.168.0.100:80- O HAProxy faz bind no IP flutuante, não no IP do próprio nó. Isso significa que o HAProxy precisa denet.ipv4.ip_nonlocal_bind = 1(abordado abaixo), já que o IP flutuante não estará presente na interface do nó BACKUP.option httpchk GET /healthzcomfall 3 rise 2- um backend é marcado como down após 3 falhas consecutivas e como saudável novamente após 2 sucessos consecutivos, verificado a cada 2 segundos. Isso evita flapping na queda de um único pacote.- Altere a senha da página de stats antes de implantar em qualquer coisa que se pareça com produção.
option httpchk GET /healthz(método + URI, sem nadaelseanexado) ainda é totalmente suportado no HAProxy 3.2 e é a forma mais simples para um check básico. Se quiser adicionar cabeçalhos personalizados ou uma versão HTTP específica à requisição de check, use a sintaxe mais novahttp-check send meth GET uri /healthz hdr Host example.com. Colocar esse detalhe extra no final da linhaoption httpchké obsoleto e vai gerar um aviso de config.
2.3 Permitir que o HAProxy faça bind em um IP não local
Como o IP flutuante só está fisicamente presente no nó que é MASTER no momento, ambos os nós precisam de permissão para fazer bind nele antecipadamente:
sudo sysctl -w net.ipv4.ip_nonlocal_bind=1
echo "net.ipv4.ip_nonlocal_bind = 1" | sudo tee -a /etc/sysctl.conf
2.4 Validar e iniciar o HAProxy
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl enable --now haproxy
sudo systemctl status haproxy
Em seguida, vamos configurar o Keepalived nos dois balanceadores.
Parte 3: Configurar o Keepalived (node-lb01, node-lb02)
3.1 Instalar o Keepalived
Em ambos os nós:
sudo apt install -y keepalived
3.2 Criar um script de health check
O Keepalived só deve anunciar o IP flutuante a partir de um nó se o HAProxy nesse nó estiver realmente vivo.
Crie este script em ambos os nós:
sudo tee /etc/keepalived/check_haproxy.sh > /dev/null << 'EOF'
#!/bin/bash
# Exit 0 = healthy, exit 1 = unhealthy
if systemctl is-active --quiet haproxy; then
exit 0
else
exit 1
fi
EOF
Torne-o executável:
sudo chmod +x /etc/keepalived/check_haproxy.sh
3.3 Configurar o Keepalived no node-lb01 (MASTER)
Crie um arquivo de configuração do Keepalived para o nó master:
sudo nano /etc/keepalived/keepalived.conf
Adicione o seguinte conteúdo:
vrrp_script chk_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 2
weight -60
fall 3
rise 2
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass ChangeThisSecret
}
virtual_ipaddress {
192.168.0.100/24
}
track_script {
chk_haproxy
}
}
Substitua
eth0pelo nome real da sua interface de rede. Verifique com o comandoip a. Em muitas imagens de nuvem, éens3,ens5ou similar.
3.4 Configurar o Keepalived no node-lb02 (BACKUP)
Mesmo arquivo, no segundo nó, com duas diferenças: state BACKUP e um priority menor.
sudo nano /etc/keepalived/keepalived.conf
Adicione as seguintes linhas:
vrrp_script chk_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 2
weight -60
fall 3
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass ChangeThisSecret
}
virtual_ipaddress {
192.168.0.100/24
}
track_script {
chk_haproxy
}
}
Principais parâmetros explicados:
virtual_router_id- Deve ser idêntico em ambos os nós; é assim que eles se reconhecem como parte do mesmo grupo VRRP. Deve ser único na LAN se outros grupos VRRP existirem.priority- O maior vence a eleição.node-lb01(150) torna-se MASTER quando ambos estão saudáveis;node-lb02(100) assume quando opriorityefetivo denode-lb01cai abaixo de 100.weight -60emchk_haproxy- Esta é a peça que realmente liga o failover do Keepalived à saúde do HAProxy. No Keepalived, um peso negativo é subtraído dopriorityda instância assim que o script rastreado falhafall(3) vezes consecutivas. Um peso positivo faria o oposto (adicionaria aopriorityem caso de sucesso) e não dispararia um failover aqui. Comweight -60, se o HAProxy morrer emnode-lb01, opriorityefetivo cai de 150 para 90 — abaixo donode-lb02(100) — entãonode-lb02assume o IP flutuante.auth_pass- Deve coincidir em ambos os nós. Impede que tráfego VRRP não relacionado na mesma rede interfira. Note queauth_type PASSenvia essa senha em texto claro na rede (apenas os primeiros 8 caracteres são realmente usados). Serve para evitar cross-talk acidental entre grupos VRRP não relacionados em uma LAN compartilhada, não para resistir a um agente hostil já nessa rede. Se precisar de proteção real contra tráfego VRRP falsificado, use a opção mais novaauth_hmacdo Keepalived.advert_int 1- O MASTER envia um anúncio a cada 1 segundo; o BACKUP considera o MASTER morto após aproximadamente 3 intervalos perdidos.
3.5 Iniciar o Keepalived
Em ambos os nós:
sudo systemctl enable --now keepalived
sudo systemctl status keepalived
3.6 Verificar o IP flutuante
Em node-lb01, confirme que agora ele é dono do IP flutuante:
ip a show eth0 | grep 192.168.0.100
ip a show eth0
Você deve ver 192.168.0.100/24 listado como endereço secundário no nó MASTER.
Exemplo de saída de node-lb01:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether bc:24:11:29:90:f6 brd ff:ff:ff:ff:ff:ff
altname enp0s18
altname enxbc24112990f6
inet 192.168.0.11/24 brd 192.168.0.255 scope global eth0
valid_lft forever preferred_lft forever
inet 192.168.0.100/24 scope global secondary proto 0x12 eth0
valid_lft forever preferred_lft forever
inet6 fe80::be24:11ff:fe29:90f6/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
Em node-lb02, o mesmo comando não deve mostrar nada. Ele está no estado BACKUP.
Exemplo de saída de node-lb02:
Verifique os logs do Keepalived para confirmar a transição de estado:
sudo journalctl -u keepalived -n 30 --no-pager
No nó MASTER, você veria:
[...]
Aug 09 08:52:57 node-lb01 Keepalived_vrrp[15913]: (VI_1) Entering MASTER STATE
No nó Backup, você veria:
Aug 09 08:52:57 node-lb02 Keepalived_vrrp[15682]: (VI_1) Entering BACKUP STATE
Parte 4: Teste de ponta a ponta
De qualquer máquina cliente na rede:
curl http://192.168.0.100/
Execute algumas vezes e você deve ver a resposta alternar entre "Response from web01" e "Response from web02" graças ao balanceamento round-robin do HAProxy.
Verifique a página de stats do HAProxy (de um navegador ou via túnel SSH, já que ela não fica exposta externamente nesta config):
http://192.168.0.11:8404/stats
http://192.168.0.12:8404/stats
Você deve ver tanto web01 quanto web02 listados como UP (verde).
Página de estatísticas do HAProxy no nó MASTER:
Parte 5: Testar o failover
Esta é a parte que realmente valida a afirmação de "alta disponibilidade". Não a pule.
5.1 Simular falha do balanceador de carga
Em node-lb01 (o MASTER atual), pare o Keepalived:
sudo systemctl stop keepalived
Dentro de alguns segundos, vá para node-lb02 e verifique o IP flutuante:
ip a show eth0 | grep 192.168.0.100
Ele agora deve mostrar o IP flutuante. Enquanto isso, execute um loop contínuo de curl a partir de um cliente para confirmar que não há interrupção significativa:
while true; do curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" http://192.168.0.100/; sleep 0.5; done
Você deve ver no máximo uma ou duas requisições com falha/lentas durante a transição; em seguida, o tráfego retoma normalmente através de node-lb02.
Reinicie o Keepalived em node-lb01 para restaurá-lo como MASTER (o priority maior faz com que ele recupere o IP flutuante automaticamente):
sudo systemctl start keepalived
Vá para node-lb02 e verifique se ele usa o IP flutuante. Desta vez, você não verá o IP flutuante.
5.2 Simular falha de um servidor web
Pare o NGINX em web01:
sudo systemctl stop nginx
Observe a página de stats do HAProxy. web01 deve mudar para DOWN em cerca de 6 segundos (3 falhas × intervalo 2s), e todo o tráfego deve migrar para web02 com zero erros visíveis ao cliente, já que o HAProxy para de rotear para um backend no momento em que ele é marcado como down.
Traga o NGINX de volta e confirme que web01 volta à rotação:
sudo systemctl start nginx
5.3 Simular falha do HAProxy no nó ativo
Pare o HAProxy (não o Keepalived) no nó que está MASTER no momento:
sudo systemctl stop haproxy
Por causa do bloco track_script, o Keepalived deve detectar isso via check_haproxy.sh e se rebaixar, entregando o IP flutuante ao outro nó, mesmo que o Keepalived em si ainda esteja rodando.
Agora tente acessar a página de stats do HAProxy do nó MASTER, ou seja, http://192.168.0.11:8404/stats. Ela não vai carregar.
Você ainda pode acessar a página de stats do HAProxy do nó BACKUP, ou seja, http://192.168.0.12:8404/stats, e ver as estatísticas de todos os nós.
Este é o cenário que uma configuração ingênua apenas com Keepalived-only (sem health check do HAProxy) perderia completamente, deixando o IP flutuante apontado para um nó com um proxy morto.
Parte 6: Endurecimento para produção
Uma demonstração de failover funcionando não é o mesmo que uma stack pronta para produção. Antes de colocar isto em produção, considere:
1. Terminação TLS
Este guia usou HTTP simples para clareza. Em produção, termine o TLS no HAProxy (ou no NGINX no backend, ou em ambos) usando o Let's Encrypt via certbot e redirecione a porta 80 para a 443.
2. VRRP unicast se o multicast não for confiável
Alguns provedores de nuvem (AWS, Azure, GCP) bloqueiam tráfego multicast/broadcast, do qual o VRRP depende por padrão. Use as diretivas unicast_src_ip e unicast_peer dentro de vrrp_instance para executar VRRP via unicast.
3. Regras de firewall
Bloqueie a página de stats do HAProxy (porta 8404) para apenas IPs internos e restrinja o tráfego VRRP de entrada entre os dois nós LB apenas entre eles.
4. Mais de dois servidores web backend
O padrão aqui se estende facilmente. Adicione mais linhas server ao bloco backend do HAProxy.
5. Monitoramento e alertas
Envie os logs do HAProxy e do Keepalived para um sistema central (ex.: Prometheus + o exporter do HAProxy, ou um agregador de logs) e alerte sobre transições de estado VRRP. Um failover funcionando corretamente é bom, mas você ainda quer saber que ele aconteceu.
6. Persistência de sessão
Se sua aplicação depende de sessões server-sside (não stateless/JWT), adicione cookie SRVID insert ao bloco backend, ou mova o estado da sessão para um armazenamento compartilhado como Redis, para que qualquer backend possa atender qualquer requisição.
7. Gerenciamento de configuração
Depois de validar manualmente, mova as configs do HAProxy e do Keepalived para Ansible, Terraform ou sua ferramenta de provisionamento preferida, para que ambos os nós LB permaneçam sincronizados e novos nós possam ser construídos de forma reproduzível.
Por que isso aproxima você dos 99.99%
A matemática de uptime é implacável. Como já dissemos, 99.99% permite aproximadamente 52 minutos e 36 segundos de indisponibilidade por ano, ou cerca de 4.3 minutos por mês. Um único balanceador de carga ou um único servidor web server pode facilmente estourar esse orçamento com uma atualização ruim do kernel ou uma falha de hardware.
Esta arquitetura remove pontos únicos de falha em duas camadas:
- Camada de balanceamento de carga: o failover VRRP do Keepalived normalmente completa em 1–3 segundos e, como ele também rastreia a saúde do próprio HAProxy (não apenas se a máquina está de pé), um processo de proxy travado ou com crash também dispara o failover.
- Camada de aplicação: os health checks ativos do HAProxy removem um servidor web server com falha da rotação em segundos, mantendo zero erros client-visíveis, e o tráfego continua sendo servido pelo(s) nó(s) restante(s).
O que esta stack não resolve por si só são bugs no nível da aplicação, failover de banco de dados, interrupções no nível de DNS ou falhas que derrubam ambos os nós LB simultaneamente (ex.: uma dependência compartilhada de energia/rede).
Portanto, se 99.99% é um compromisso de SLA rígido, certifique-se de que os dois balanceadores e os dois servidores web server estejam em domínios de falha genuinamente independentes (racks separados, zonas de disponibilidade ou hosts), não apenas VMs separadas no mesmo hipervisor.
Resumo
Você agora tem:
- Dois servidores web NGINX servindo conteúdo e um endpoint de health-ccheck
- Duas instâncias HAProxy balanceando a carga entre esses servidores web server com health checks ativos
- Duas instâncias Keepalived compartilhando um IP flutuante via VRRP, com failover ligado à saúde real do HAProxy, não apenas à vivacidade do nó
Este é o mesmo padrão usado nos bastidores por inúmeras plataformas SaaS de produção, e é totalmente open source, auto-hospedável e cloud-aagnóstico. A partir daqui, os próximos passos naturais são terminação TLS, automação de configuração e adição de monitoramento para que os failovers sejam visíveis em vez de silenciosos.



