Keepalived implementa el protocolo VRRP (Virtual Router Redundancy Protocol) en Linux: dos o más servidores comparten una IP virtual, uno de ellos la tiene asignada (MASTER) y, si deja de anunciarse o su servicio falla, otro (BACKUP) la asume en uno o dos segundos. Es la forma más sencilla de eliminar el punto único de fallo de un balanceador de carga. En esta guía configurarás Keepalived en dos servidores Ubuntu 24.04 que ejecutan HAProxy, con comprobación de salud del servicio, VRRP en modo unicast y un script de notificación, y probarás el failover.

Cómo funciona VRRP

  • Cada nodo tiene una prioridad entre 1 y 254. El de mayor prioridad se convierte en MASTER, se asigna la IP virtual y envía anuncios VRRP (protocolo IP 112) cada segundo.
  • Los nodos BACKUP escuchan esos anuncios. Si dejan de recibirlos durante unas tres veces el intervalo de anuncio, el de mayor prioridad pasa a MASTER, se asigna la IP y envía un ARP gratuito para que los equipos de la red actualicen la MAC asociada a la IP.
  • Un vrrp_script comprueba periódicamente un servicio local (aquí, HAProxy). Si falla, el nodo pasa al estado FAULT y libera la IP aunque el servidor siga encendido.
  • Todos los nodos de una misma instancia comparten el mismo virtual_router_id (1-255), que debe ser único en ese segmento de red.

Requisitos previos

Necesitas:

  • Dos servidores con Ubuntu 24.04 LTS, por ejemplo VPS o servidores dedicados de CubePath, con un usuario no root con privilegios sudo.
  • Una red privada común entre ellos. En esta guía:
NodoIP privadaPapel inicial
lb110.0.0.21MASTER
lb210.0.0.22BACKUP
  • Una IP libre en la misma subred para la IP virtual: 10.0.0.100.
  • HAProxy (o el servicio que quieras proteger) instalado y configurado igual en ambos nodos. Si aún no lo tienes, sigue primero la guía de balanceo de carga con HAProxy.

Paso 1: Instalar Keepalived

En ambos nodos, instala el paquete:

sudo apt update
sudo apt install keepalived

Comprueba la versión:

keepalived --version 2>&1 | head -n 1
Keepalived v2.2.8

El servicio no arrancará todavía, porque Ubuntu no incluye un archivo de configuración por defecto. Averigua el nombre de la interfaz conectada a la red privada, lo necesitarás más adelante:

ip -brief address
lo               UNKNOWN        127.0.0.1/8 ::1/128
eth0             UP             203.0.113.21/24
eth1             UP             10.0.0.21/24

En este ejemplo la red privada está en eth1. Sustitúyelo por el nombre que veas en tus servidores.

Paso 2: Permitir VRRP en el cortafuegos

VRRP no usa TCP ni UDP, sino su propio protocolo IP (112), que UFW no permite abrir con ufw allow. Añade una regla en /etc/ufw/before.rules en ambos nodos:

sudo nano /etc/ufw/before.rules

Justo antes de la línea COMMIT final de la sección *filter, añade esta regla, que acepta VRRP solo desde la red privada:

# Allow VRRP from the private network (Keepalived)
-A ufw-before-input -p 112 -s 10.0.0.0/24 -j ACCEPT

Recarga UFW:

sudo ufw reload

Verifica que la regla está cargada:

sudo iptables -S ufw-before-input | grep 112
-A ufw-before-input -s 10.0.0.0/24 -p vrrp -j ACCEPT

Paso 3: Permitir que HAProxy escuche en la IP virtual

En el nodo BACKUP la IP virtual no está asignada, así que HAProxy fallaría al arrancar si su configuración usa bind 10.0.0.100:80. El parámetro del kernel ip_nonlocal_bind permite enlazar a una IP que todavía no existe en el sistema. Créalo en ambos nodos:

echo "net.ipv4.ip_nonlocal_bind = 1" | sudo tee /etc/sysctl.d/60-keepalived.conf
sudo sysctl --system

