PostgreSQL repmgr con Keepalived Agregando un VIP Flotante

TL;DR: Un clúster de repmgr maneja la conmutación por error automática, pero las aplicaciones aún necesitan saber qué nodo es la instancia principal actual.

Keepalived resuelve esto con una IP Virtual (VIP) flotante que se mueve automáticamente al nodo que ostenta el rol principal.

Esta guía agrega un VIP a un clúster existente de PostgreSQL 18 + repmgr en Ubuntu 24.04 usando Keepalived 2.x.

Cada paso se ha ejecutado en tiempo real en un clúster real y la salida ha sido verificada.


Tu clúster de repmgr falla en 60 segundos.

Tu aplicación todavía apunta a la IP del primario antiguo.

Una VIP flotante resuelve esto: una dirección IP estable que siempre se conecta al nodo primario actual, independientemente de cuál sea ese nodo físico.

Keepalived implementa esto usando VRRP — un protocolo estándar diseñado exactamente para este propósito.

Esta guía añade un VIP a un clúster funcional de PostgreSQL + repmgr de dos nodos.

Si aún no tienes ese clúster, sigue las Guía de configuración de repmgr primero, luego vuelve aquí.



Cómo funciona

Keepalived ejecuta un script de verificación de estado en cada nodo cada 2 segundos.

El script se conecta a la instancia local de PostgreSQL a través de un socket Unix y consulta pg_is_in_recovery().

Si el nodo es el principal (la función devuelve f), el script sale 0 — Keepalived mantiene o reclama la VIP.

Si el nodo es un respaldo o PostgreSQL no es accesible (la función devuelve t o la conexión falla), el script sale 1 — Keepalived libera la VIP.

El nodo con la prioridad efectiva más alta tiene el VIP.

Server1 tiene una prioridad base de 100, server2 tiene una prioridad base de 90.

El script de verificación de salud está configurado con un peso de -50.

Cuando el script de un nodo falla, su prioridad efectiva se reduce en un 50%: server1 baja de 100 a 50, server2 de 90 a 40.

El nodo principal siempre gana; su script tiene éxito y su prioridad efectiva se mantiene en su valor base.


El medioambiente

Requisitos previos: un clúster PostgreSQL 18 + repmgr que funcione con dos nodos.

AnfitriónPIRol
servidor1 (Ubuntu 24.04)192.168.0.181Nodo de PostgreSQL
servidor2 (Ubuntu 24.04)192.168.0.182Nodo de PostgreSQL
VIP192.168.0.180Flota hasta el primario actual

Paso 1 — Instalar Keepalived en Ambos Servidores

En server1:

# On server1
sudo apt update
sudo apt install -y keepalived

keepalived --version
# Expected: Keepalived v2.x.x

En server2:

# On server2
sudo apt update
sudo apt install -y keepalived

keepalived --version

Paso 2: Cree el script de verificación de estado en ambos servidores

El script se conecta a la instancia local de PostgreSQL a través de un socket Unix y verifica si el nodo es el principal.

Sale 0 en el primario, 1 en un secundario o si PostgreSQL no es accesible.

Una advertencia es importante aquí: Keepalived ejecuta este script como el postgres Usuario del SO, configurado a través de script_usuario postgres postgres en definiciones\_globales.

Como el script ya se ejecuta como postgres, llama psql directamente — no usar ejecutarusuario o sudo -u postgres.

ejecutarusuario requiere acceso root y fallará silenciosamente cuando sea llamado por un usuario que no es root, causando que el script siempre salga con 1 en ambos nodos y que la VIP se comporte de forma incorrecta.

En server1:

# On server1
sudo tee /usr/local/bin/check_postgres_primary.sh > /dev/null << 'EOF'
#!/bin/bash
result=$(psql -t -c "SELECT pg_is_in_recovery();" 2>/dev/null | tr -d '[:space:]')
[ "$result" = "f" ]
EOF

# The script must be executable — Keepalived will not run it otherwise
sudo chmod +x /usr/local/bin/check_postgres_primary.sh

Prueba el script manualmente como el usuario postgres:

# On server1
sudo -u postgres /usr/local/bin/check_postgres_primary.sh
echo $?
# Expected: 0 if server1 is currently the primary, 1 if it is a standby
# Always test as postgres — running as root will give a different result

En server2:

# On server2
sudo tee /usr/local/bin/check_postgres_primary.sh > /dev/null << 'EOF'
#!/bin/bash
result=$(psql -t -c "SELECT pg_is_in_recovery();" 2>/dev/null | tr -d '[:space:]')
[ "$result" = "f" ]
EOF

sudo chmod +x /usr/local/bin/check_postgres_primary.sh

sudo -u postgres /usr/local/bin/check_postgres_primary.sh
echo $?
# Expected: 1 — server2 is currently a standby

Paso 3 — Configurar Keepalived en Ambos Servidores

Haz una copia de seguridad de la configuración predeterminada en ambos servidores antes de sobrescribirla:

