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:

  1. 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.
  2. O HAProxy roda em ambos os balanceadores o tempo todo, escutando no IP flutuante, e encaminha traffic para web01 e web02 com base em health checks.
  3. Se node-lb01 falhar (crash, queda de rede, HAProxy morre), o Keepalived em node-lb02 detecta a ausência do anúncio VRRP e se promove a MASTER, assumindo o IP flutuante em 1–3 segundos.
  4. O NGINX em web01 e web02 serve 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/443 para o IP flutuante a partir dos clientes
    • Porta 80 dos nós LB para os servidores web server
    • Protocolo VRRP (protocolo IP 112) e endereço multicast 224.0.0.18 entre os dois nós LB
  • 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 de net.ipv4.ip_nonlocal_bind = 1 (abordado abaixo), já que o IP flutuante não estará presente na interface do nó BACKUP.
  • option httpchk GET /healthz com fall 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 nada else anexado) 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 nova http-check send meth GET uri /healthz hdr Host example.com. Colocar esse detalhe extra no final da linha option 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 eth0 pelo nome real da sua interface de rede. Verifique com o comando ip a. Em muitas imagens de nuvem, é ens3, ens5 ou 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 o priority efetivo de node-lb01 cai abaixo de 100.
  • weight -60 em chk_haproxy - Esta é a peça que realmente liga o failover do Keepalived à saúde do HAProxy. No Keepalived, um peso negativo é subtraído do priority da instância assim que o script rastreado falha fall (3) vezes consecutivas. Um peso positivo faria o oposto (adicionaria ao priority em caso de sucesso) e não dispararia um failover aqui. Com weight -60, se o HAProxy morrer em node-lb01, o priority efetivo cai de 150 para 90 — abaixo do node-lb02 (100) — então node-lb02 assume 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 que auth_type PASS envia 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 nova auth_hmac do 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.