Comprueba el valor:

sysctl net.ipv4.ip_nonlocal_bind
net.ipv4.ip_nonlocal_bind = 1

Si tu HAProxy escucha en todas las direcciones (bind :80), este paso no es estrictamente necesario, pero no hace daño.

Paso 4: Crear un usuario para los scripts

Keepalived ejecuta los scripts de comprobación con el usuario indicado en script_user. Con enable_script_security activado se niega a ejecutar scripts que puedan ser modificados por usuarios sin privilegios. Crea en ambos nodos un usuario de sistema dedicado:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin keepalived_script

Paso 5: Escribir el script de notificación

Keepalived puede ejecutar un script cada vez que el nodo cambia de estado. Aquí lo usarás para dejar constancia en el registro del sistema; puedes ampliarlo para avisar a tu sistema de monitorización. Créalo en ambos nodos:

sudo nano /usr/local/bin/keepalived-notify.sh
#!/usr/bin/env bash
set -euo pipefail

# Keepalived passes: $1 = INSTANCE or GROUP, $2 = name, $3 = new state
type="$1"
name="$2"
state="$3"

logger -t keepalived-notify "${type} ${name} changed to ${state} on $(hostname)"

Hazlo ejecutable y propiedad de root:

sudo chown root:root /usr/local/bin/keepalived-notify.sh
sudo chmod 755 /usr/local/bin/keepalived-notify.sh

Paso 6: Configurar Keepalived en el nodo MASTER

En lb1, crea el archivo de configuración:

sudo nano /etc/keepalived/keepalived.conf
global_defs {
    router_id lb1
    enable_script_security
    script_user keepalived_script
}

vrrp_script chk_haproxy {
    script "/usr/bin/systemctl is-active --quiet haproxy"
    interval 2
    fall 2
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth1
    virtual_router_id 51
    priority 150
    advert_int 1

    unicast_src_ip 10.0.0.21
    unicast_peer {
        10.0.0.22
    }

    virtual_ipaddress {
        10.0.0.100/24 dev eth1
    }

    track_script {
        chk_haproxy
    }

    notify /usr/local/bin/keepalived-notify.sh
}

Puntos clave de esta configuración:

  • chk_haproxy comprueba cada 2 segundos que el servicio HAProxy está activo. Tras 2 fallos seguidos (fall 2), el nodo pasa a FAULT y libera la IP; tras 2 éxitos (rise 2) vuelve a participar.
  • unicast_src_ip y unicast_peer hacen que los anuncios VRRP se envíen directamente al otro nodo en lugar de a la dirección multicast 224.0.0.18. Es la opción recomendada en redes de nube y en redes donde el multicast está filtrado, y evita interferir con otros grupos VRRP del mismo segmento.
  • virtual_router_id 51 debe ser idéntico en ambos nodos y distinto de cualquier otro grupo VRRP de la red.
  • Cambia eth1 por el nombre de tu interfaz privada.

Valida la sintaxis:

sudo keepalived -t -f /etc/keepalived/keepalived.conf && echo "OK"

Paso 7: Configurar Keepalived en el nodo BACKUP

En lb2, crea el mismo archivo con cuatro diferencias: el router_id, el estado inicial, una prioridad menor y las IP de origen y del par intercambiadas:

sudo nano /etc/keepalived/keepalived.conf
global_defs {
    router_id lb2
    enable_script_security
    script_user keepalived_script
}

vrrp_script chk_haproxy {
    script "/usr/bin/systemctl is-active --quiet haproxy"
    interval 2
    fall 2
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth1
    virtual_router_id 51
    priority 100
    advert_int 1

    unicast_src_ip 10.0.0.22
    unicast_peer {
        10.0.0.21
    }

    virtual_ipaddress {
        10.0.0.100/24 dev eth1
    }

    track_script {
        chk_haproxy
    }

    notify /usr/local/bin/keepalived-notify.sh
}

Valida también este archivo con sudo keepalived -t -f /etc/keepalived/keepalived.conf.

