{"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: A repmgr cluster handles automatic failover \u2014 but applications still need to know which node is the current primary. Keepalived solves this with a floating Virtual IP (VIP) that moves automatically to whichever node holds the primary role. This guide adds a VIP to an existing PostgreSQL 18 + repmgr cluster on Ubuntu 24.04 &hellip; <\/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\"> &#8220;PostgreSQL repmgr with Keepalived Adding a Floating VIP&#8221;<\/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_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}]}}