{"id":6867,"date":"2026-05-27T14:52:10","date_gmt":"2026-05-27T12:52:10","guid":{"rendered":"https:\/\/rootfan.com\/?p=6867"},"modified":"2026-05-27T14:54:02","modified_gmt":"2026-05-27T12:54:02","slug":"postgresql-repmgr-con-keepalived","status":"publish","type":"post","link":"https:\/\/rootfan.com\/es\/postgresql-repmgr-with-keepalived\/","title":{"rendered":"PostgreSQL repmgr con Keepalived Agregando un VIP Flotante"},"content":{"rendered":"<p class=\"wp-block-paragraph\" id=\"tl-dr\">TL;DR: Un cl\u00faster de repmgr maneja la conmutaci\u00f3n por error autom\u00e1tica, pero las aplicaciones a\u00fan necesitan saber qu\u00e9 nodo es la instancia principal actual.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keepalived resuelve esto con una IP Virtual (VIP) flotante que se mueve autom\u00e1ticamente al nodo que ostenta el rol principal.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Esta gu\u00eda agrega un VIP a un cl\u00faster existente de PostgreSQL 18 + repmgr en Ubuntu 24.04 usando Keepalived 2.x.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cada paso se ha ejecutado en tiempo real en un cl\u00faster real y la salida ha sido verificada.<\/p>\n\n\n\n<!--more-->\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Tu cl\u00faster de repmgr falla en 60 segundos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tu aplicaci\u00f3n todav\u00eda apunta a la IP del primario antiguo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Una VIP flotante resuelve esto: una direcci\u00f3n IP estable que siempre se conecta al nodo primario actual, independientemente de cu\u00e1l sea ese nodo f\u00edsico.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keepalived implementa esto usando VRRP \u2014 un protocolo est\u00e1ndar dise\u00f1ado exactamente para este prop\u00f3sito.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Esta gu\u00eda a\u00f1ade un VIP a un cl\u00faster funcional de PostgreSQL + repmgr de dos nodos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si a\u00fan no tienes ese cl\u00faster, sigue las <a href=\"https:\/\/rootfan.com\/es\/configuracion-de-repmgr-en-postgresql\/\">Gu\u00eda de configuraci\u00f3n de repmgr<\/a> primero, luego vuelve aqu\u00ed.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>\u00cdndice<\/h2><nav><ul><li><a href=\"#how-it-works\">C\u00f3mo funciona<\/a><\/li><li><a href=\"#the-environment\">El medioambiente<\/a><\/li><li><a href=\"#step-1-install-keepalived-on-both-servers\">Paso 1 \u2014 Instalar Keepalived en Ambos Servidores<\/a><\/li><li><a href=\"#step-2-create-the-health-check-script-on-both-servers\">Paso 2: Cree el script de verificaci\u00f3n de estado en ambos servidores<\/a><\/li><li><a href=\"#step-3-configure-keepalived-on-both-servers\">Paso 3 \u2014 Configurar Keepalived en Ambos Servidores<\/a><ul><li><a href=\"#server-1-configuration-priority-100\">configuraci\u00f3n server1 (prioridad 100)<\/a><\/li><li><a href=\"#server-2-configuration-priority-90\">configuraci\u00f3n del servidor2 (prioridad 90)<\/a><\/li><\/ul><\/li><li><a href=\"#step-4-start-keepalived-and-verify-the-vip\">Paso 4: Iniciar Keepalived y verificar la VIP<\/a><\/li><li><a href=\"#step-5-test-vip-failover\">Paso 5 \u2014 Probar la conmutaci\u00f3n por error VIP<\/a><\/li><li><a href=\"#step-6-test-vip-switchover\">Paso 6 \u2014 Probar el cambio de rol del VIP<\/a><\/li><li><a href=\"#frequently-asked-questions\">Preguntas frecuentes<\/a><ul><li><a href=\"#faq-question-1779310385824\">\u00bfPor qu\u00e9 el script de verificaci\u00f3n de salud utiliza psql directamente en lugar de sudo -u postgres psql?<\/a><\/li><li><a href=\"#faq-question-1779310386824\">\u00bfPor qu\u00e9 ambos nodos usan el estado BACKUP en lugar de uno usar el estado MASTER?<\/a><\/li><li><a href=\"#faq-question-1779310387824\">\u00bfCu\u00e1nto tiempo tarda el VIP en moverse tras una conmutaci\u00f3n por error?<\/a><\/li><li><a href=\"#faq-question-1779310388824\">\u00bfEl VIP se mueve durante un cambio planificado?<\/a><\/li><li><a href=\"#faq-question-1779310389824\">\u00bfQu\u00e9 es VRRP y por qu\u00e9 se usa aqu\u00ed?<\/a><\/li><\/ul><\/li><li><a href=\"#in-summary\">En resumen<\/a><\/li><\/ul><\/nav><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 id=\"how-it-works\" class=\"wp-block-heading\">C\u00f3mo funciona<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Keepalived ejecuta un script de verificaci\u00f3n de estado en cada nodo cada 2 segundos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El script se conecta a la instancia local de PostgreSQL a trav\u00e9s de un socket Unix y consulta <code>pg_is_in_recovery()<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si el nodo es el principal (la funci\u00f3n devuelve <code>f<\/code>), el script sale 0 \u2014 Keepalived mantiene o reclama la VIP.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si el nodo es un respaldo o PostgreSQL no es accesible (la funci\u00f3n devuelve <code>t<\/code> o la conexi\u00f3n falla), el script sale 1 \u2014 Keepalived libera la VIP.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El nodo con la prioridad efectiva m\u00e1s alta tiene el VIP.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Server1 tiene una prioridad base de 100, server2 tiene una prioridad base de 90.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El script de verificaci\u00f3n de salud est\u00e1 configurado con un peso de -50.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El nodo principal siempre gana; su script tiene \u00e9xito y su prioridad efectiva se mantiene en su valor base.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 id=\"the-environment\" class=\"wp-block-heading\">El medioambiente<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Requisitos previos:<\/strong> un cl\u00faster PostgreSQL 18 + repmgr que funcione con dos nodos.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Anfitri\u00f3n<\/th><th>PI<\/th><th>Rol<\/th><\/tr><\/thead><tbody><tr><td>servidor1 (Ubuntu 24.04)<\/td><td>192.168.0.181<\/td><td>Nodo de PostgreSQL<\/td><\/tr><tr><td>servidor2 (Ubuntu 24.04)<\/td><td>192.168.0.182<\/td><td>Nodo de PostgreSQL<\/td><\/tr><tr><td>VIP<\/td><td>192.168.0.180<\/td><td>Flota hasta el primario actual<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 id=\"step-1-install-keepalived-on-both-servers\" class=\"wp-block-heading\">Paso 1 \u2014 Instalar Keepalived en Ambos Servidores<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En server1:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1\nsudo apt update\nsudo apt install -y keepalived\n\nkeepalived --version\n# Expected: Keepalived v2.x.x\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">En server2:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server2\nsudo apt update\nsudo apt install -y keepalived\n\nkeepalived --version\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 id=\"step-2-create-the-health-check-script-on-both-servers\" class=\"wp-block-heading\">Paso 2: Cree el script de verificaci\u00f3n de estado en ambos servidores<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">El script se conecta a la instancia local de PostgreSQL a trav\u00e9s de un socket Unix y verifica si el nodo es el principal.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sale 0 en el primario, 1 en un secundario o si PostgreSQL no es accesible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Una advertencia es importante aqu\u00ed: Keepalived ejecuta este script como el <code>postgres<\/code> Usuario del SO, configurado a trav\u00e9s de <code>script_usuario postgres postgres<\/code> en <code>definiciones\\_globales<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Como el script ya se ejecuta como postgres, llama <code>psql<\/code> directamente \u2014 no usar <code>ejecutarusuario<\/code> o <code>sudo -u postgres<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>ejecutarusuario<\/code> requiere acceso root y fallar\u00e1 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En server1:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1\nsudo tee \/usr\/local\/bin\/check_postgres_primary.sh &gt; \/dev\/null &lt;&lt; &#039;EOF&#039;\n#!\/bin\/bash\nresult=$(psql -t -c &quot;SELECT pg_is_in_recovery();&quot; 2&gt;\/dev\/null | tr -d &#039;&#x5B;:space:]&#039;)\n&#x5B; &quot;$result&quot; = &quot;f&quot; ]\nEOF\n\n# The script must be executable \u2014 Keepalived will not run it otherwise\nsudo chmod +x \/usr\/local\/bin\/check_postgres_primary.sh\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Prueba el script manualmente como el usuario postgres:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1\nsudo -u postgres \/usr\/local\/bin\/check_postgres_primary.sh\necho $?\n# Expected: 0 if server1 is currently the primary, 1 if it is a standby\n# Always test as postgres \u2014 running as root will give a different result\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">En server2:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server2\nsudo tee \/usr\/local\/bin\/check_postgres_primary.sh &gt; \/dev\/null &lt;&lt; &#039;EOF&#039;\n#!\/bin\/bash\nresult=$(psql -t -c &quot;SELECT pg_is_in_recovery();&quot; 2&gt;\/dev\/null | tr -d &#039;&#x5B;:space:]&#039;)\n&#x5B; &quot;$result&quot; = &quot;f&quot; ]\nEOF\n\nsudo chmod +x \/usr\/local\/bin\/check_postgres_primary.sh\n\nsudo -u postgres \/usr\/local\/bin\/check_postgres_primary.sh\necho $?\n# Expected: 1 \u2014 server2 is currently a standby\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 id=\"step-3-configure-keepalived-on-both-servers\" class=\"wp-block-heading\">Paso 3 \u2014 Configurar Keepalived en Ambos Servidores<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Haz una copia de seguridad de la configuraci\u00f3n predeterminada en ambos servidores antes de sobrescribirla:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nsudo cp \/etc\/keepalived\/keepalived.conf \/etc\/keepalived\/keepalived.conf.20260519 2&gt;\/dev\/null || true\n<\/pre><\/div>\n\n\n<h3 id=\"server-1-configuration-priority-100\" class=\"wp-block-heading\">configuraci\u00f3n server1 (prioridad 100)<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1\nsudo tee \/etc\/keepalived\/keepalived.conf &gt; \/dev\/null &lt;&lt; &#039;EOF&#039;\nglobal_defs {\n    # Run health check scripts as the postgres OS user\n    # This allows the script to connect via Unix socket using peer authentication\n    script_user postgres postgres\n    enable_script_security\n}\n\nvrrp_script check_postgres {\n    script &quot;\/usr\/local\/bin\/check_postgres_primary.sh&quot;\n    # Run the check every 2 seconds\n    interval 2\n    # If the script fails, subtract 50 from this node&#039;s effective priority\n    # server1 base priority is 100 \u2014 on failure it drops to 50, losing to server2 (base 90)\n    weight -50\n    # Number of consecutive failures before declaring the script failed\n    fall 2\n    # Number of consecutive successes before declaring the script recovered\n    rise 2\n}\n\nvrrp_instance VI_POSTGRES {\n    state BACKUP\n    interface enp0s3\n    virtual_router_id 51\n    # Base priority \u2014 must differ between nodes; server1 is preferred primary candidate\n    priority 100\n    advert_int 1\n\n    authentication {\n        auth_type PASS\n        auth_pass pg_vip_2026\n    }\n\n    virtual_ipaddress {\n        192.168.0.180\/24\n    }\n\n    track_script {\n        check_postgres\n    }\n}\nEOF\n<\/pre><\/div>\n\n\n<h3 id=\"server-2-configuration-priority-90\" class=\"wp-block-heading\">configuraci\u00f3n del servidor2 (prioridad 90)<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server2\nsudo tee \/etc\/keepalived\/keepalived.conf &gt; \/dev\/null &lt;&lt; &#039;EOF&#039;\nglobal_defs {\n    script_user postgres postgres\n    enable_script_security\n}\n\nvrrp_script check_postgres {\n    script &quot;\/usr\/local\/bin\/check_postgres_primary.sh&quot;\n    interval 2\n    # server2 base priority is 90 \u2014 on failure it drops to 40\n    weight -50\n    fall 2\n    rise 2\n}\n\nvrrp_instance VI_POSTGRES {\n    state BACKUP\n    interface enp0s3\n    virtual_router_id 51\n    # Lower base priority than server1 \u2014 server1 holds the VIP when both are healthy\n    priority 90\n    advert_int 1\n\n    authentication {\n        auth_type PASS\n        auth_pass pg_vip_2026\n    }\n\n    virtual_ipaddress {\n        192.168.0.180\/24\n    }\n\n    track_script {\n        check_postgres\n    }\n}\nEOF\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Ambos nodos usan <code>estado COPIA DE SEGURIDAD<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keepalived elige din\u00e1micamente el m\u00e1ster bas\u00e1ndose en la prioridad efectiva; no es necesario establecer uno en un nodo. <code>estado MAESTRO<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 id=\"step-4-start-keepalived-and-verify-the-vip\" class=\"wp-block-heading\">Paso 4: Iniciar Keepalived y verificar la VIP<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En server1:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1\nsudo systemctl enable keepalived\nsudo systemctl start keepalived\nsudo systemctl status keepalived\n# Expected: active (running)\n# If failed: sudo journalctl -u keepalived -n 30\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">En server2:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server2\nsudo systemctl enable keepalived\nsudo systemctl start keepalived\nsudo systemctl status keepalived\n# Expected: active (running)\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Permita 5\u201310 segundos despu\u00e9s del inicio para que las comprobaciones de estado se estabilicen, luego verifique que la VIP est\u00e9 en la principal actual.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En server1:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1\nip addr show enp0s3 | grep 192.168.0.180\n# Expected: inet 192.168.0.180\/24 \u2014 VIP is present if server1 is the current primary\n# If not present and server1 IS the primary: wait 10 seconds and retry\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">En server2:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server2\nip addr show enp0s3 | grep 192.168.0.180\n# Expected: no output \u2014 server2 is standby and does not hold the VIP\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">A\u00f1ade el VIP <code>.pgpass<\/code> en ambos servidores para que repmgr pueda conectarse a trav\u00e9s de \u00e9l sin solicitar una contrase\u00f1a:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1\nsudo -u postgres bash -c &#039;echo &quot;192.168.0.180:5432:repmgr:repmgr:repmgr&quot; &gt;&gt; \/var\/lib\/postgresql\/.pgpass&#039;\nsudo -u postgres bash -c &#039;echo &quot;192.168.0.180:5432:replication:repmgr:repmgr&quot; &gt;&gt; \/var\/lib\/postgresql\/.pgpass&#039;\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server2\nsudo -u postgres bash -c &#039;echo &quot;192.168.0.180:5432:repmgr:repmgr:repmgr&quot; &gt;&gt; \/var\/lib\/postgresql\/.pgpass&#039;\nsudo -u postgres bash -c &#039;echo &quot;192.168.0.180:5432:replication:repmgr:repmgr&quot; &gt;&gt; \/var\/lib\/postgresql\/.pgpass&#039;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Verificar que el VIP sea alcanzable y se conecte al primario:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1 or server2\nping -c 3 192.168.0.180\n# Expected: replies from 192.168.0.180\n\n# Connect to PostgreSQL via VIP \u2014 must run as postgres OS user to use .pgpass\nsudo -u postgres psql -h 192.168.0.180 -U repmgr -d repmgr -c &quot;SELECT pg_is_in_recovery(), inet_server_addr();&quot;\n# Expected: f (false) | 192.168.0.180 \u2014 connected to the primary through the VIP\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 id=\"step-5-test-vip-failover\" class=\"wp-block-heading\">Paso 5 \u2014 Probar la conmutaci\u00f3n por error VIP<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Observe qu\u00e9 nodo tiene actualmente la VIP:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1\nip addr show enp0s3 | grep 192.168.0.180\n# Record which node holds the VIP before triggering the failover\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Detenga PostgreSQL en el primario para activar el failover autom\u00e1tico:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1\nsudo systemctl stop postgresql\n# repmgrd on server2 will detect this and promote server2 after ~60 seconds\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Ver Keepalived en server2 recoger la VIP:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server2\nsudo journalctl -u keepalived -f\n# Expected sequence:\n#   Script check_postgres_primary.sh succeeded \u2014 server2 now primary after promotion\n#   VRRP_Instance(VI_POSTGRES) Entering MASTER STATE\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Verificar que el VIP se ha movido<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server2\nip addr show enp0s3 | grep 192.168.0.180\n# Expected: inet 192.168.0.180\/24 \u2014 VIP is now on server2\n\npsql -h 192.168.0.180 -U repmgr -d repmgr -c &quot;SELECT pg_is_in_recovery(), inet_server_addr();&quot;\n# Expected: f | 192.168.0.182 \u2014 VIP now connects to server2, which is the new primary\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 id=\"step-6-test-vip-switchover\" class=\"wp-block-heading\">Paso 6 \u2014 Probar el cambio de rol del VIP<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Un cambio sin interrupciones tambi\u00e9n mueve la VIP.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El primario antiguo se convierte en secundario; su script de verificaci\u00f3n de estado falla; la VIP se mueve al nuevo primario.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Antes de ejecutar un cambio, reintegre el nodo fallido de la prueba anterior como un nodo en espera.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En el nodo en espera (el nodo que se convertir\u00e1 en el nuevo primario):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1 (assuming server1 is currently standby)\nsudo -u postgres repmgr standby switchover\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Durante el cambio, repmgr detiene el primario antiguo a trav\u00e9s de SSH, pero no lo reinicia autom\u00e1ticamente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Inicia PostgreSQL manualmente en el nodo degradado cuando la salida del cambio de control muestra \u201cesperando que el nodo X se conecte\u201d:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On the demoted node (the old primary)\nsudo systemctl start postgresql\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Verificar que el VIP se traslad\u00f3 de regreso al servidor1<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# On server1\nip addr show enp0s3 | grep 192.168.0.180\n# Expected: inet 192.168.0.180\/24 \u2014 VIP is back on server1\n\npsql -h 192.168.0.180 -U repmgr -d repmgr -c &quot;SELECT pg_is_in_recovery(), inet_server_addr();&quot;\n# Expected: f | 192.168.0.181 \u2014 VIP connects to server1, now the primary\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 id=\"frequently-asked-questions\" class=\"wp-block-heading\">Preguntas frecuentes<\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list\">\n<div id=\"faq-question-1779310385824\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>\u00bfPor qu\u00e9 el script de verificaci\u00f3n de salud utiliza psql directamente en lugar de sudo -u postgres psql?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>Keepalived ejecuta el script como el usuario del sistema operativo postgres a trav\u00e9s de <code>script_usuario postgres postgres<\/code> en <code>definiciones\\_globales<\/code>.<br \/>El script ya se ejecuta como postgres, as\u00ed que llamar <code>psql<\/code> se conecta directamente a trav\u00e9s de un socket Unix con autenticaci\u00f3n de par.<br \/>Utilizando <code>ejecutarusuario<\/code> o <code>sudo -u postgres<\/code> dentro del script fallar\u00e1 silenciosamente; ambos requieren ser root y el script no se est\u00e1 ejecutando como root.<br \/>El resultado es que ambos nodos siempre salen 1, y ninguno mantiene la VIP correctamente.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1779310386824\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>\u00bfPor qu\u00e9 ambos nodos usan el estado BACKUP en lugar de uno usar el estado MASTER?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>Con una comprobaci\u00f3n de estado basada en peso, el maestro VRRP se determina din\u00e1micamente por la prioridad efectiva, no por la est\u00e1tica <code>estado<\/code> directiva.<br \/>Si un nodo se establece en <code>estado MAESTRO<\/code>, reclamar\u00e1 el VIP al inicio independientemente del resultado de la comprobaci\u00f3n del estado de salud, provocando una condici\u00f3n de carrera durante los primeros segundos.<br \/>Utilizando <code>estado COPIA DE SEGURIDAD<\/code> en ambos nodos, deja que el script de verificaci\u00f3n de estado determine qu\u00e9 nodo debe tener la VIP desde el principio.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1779310387824\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>\u00bfCu\u00e1nto tiempo tarda el VIP en moverse tras una conmutaci\u00f3n por error?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>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\u00f3n de estado de Keepalived ha confirmado la promoci\u00f3n (hasta <code>oto\u00f1o 2<\/code> \u00d7 <code>intervalo 2<\/code> = 4 segundos).<br \/>El tiempo total desde la falla primaria hasta el movimiento de VIP es de aproximadamente 60 a 70 segundos con la configuraci\u00f3n de esta gu\u00eda.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1779310388824\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>\u00bfEl VIP se mueve durante un cambio planificado?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>S\u00ed.<br \/>Cu\u00e1ndo <code>repmgr standby switchover<\/code> desgrada al primario anterior, el script de verificaci\u00f3n de estado de ese nodo comienza a devolver 1 (porque el nodo ahora es un standby).<br \/>Keepalived detecta el cambio dentro de <code>oto\u00f1o 2<\/code> \u00d7 <code>intervalo 2<\/code> = 4 segundos y mueve al VIP al primario nuevo.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1779310389824\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question\"><strong>\u00bfQu\u00e9 es VRRP y por qu\u00e9 se usa aqu\u00ed?<\/strong><\/h3>\n<div class=\"rank-math-answer\">\n\n<p>VRRP (Virtual Router Redundancy Protocol) es un protocolo de red est\u00e1ndar (RFC 5798) dise\u00f1ado para proporcionar la asignaci\u00f3n autom\u00e1tica de routers IP a los hosts participantes.<br \/>Keepalived implementa VRRP para gestionar la VIP flotante \u2014m\u00faltiples nodos participan en un grupo VRRP y eligen un maestro bas\u00e1ndose en la prioridad.<br \/>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.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 id=\"in-summary\" class=\"wp-block-heading\">En resumen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Keepalived agrega una VIP flotante a un cl\u00faster existente de PostgreSQL + repmgr con una configuraci\u00f3n m\u00ednima.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El script de verificaci\u00f3n de estado es el componente cr\u00edtico: debe ejecutarse como el usuario del sistema operativo postgres y llamar <code>psql<\/code> directamente \u2014 no a trav\u00e9s de <code>ejecutarusuario<\/code> o <code>sudo<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Con <code>oto\u00f1o 2<\/code> y <code>intervalo 2<\/code>, la VIP se mueve dentro de los 4 segundos posteriores a que Keepalived detecte el cambio de rol, lo que sucede autom\u00e1ticamente despu\u00e9s de que repmgrd promueve el standby.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si est\u00e1 dise\u00f1ando una arquitectura de alta disponibilidad para PostgreSQL y desea una segunda opini\u00f3n antes de pasar a producci\u00f3n, <a href=\"https:\/\/rootfan.com\/es\/servicios\/\">ponerse en contacto<\/a><\/p>","protected":false},"excerpt":{"rendered":"<p>TL;DR: Un cl\u00faster repmgr maneja la conmutaci\u00f3n autom\u00e1tica de errores, pero las aplicaciones a\u00fan necesitan saber cu\u00e1l nodo es el primario actual. Keepalived resuelve esto con una IP virtual (VIP) flotante que se mueve autom\u00e1ticamente al nodo que ostenta el rol principal. Esta gu\u00eda a\u00f1ade una VIP a un cl\u00faster existente de PostgreSQL 18 + repmgr en Ubuntu 24.04\u2026 <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/rootfan.com\/es\/postgresql-repmgr-with-keepalived\/\" class=\"more-link\">Seguir leyendo<span class=\"screen-reader-text\"> \u201cPostgreSQL repmgr con Keepalived A\u00f1adiendo una VIP Flotante\u201d<\/span><\/a><\/p>","protected":false},"author":1,"featured_media":6878,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_focus_keyword":"postgresql keepalived vip","rank_math_title":"PostgreSQL repmgr with Keepalived Adding a Floating VIP","rank_math_description":"Step-by-step guide to adding a floating Virtual IP to a PostgreSQL 18 + repmgr cluster using Keepalived on Ubuntu 24.04. The VIP moves automatically to the current primary after failover or switchover.","rank_math_robots":"","rank_math_og_title":"","rank_math_og_description":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[126],"tags":[127,81],"class_list":["post-6867","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-postgresql","tag-architecture","tag-step-by-step"],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/rootfan.com\/wp-content\/uploads\/8371682954_6b070daa8c_b-1.jpg?fit=1024%2C576&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/posts\/6867","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/comments?post=6867"}],"version-history":[{"count":9,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/posts\/6867\/revisions"}],"predecessor-version":[{"id":6880,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/posts\/6867\/revisions\/6880"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/media\/6878"}],"wp:attachment":[{"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/media?parent=6867"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/categories?post=6867"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rootfan.com\/es\/wp-json\/wp\/v2\/tags?post=6867"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}