Guía práctica: configurar failover activo‑pasivo entre dos VPS con Keepalived, HAProxy y lsyncd para uploads sincronizados
Por Kevin Marcondes · Publicado el · 10 min de lectura
Cómo configurar failover activo‑pasivo entre dos VPS con Keepalived, HAProxy y lsyncd para sincronizar uploads. Este guía muestra la topología, prerequisitos y la configuración paso a paso para tener un VIP flotante con VRRP, HAProxy como entrada y lsyncd replicando uploads casi en tiempo real vía SSH. Enfocado en simplicidad y conmutación automática de nodo.
¿Qué es el failover activo‑pasivo con Keepalived, HAProxy y lsyncd?
El failover activo‑pasivo emplea dos VPS: una atiende (MASTER) y la otra queda en espera (BACKUP). Keepalived mueve una IP virtual (VIP) entre los nodos usando VRRP. HAProxy escucha en el VIP y enruta al servicio local. lsyncd replica los uploads del MASTER hacia el BACKUP por SSH.
Esta arquitectura reduce la indisponibilidad y mantiene los uploads coherentes. Es sencilla y no requiere un orquestador. A continuación se explica el papel de cada componente y los puntos de atención.
| Componente | Función | Puntos a atención |
|---|---|---|
| Keepalived (VRRP) | Mueve el VIP entre las VPS | Unicast en la nube; prioridad; healthchecks; ARP/GARP |
| HAProxy | Punto de entrada HTTP/HTTPS en el VIP | Health check de la app; ip_nonlocal_bind |
| lsyncd (rsync/SSH) | Replica uploads del MASTER → BACKUP | Claves SSH; exclusiones; evitar split‑brain |
| Aplicación | Atiende localmente en el nodo activo | Uploads en directorio dedicado; permisos |
| Firewall/Kernel | Permite VRRP/anuncio del VIP | arp_announce/ignore; rutas; journald/logrotate |
¿Qué prerequisitos y limitaciones en la VPS debo comprobar?
Verifica que ambas VPS compartan la misma subred o soporten VIP/floating IP. Algunos proveedores bloquean multicast o anuncios ARP. Si no hay L2 común, usa VRRP en unicast y prueba el anuncio del VIP.
Si el proveedor bloquea ARP/VRRP, el failover de VIP no funcionará. Alternativa: automatizar DNS con TTL bajo, asumiendo los trade‑offs de propagación. Asegura claves SSH entre nodos, NTP y un directorio exclusivo para uploads.
Para preparar el sistema y evitar cuellos de botella en kernel y logs, consulta este checklist para VPS Debian en producción: checklist técnico VPS Debian. Para políticas de retención y compresión del journald, consulta: Journald: retención y compresión en Ubuntu.
- Dos VPS en la misma subred o soporte a VIP/floating IP
- Acceso root por SSH para instalar y configurar paquetes
- Reloj sincronizado (chrony o systemd‑timesyncd)
- Directorio de uploads separado (p. ej.: /var/www/app/uploads)
- Puertos: HTTP/HTTPS para HAProxy; VRRP unicast entre los nodos
- Rotación de logs para no llenar disco
Preparar usuarios, directorios y claves SSH para lsyncd
Crea un usuario de sincronización con acceso restringido, ajusta permisos del directorio de uploads y cambia las claves SSH. Haz un respaldo del directorio de uploads antes de automatizar la sincronización.
En el ejemplo, nodo A = 10.0.0.10 (MASTER preferente) y nodo B = 10.0.0.11 (BACKUP). Ajusta IPs, rutas y usuarios a tu entorno.
# Em AMBAS as VPS (A e B):
sudo adduser --system --group --home /var/sync sync
sudo usermod -aG www-data sync
# Diretório de uploads da aplicação (se já existir, apenas ajuste permissões):
sudo mkdir -p /var/www/app/uploads
sudo chown -R www-data:www-data /var/www/app/uploads
sudo chmod 2755 /var/www/app/uploads # setgid para manter o grupo
# Gerar chave no nó A e autorizar no nó B (para rsync via SSH):
sudo -u sync mkdir -p /var/sync/.ssh && sudo chown -R sync:sync /var/sync/.ssh && sudo chmod 700 /var/sync/.ssh
sudo -u sync ssh-keygen -t ed25519 -C sync@vpsA -f /var/sync/.ssh/id_ed25519 -N ""
ssh-copy-id -i /var/sync/.ssh/id_ed25519.pub [email protected]
# Segurança no nó B (opcional avançado): restringir comandos no authorized_keys
# command="rrsync /var/www/app/uploads",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-ed25519 AAAA... sync@vpsA
Cómo configurar Keepalived (VRRP) en unicast con VIP flotante
Instala Keepalived, habilita los sysctl necesarios para ARP e ip_nonlocal_bind y crea una instancia VRRP por nodo. Usa prioridad mayor en el primario. Un vrrp_script debe bajar la prioridad si HAProxy para.
Usa scripts de notify para controlar lsyncd: en el MASTER realiza un rsync de calentamiento e inicia lsyncd; en BACKUP/FAULT detén lsyncd. Ajusta IPs, interfaz y VIP según tu entorno.
# Em AMBAS as VPS (ajuste interface e IPs):
# /etc/sysctl.d/99-vrrp.conf
net.ipv4.ip_nonlocal_bind=1
net.ipv4.conf.all.arp_ignore=1
net.ipv4.conf.all.arp_announce=2
net.ipv4.conf.default.arp_ignore=1
net.ipv4.conf.default.arp_announce=2
# Aplicar sysctl
# sysctl --system
# Instalar serviços essenciais
# apt-get update && apt-get install -y keepalived haproxy lsyncd
# /etc/keepalived/keepalived.conf (exemplo para o nó A - MASTER preferencial)
vrrp_script chk_haproxy {
script "/usr/bin/pgrep -x haproxy"
interval 2
fall 2
rise 2
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 150 # nó A mais prioritário
advert_int 1
authentication {
auth_type PASS
auth_pass 7c0m53gr3t
}
unicast_src_ip 10.0.0.10
unicast_peer {
10.0.0.11
}
track_script {
chk_haproxy
}
virtual_ipaddress {
10.0.0.50/24 dev eth0 label eth0:vip
}
notify_master "/etc/keepalived/notify.sh master"
notify_backup "/etc/keepalived/notify.sh backup"
notify_fault "/etc/keepalived/notify.sh fault"
}
# No nó B, mude:
# state BACKUP
# priority 100
# unicast_src_ip 10.0.0.11
# unicast_peer { 10.0.0.10 }
# notify.sh para iniciar/parar o lsyncd conforme o papel do nó
# /etc/keepalived/notify.sh
#-----------------------------------
#!/usr/bin/env bash
set -euo pipefail
ROLE="$1" # master|backup|fault
PEER_IP="10.0.0.11" # do ponto de vista do nó A; ajuste no B
SRC_DIR="/var/www/app/uploads"
DST="sync@${PEER_IP}:${SRC_DIR}"
LOGTAG="keepalived-notify"
case "$ROLE" in
master)
logger -t "$LOGTAG" "MASTER: warm-up rsync -> $DST"
rsync -az --inplace --partial "$SRC_DIR/" "$DST/" || true
systemctl restart lsyncd
;;
backup|fault)
logger -t "$LOGTAG" "${ROLE^^}: stopping lsyncd"
systemctl stop lsyncd || true
;;
*)
logger -t "$LOGTAG" "unknown role: $ROLE" ;;
esac
#-----------------------------------
# chmod 750 /etc/keepalived/notify.sh && chown root:root /etc/keepalived/notify.sh
# systemctl enable --now keepalived
Configurar HAProxy para atender en el VIP
HAProxy debe escuchar en 0.0.0.0; cuando el VIP esté en el nodo, los clientes llegarán por él. El health check del backend soporta el vrrp_script de Keepalived. Sustituye el puerto 8080 por el de tu aplicación.
Para TLS y ACME puedes ampliar la configuración más adelante. Más ideas y configuraciones en: HAProxy con Let's Encrypt y healthchecks.
# /etc/haproxy/haproxy.cfg (mínimo funcional)
global
log /dev/log local0
maxconn 2000
daemon
defaults
mode http
log global
option httplog
timeout connect 5s
timeout client 30s
timeout server 30s
frontend fe_http
bind 0.0.0.0:80
http-request set-header X-Forwarded-Proto http
default_backend be_app
backend be_app
option httpchk GET /health
server app1 127.0.0.1:8080 check
# Valide e habilite
# haproxy -c -f /etc/haproxy/haproxy.cfg && systemctl enable --now haproxy
Cómo sincronizar uploads solo en el nodo MASTER con lsyncd
lsyncd vigila cambios y lanza rsync vía SSH del MASTER al BACKUP. Para evitar split‑brain, debe ejecutarse únicamente en el nodo que tiene el VIP. notify.sh de Keepalived se encarga de esto.
Usa exclusiones para archivos temporales y activa --inplace para reducir escrituras duplicadas. Atención con --delete: úsalo solo después de validar la dirección de la sincronización.
-- /etc/lsyncd/lsyncd.lua
settings {
logfile = "/var/log/lsyncd.log",
statusFile = "/var/run/lsyncd.status",
nodaemon = false,
maxDelays = 5,
}
default.rsyncssh {
source = "/var/www/app/uploads",
host = "[email protected]", -- destino (nó B)
targetdir = "/var/www/app/uploads",
rsync = {
archive = true,
compress = true,
verbose = true,
_extra = { "--inplace", "--partial" }
-- Para espelhamento completo, adicione: "--delete" (use com cautela)
},
delay = 2,
excludeFrom = "/etc/lsyncd.exclude"
}
-- /etc/lsyncd.exclude (exemplo)
-- *.tmp
-- .*swp
-- .DS_Store
-- Habilite o serviço (start/stop será controlado pelo Keepalived):
-- systemctl enable lsyncd
Cómo probar el failover y confirmar que funcionó
Verifica qué nodo tiene el VIP, realiza un upload de prueba y simula la caída del servicio. HAProxy debe exponer una ruta /health simple. El VIP debe migrar en segundos si el health check falla.
Tras estabilizar, valida que el archivo de prueba llegó al BACKUP. Revisa logs para analizar tiempos de conmutación y la sincronización.
# 1) Quem é o MASTER (deve mostrar 10.0.0.50 no nó ativo)
ip -br a | grep 10.0.0.50 || echo "VIP não está neste nó"
# 2) Teste HTTP pelo VIP
curl -sS http://10.0.0.50/health || echo "sem resposta"
# 3) Upload/arquivo de teste no MASTER e checar no BACKUP
TS=$(date +%s)
echo "ok-$TS" | sudo -u www-data tee /var/www/app/uploads/test-$TS.txt >/dev/null
ssh [email protected] "ls -l /var/www/app/uploads/test-$TS.txt"
# 4) Simule falha do HAProxy no MASTER e observe failover
sudo systemctl stop haproxy
sleep 3
curl -sS http://10.0.0.50/health || echo "sem resposta"
# 5) Logs úteis
journalctl -u keepalived -n 50 --no-pager
journalctl -u lsyncd -n 50 --no-pager
Buenas prácticas, observabilidad y problemas comunes
Monitorea logs, uso de disco y latencia de sincronización. Rota logs para que no afecten VRRP ni lsyncd. Mantén healthchecks simples y confiables. Evita que la aplicación acepte escrituras en el BACKUP.
Si algo falla, revisa red, ARP/VRRP y permisos del directorio de uploads. Para riesgos de split‑brain en escenarios de datos, consulta también cómo evitar split‑brain en failover de PostgreSQL: evitar split‑brain en failover de PostgreSQL.
- Usa healthchecks rápidos (/health) en HAProxy y un track_script sencillo
- Prioridad del MASTER mayor; no cambies virtual_router_id
- Permite tráfico de VRRP unicast entre las IPs de las VPS
- Revisa permisos del directorio (setgid y grupo de la app)
- Rota logs de systemd‑journald para evitar disco lleno
- Documenta runbooks y prueba el failover periódicamente
Preguntas frecuentes
¿Necesitan estar las dos VPS en la misma red para que el VIP funcione?
Sí. En la mayoría de los casos ambas VPS deben compartir la misma subred o usar el floating IP del proveedor. Sin L2 común o soporte a ARP/VRRP, el VIP no se propagará entre nodos. Alternativa: automatizar DNS con TTL bajo, aceptando la latencia de propagación.
¿Cómo evitar split‑brain en los uploads con lsyncd?
Ejecuta lsyncd únicamente en el nodo MASTER, controlado por el notify de Keepalived. Haz un rsync de calentamiento al asumir MASTER y, si usas --delete, valida la dirección de la sincronización. Evita que la app escriba en ambos nodos simultáneamente.
¿Cuál es la latencia típica de sincronización con lsyncd?
Es casi en tiempo real, pero depende de I/O, CPU, red y número de archivos. Los cambios se agrupan con pequeños retrasos (p. ej. 2 a 5 segundos) para eficiencia. En picos con muchos ficheros, la sincronización inicial puede tardar más antes de estabilizarse.
¿Funciona con IPv6 para el VIP?
Funciona si el proveedor admite anuncios de VIP IPv6 y la red permite Neighbor Advertisements (análogos a ARP en IPv4). En muchas nubes es más sencillo usar el floating IP del proveedor. Ajusta Keepalived y HAProxy para direcciones IPv6 si es necesario.
¿Puedo usar NFS o un almacenamiento compartido en lugar de lsyncd?
Sí, pero añade complejidad y posible punto único de fallo. NFS requiere tuning, reglas de firewall y pruebas de bloqueo. lsyncd es simple para uploads ocasionales y directorios pequeños‑medios. Para volúmenes grandes o escritura concurrente, evalúa un storage distribuido.
¿Quieres revisar tu topología de failover antes de producción?
Envía un esquema de tu red (IPs, subred, VIP deseado) y los requisitos de uploads. Una revisión externa ayuda a detectar puntos frágiles y definir pruebas de failover realistas.
Hablar sobre mi escenarioVer también
- Guía: checklist técnico para preparar un VPS Debian con Docker Compose para aplicaciones PHP en producción
- Guía práctica para dimensionamiento de VPS y tuning de PHP‑FPM para tiendas Magento/OpenMage con picos estacionales: del dimensionamiento a la prueba de carga
- Guía: cómo planificar y ejecutar pruebas de carga en aplicaciones PHP monolito usando k6 y Docker Compose — paso a paso
- Guía de backup y recuperación PostgreSQL con wal‑shipping y basebackup en VPS con espacio limitado: práctica de bajo uso de disco