sudo cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.20260519 2>/dev/null || true

configuración server1 (prioridad 100)

# On server1
sudo tee /etc/keepalived/keepalived.conf > /dev/null << 'EOF'
global_defs {
    # Run health check scripts as the postgres OS user
    # This allows the script to connect via Unix socket using peer authentication
    script_user postgres postgres
    enable_script_security
}

vrrp_script check_postgres {
    script "/usr/local/bin/check_postgres_primary.sh"
    # Run the check every 2 seconds
    interval 2
    # If the script fails, subtract 50 from this node's effective priority
    # server1 base priority is 100 — on failure it drops to 50, losing to server2 (base 90)
    weight -50
    # Number of consecutive failures before declaring the script failed
    fall 2
    # Number of consecutive successes before declaring the script recovered
    rise 2
}

vrrp_instance VI_POSTGRES {
    state BACKUP
    interface enp0s3
    virtual_router_id 51
    # Base priority — must differ between nodes; server1 is preferred primary candidate
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass pg_vip_2026
    }

    virtual_ipaddress {
        192.168.0.180/24
    }

    track_script {
        check_postgres
    }
}
EOF

configuración del servidor2 (prioridad 90)

# On server2
sudo tee /etc/keepalived/keepalived.conf > /dev/null << 'EOF'
global_defs {
    script_user postgres postgres
    enable_script_security
}

vrrp_script check_postgres {
    script "/usr/local/bin/check_postgres_primary.sh"
    interval 2
    # server2 base priority is 90 — on failure it drops to 40
    weight -50
    fall 2
    rise 2
}

vrrp_instance VI_POSTGRES {
    state BACKUP
    interface enp0s3
    virtual_router_id 51
    # Lower base priority than server1 — server1 holds the VIP when both are healthy
    priority 90
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass pg_vip_2026
    }

    virtual_ipaddress {
        192.168.0.180/24
    }

    track_script {
        check_postgres
    }
}
EOF

Ambos nodos usan estado COPIA DE SEGURIDAD.

Keepalived elige dinámicamente el máster basándose en la prioridad efectiva; no es necesario establecer uno en un nodo. estado MAESTRO.


Paso 4: Iniciar Keepalived y verificar la VIP

En server1:

# On server1
sudo systemctl enable keepalived
sudo systemctl start keepalived
sudo systemctl status keepalived
# Expected: active (running)
# If failed: sudo journalctl -u keepalived -n 30

En server2:

# On server2
sudo systemctl enable keepalived
sudo systemctl start keepalived
sudo systemctl status keepalived
# Expected: active (running)

Permita 5–10 segundos después del inicio para que las comprobaciones de estado se estabilicen, luego verifique que la VIP esté en la principal actual.

En server1:

# On server1
ip addr show enp0s3 | grep 192.168.0.180
# Expected: inet 192.168.0.180/24 — VIP is present if server1 is the current primary
# If not present and server1 IS the primary: wait 10 seconds and retry

En server2:

# On server2
ip addr show enp0s3 | grep 192.168.0.180
# Expected: no output — server2 is standby and does not hold the VIP

Añade el VIP .pgpass en ambos servidores para que repmgr pueda conectarse a través de él sin solicitar una contraseña:

# On server1
sudo -u postgres bash -c 'echo "192.168.0.180:5432:repmgr:repmgr:repmgr" >> /var/lib/postgresql/.pgpass'
sudo -u postgres bash -c 'echo "192.168.0.180:5432:replication:repmgr:repmgr" >> /var/lib/postgresql/.pgpass'
# On server2
sudo -u postgres bash -c 'echo "192.168.0.180:5432:repmgr:repmgr:repmgr" >> /var/lib/postgresql/.pgpass'
sudo -u postgres bash -c 'echo "192.168.0.180:5432:replication:repmgr:repmgr" >> /var/lib/postgresql/.pgpass'

Verificar que el VIP sea alcanzable y se conecte al primario:

# On server1 or server2
ping -c 3 192.168.0.180
# Expected: replies from 192.168.0.180

# Connect to PostgreSQL via VIP — must run as postgres OS user to use .pgpass
sudo -u postgres psql -h 192.168.0.180 -U repmgr -d repmgr -c "SELECT pg_is_in_recovery(), inet_server_addr();"
# Expected: f (false) | 192.168.0.180 — connected to the primary through the VIP

Paso 5 — Probar la conmutación por error VIP

Observe qué nodo tiene actualmente la VIP:

# On server1
ip addr show enp0s3 | grep 192.168.0.180
# Record which node holds the VIP before triggering the failover

Detenga PostgreSQL en el primario para activar el failover automático:

# On server1
sudo systemctl stop postgresql
# repmgrd on server2 will detect this and promote server2 after ~60 seconds

Ver Keepalived en server2 recoger la VIP:

# On server2
sudo journalctl -u keepalived -f
# Expected sequence:
#   Script check_postgres_primary.sh succeeded — server2 now primary after promotion
#   VRRP_Instance(VI_POSTGRES) Entering MASTER STATE