Paso 8: Arrancar Keepalived y comprobar el estado

Arranca el servicio y habilítalo en el arranque, primero en lb1 y después en lb2:

sudo systemctl enable --now keepalived

En lb1, comprueba que tiene asignada la IP virtual:

ip -brief address show eth1
eth1             UP             10.0.0.21/24 10.0.0.100/24

En lb2 la interfaz solo debe mostrar 10.0.0.22/24. Consulta el registro de cada nodo para ver su estado:

sudo journalctl -u keepalived -n 20 --no-pager

En lb1 verás una línea como:

Keepalived_vrrp[1234]: (VI_1) Entering MASTER STATE

Y en lb2, (VI_1) Entering BACKUP STATE. Desde otro equipo de la red privada, haz una petición a la IP virtual:

curl -s http://10.0.0.100

Paso 9: Probar el failover

Deja en un equipo cliente un bucle que haga una petición por segundo a la IP virtual, para ver cuánto dura el corte:

while sleep 1; do curl -s -o /dev/null -w "%{http_code}\n" --max-time 1 http://10.0.0.100; done

Fallo del servicio. En lb1, detén HAProxy:

sudo systemctl stop haproxy

En unos 4 segundos (dos comprobaciones fallidas), lb1 pasa a FAULT, libera la IP y lb2 la asume. En lb2, ip -brief address show eth1 mostrará ahora 10.0.0.100/24, y el script de notificación habrá dejado constancia:

sudo journalctl -t keepalived-notify --no-pager
keepalived-notify[2345]: INSTANCE VI_1 changed to MASTER on lb2

Vuelve a arrancar HAProxy en lb1. Como tiene mayor prioridad, recuperará la IP virtual:

sudo systemctl start haproxy

Fallo del servidor. Apaga lb1 o desconecta su interfaz privada. lb2 dejará de recibir anuncios y asumirá la IP en unos 3 segundos.

En el bucle del cliente verás como mucho unas pocas peticiones fallidas durante cada cambio.

Solución de problemas

Ambos nodos están en MASTER (split-brain). Los nodos no reciben los anuncios del otro. Comprueba que la regla de VRRP está cargada en UFW en los dos nodos, que unicast_peer apunta a la IP correcta y que virtual_router_id coincide. Puedes ver si llegan los anuncios con tcpdump:

sudo tcpdump -ni eth1 vrrp

Deberías ver un paquete por segundo desde el nodo MASTER con su prioridad (prio 150).

El nodo entra en FAULT aunque HAProxy funciona. Ejecuta el script como lo hace Keepalived para ver si falla:

sudo -u keepalived_script /usr/bin/systemctl is-active haproxy

Revisa también journalctl -u keepalived por mensajes de script security, que indican permisos incorrectos en el script o su directorio.

La IP virtual no aparece en ningún nodo. Suele ser un nombre de interfaz incorrecto en interface o virtual_ipaddress. El registro de Keepalived lo indica al arrancar.

La IP cambia de nodo pero los clientes no llegan. Los equipos de la red siguen usando la MAC antigua o la red filtra la IP virtual. Comprueba desde un cliente con ip neigh show 10.0.0.100 qué MAC tiene asociada y confirma con tu proveedor que la red permite IP adicionales no asignadas.

El estado cambia continuamente (flapping). La comprobación de salud es demasiado sensible o la red pierde paquetes. Sube fall y rise en vrrp_script y revisa la pérdida de paquetes entre los nodos con ping -c 100.

Conclusión

Tienes dos balanceadores compartiendo una IP virtual con Keepalived: si HAProxy o el servidor activo fallan, el otro nodo asume la IP en segundos y los clientes siguen llegando a la misma dirección. Como siguientes pasos puedes añadir una segunda instancia VRRP con otra IP virtual y prioridades invertidas para repartir carga entre ambos nodos (activo-activo), ampliar el script de notificación para avisar a tu sistema de alertas, o pasar a Pacemaker y Corosync si necesitas gestionar más recursos que una IP y un servicio.