Verificar que el VIP se ha movido

# On server2
ip addr show enp0s3 | grep 192.168.0.180
# Expected: inet 192.168.0.180/24 — VIP is now on server2

psql -h 192.168.0.180 -U repmgr -d repmgr -c "SELECT pg_is_in_recovery(), inet_server_addr();"
# Expected: f | 192.168.0.182 — VIP now connects to server2, which is the new primary

Paso 6 — Probar el cambio de rol del VIP

Un cambio sin interrupciones también mueve la VIP.

El primario antiguo se convierte en secundario; su script de verificación de estado falla; la VIP se mueve al nuevo primario.

Antes de ejecutar un cambio, reintegre el nodo fallido de la prueba anterior como un nodo en espera.

En el nodo en espera (el nodo que se convertirá en el nuevo primario):

# On server1 (assuming server1 is currently standby)
sudo -u postgres repmgr standby switchover

Durante el cambio, repmgr detiene el primario antiguo a través de SSH, pero no lo reinicia automáticamente.

Inicia PostgreSQL manualmente en el nodo degradado cuando la salida del cambio de control muestra “esperando que el nodo X se conecte”:

# On the demoted node (the old primary)
sudo systemctl start postgresql

Verificar que el VIP se trasladó de regreso al servidor1

# On server1
ip addr show enp0s3 | grep 192.168.0.180
# Expected: inet 192.168.0.180/24 — VIP is back on server1

psql -h 192.168.0.180 -U repmgr -d repmgr -c "SELECT pg_is_in_recovery(), inet_server_addr();"
# Expected: f | 192.168.0.181 — VIP connects to server1, now the primary

Preguntas frecuentes

¿Por qué el script de verificación de salud utiliza psql directamente en lugar de sudo -u postgres psql?

Keepalived ejecuta el script como el usuario del sistema operativo postgres a través de script_usuario postgres postgres en definiciones\_globales.
El script ya se ejecuta como postgres, así que llamar psql se conecta directamente a través de un socket Unix con autenticación de par.
Utilizando ejecutarusuario o sudo -u postgres dentro del script fallará silenciosamente; ambos requieren ser root y el script no se está ejecutando como root.
El resultado es que ambos nodos siempre salen 1, y ninguno mantiene la VIP correctamente.

¿Por qué ambos nodos usan el estado BACKUP en lugar de uno usar el estado MASTER?

Con una comprobación de estado basada en peso, el maestro VRRP se determina dinámicamente por la prioridad efectiva, no por la estática estado directiva.
Si un nodo se establece en estado MAESTRO, reclamará el VIP al inicio independientemente del resultado de la comprobación del estado de salud, provocando una condición de carrera durante los primeros segundos.
Utilizando estado COPIA DE SEGURIDAD en ambos nodos, deja que el script de verificación de estado determine qué nodo debe tener la VIP desde el principio.

¿Cuánto tiempo tarda el VIP en moverse tras una conmutación por error?

El VIP se mueve una vez que se cumplen dos condiciones: repmgrd ha promovido el standby a primario (aproximadamente 60 segundos con ajustes predeterminados), y la verificación de estado de Keepalived ha confirmado la promoción (hasta otoño 2 × intervalo 2 = 4 segundos).
El tiempo total desde la falla primaria hasta el movimiento de VIP es de aproximadamente 60 a 70 segundos con la configuración de esta guía.

¿El VIP se mueve durante un cambio planificado?

Sí.
Cuándo repmgr standby switchover desgrada al primario anterior, el script de verificación de estado de ese nodo comienza a devolver 1 (porque el nodo ahora es un standby).
Keepalived detecta el cambio dentro de otoño 2 × intervalo 2 = 4 segundos y mueve al VIP al primario nuevo.

¿Qué es VRRP y por qué se usa aquí?

VRRP (Virtual Router Redundancy Protocol) es un protocolo de red estándar (RFC 5798) diseñado para proporcionar la asignación automática de routers IP a los hosts participantes.
Keepalived implementa VRRP para gestionar la VIP flotante —múltiples nodos participan en un grupo VRRP y eligen un maestro basándose en la prioridad.
El maestro tiene el VIP; si el maestro falla o su prioridad cae por debajo de la de otro nodo, se elige un nuevo maestro y el VIP se mueve.


En resumen

Keepalived agrega una VIP flotante a un clúster existente de PostgreSQL + repmgr con una configuración mínima.

El script de verificación de estado es el componente crítico: debe ejecutarse como el usuario del sistema operativo postgres y llamar psql directamente — no a través de ejecutarusuario o sudo.

Con otoño 2 y intervalo 2, la VIP se mueve dentro de los 4 segundos posteriores a que Keepalived detecte el cambio de rol, lo que sucede automáticamente después de que repmgrd promueve el standby.

Si está diseñando una arquitectura de alta disponibilidad para PostgreSQL y desea una segunda opinión antes de pasar a producción, ponerse en contacto

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *