--- title: "Alta disponibilidad en varias regiones con PostgreSQL y DNS" description: "Diseña una arquitectura activa-pasiva entre dos regiones con WireGuard, replicación de PostgreSQL 16 y cambio por DNS, y ensaya un failover sin split-brain." category: "Migración y Recuperación ante Desastres" language: es source: https://cubepath.com/es/docs/migración-y-recuperación-ante-desastres/arquitectura-de-alta-disponibilidad-en-multiples-regiones updated: 2026-09-25 --- # Alta disponibilidad en varias regiones con PostgreSQL y DNS Replicar servidores dentro de un mismo centro de datos te protege de un fallo de hardware, pero no de un corte de red, de energía o de un incidente que afecte a toda la ubicación. Una arquitectura en varias regiones mantiene una copia viva de tu servicio en otra ubicación geográfica, lista para recibir el tráfico. En esta guía verás los patrones posibles y sus límites reales, y montarás el más habitual y robusto para equipos pequeños: una arquitectura activa-pasiva entre dos regiones, con un túnel WireGuard, replicación en streaming de PostgreSQL 16 sobre Ubuntu 24.04 y conmutación por DNS con un procedimiento de failover ensayado. ## Requisitos previos Para seguir esta guía necesitas: - Dos servidores con Ubuntu 24.04 LTS en regiones distintas, por ejemplo dos VPS de CubePath en ubicaciones diferentes. En los ejemplos, la región A (primaria) tiene la IP pública `region_a_ip` y la región B (secundaria), `region_b_ip`. - Un usuario no root con privilegios `sudo` en ambos. - PostgreSQL 16 instalado en los dos servidores (`sudo apt install postgresql`). - Un dominio con DNS gestionado en un proveedor que permita cambiar registros rápidamente, idealmente por API. ## Elegir el patrón: activo-pasivo o activo-activo | Patrón | Cómo funciona | Ventajas | Inconvenientes | |--------|---------------|----------|----------------| | Activo-pasivo | Una región atiende todo; la otra recibe réplicas y espera | Sencillo, sin conflictos de escritura, coste bajo | La región pasiva está infrautilizada; el failover lleva minutos | | Activo-pasivo con lecturas | Como el anterior, pero la región B sirve lecturas | Aprovecha la réplica, menor latencia de lectura para usuarios cercanos | La aplicación debe separar lecturas y escrituras | | Activo-activo | Ambas regiones aceptan escrituras | Sin tiempo de conmutación, latencia baja para todos | Conflictos de escritura, necesita bases de datos distribuidas o particionar los datos por región | El activo-activo real es mucho más difícil de lo que parece: dos regiones que aceptan escrituras sobre los mismos datos generan conflictos, y la latencia entre regiones (decenas o cientos de milisegundos) se suma a cada transacción que tenga que coordinarse. Salvo que tu aplicación esté diseñada para ello, empieza por activo-pasivo. Define también dos objetivos, porque condicionan todas las decisiones: - **RPO** (*Recovery Point Objective*): cuántos datos puedes permitirte perder. Con replicación asíncrona, son los segundos de retraso de la réplica en el momento del fallo. - **RTO** (*Recovery Time Objective*): cuánto puede durar el corte. Con conmutación por DNS, es el tiempo de detección y decisión, más la promoción, más el TTL del registro. ## Paso 1: Conectar las regiones con WireGuard La replicación no debe viajar en claro por Internet ni exponer PostgreSQL públicamente. Un túnel WireGuard entre las dos regiones crea una red privada cifrada: `10.10.0.1` para la región A y `10.10.0.2` para la B. En **ambos servidores**, instala WireGuard y genera un par de claves: ```bash sudo apt install wireguard wg genkey | sudo tee /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key sudo chmod 600 /etc/wireguard/private.key ``` El último comando muestra la clave pública; anota la de cada servidor. En la **región A**, crea la configuración del túnel: ```bash sudo nano /etc/wireguard/wg0.conf ``` ```ini [Interface] Address = 10.10.0.1/24 ListenPort = 51820 PrivateKey = contenido_de_private.key_de_A [Peer] PublicKey = clave_publica_de_B Endpoint = region_b_ip:51820 AllowedIPs = 10.10.0.2/32 PersistentKeepalive = 25 ``` En la **región B**, crea el mismo archivo con los valores simétricos: ```ini [Interface] Address = 10.10.0.2/24 ListenPort = 51820 PrivateKey = contenido_de_private.key_de_B [Peer] PublicKey = clave_publica_de_A Endpoint = region_a_ip:51820 AllowedIPs = 10.10.0.1/32 PersistentKeepalive = 25 ``` En **ambos**, protege el archivo, abre el puerto de WireGuard solo para la otra región y levanta el túnel (en la región B, usa `region_a_ip` en la regla): ```bash sudo chmod 600 /etc/wireguard/wg0.conf sudo ufw allow from region_b_ip to any port 51820 proto udp sudo systemctl enable --now wg-quick@wg0 ``` Comprueba el túnel desde la región A: ```bash sudo wg show wg0 latest-handshakes ping -c 3 10.10.0.2 ``` El `ping` debe responder y el tiempo que muestra es la latencia real entre regiones. Anótalo: te dirá si la replicación síncrona es viable (lo verás más adelante). ## Paso 2: Preparar el primario en la región A Crea un usuario dedicado a la replicación. `\password` pide la contraseña sin que quede en el historial: ```bash sudo -u postgres psql ``` ```sql CREATE ROLE replicator WITH REPLICATION LOGIN; \password replicator \q ``` Configura PostgreSQL para aceptar conexiones de replicación. Crea un archivo en `conf.d`, que Ubuntu carga automáticamente: ```bash sudo nano /etc/postgresql/16/main/conf.d/replication.conf ``` ```ini listen_addresses = '*' wal_level = replica max_wal_senders = 10 max_slot_wal_keep_size = '20GB' ``` `listen_addresses = '*'` evita que PostgreSQL falle al arrancar si el túnel aún no está levantado; el acceso real lo limitan `pg_hba.conf` y el cortafuegos. `max_slot_wal_keep_size` pone un tope al WAL que el primario retiene para una réplica caída, para que no llene el disco. Permite la conexión de replicación solo desde la IP del túnel de la región B: ```bash sudo nano /etc/postgresql/16/main/pg_hba.conf ``` Añade al final: ```text host replication replicator 10.10.0.2/32 scram-sha-256 ``` Abre el puerto 5432 solo en la interfaz del túnel y reinicia PostgreSQL: ```bash sudo ufw allow in on wg0 from 10.10.0.2 to any port 5432 proto tcp sudo systemctl restart postgresql@16-main ``` ## Paso 3: Crear la réplica en la región B En la **región B**, detén PostgreSQL y vacía su directorio de datos, que se reemplazará por una copia del primario: ```bash sudo systemctl stop postgresql@16-main sudo -u postgres bash -c 'rm -rf /var/lib/postgresql/16/main/*' ``` Clona el primario con `pg_basebackup`. `-R` escribe la configuración de réplica (`standby.signal` y `primary_conninfo`), y `-C -S` crea en el primario un slot de replicación, que garantiza que no borre WAL que la réplica aún no ha recibido: ```bash sudo -u postgres pg_basebackup -h 10.10.0.1 -U replicator -D /var/lib/postgresql/16/main -R -X stream -C -S replica_region_b -P ``` Introduce la contraseña de `replicator` cuando la pida. Arranca la réplica: ```bash sudo systemctl start postgresql@16-main ``` Comprueba en la región B que funciona como réplica: ```bash sudo -u postgres psql -c "SELECT pg_is_in_recovery();" sudo -u postgres psql -c "SELECT status, sender_host FROM pg_stat_wal_receiver;" ``` ```output pg_is_in_recovery ------------------- t status | sender_host -----------+------------- streaming | 10.10.0.1 ``` Y en la región A, mide el retraso de la réplica: ```bash sudo -u postgres psql -c "SELECT client_addr, state, replay_lag FROM pg_stat_replication;" ``` ```output client_addr | state | replay_lag -------------+-----------+----------------- 10.10.0.2 | streaming | 00:00:00.041382 ``` `replay_lag` es, en la práctica, tu RPO en este momento: los datos que perderías si la región A desapareciera ahora. ### Replicación síncrona o asíncrona Por defecto la replicación es asíncrona: el primario confirma cada transacción sin esperar a la réplica. Con `synchronous_standby_names` puedes hacer que espere, lo que lleva el RPO a cero, pero cada commit tarda al menos un viaje de ida y vuelta entre regiones (el tiempo que midió el `ping` del paso 1), y si la réplica cae, las escrituras del primario se bloquean. Entre regiones lejanas, lo habitual es mantener la replicación asíncrona y vigilar el retraso. ## Paso 4: Replicar el resto del estado La base de datos no es lo único que necesita la región B para servir la aplicación: - **Código y configuración**: despliega la misma versión en ambas regiones desde tu repositorio o pipeline, nunca a mano en una sola. - **Archivos subidos por usuarios**: guárdalos en un almacenamiento de objetos compatible con S3 accesible desde ambas regiones, o sincronízalos periódicamente con `rsync` sobre el túnel. - **Secretos**: las mismas credenciales y claves deben existir en la región B antes de necesitarlas. La aplicación de la región B puede estar arrancada apuntando a su PostgreSQL local, que es de solo lectura, siempre que no intente escribir. Si no, déjala instalada y detenida. ## Paso 5: Preparar la conmutación por DNS Los usuarios llegan a tu servicio por un nombre, así que el failover consiste en que ese nombre apunte a la región B. Configura el registro A de `your_domain` apuntando a `region_a_ip` con un TTL bajo, de 60 segundos. Comprueba el TTL publicado: ```bash dig +noall +answer your_domain A ``` ```output your_domain. 60 IN A region_a_ip ``` Hay dos formas de cambiar el registro: - **Manual o por API de tu proveedor DNS**, como parte del procedimiento del paso 6. Es lo recomendado cuando solo tienes dos regiones. - **DNS con comprobaciones de salud** (servicios de failover o balanceo global de proveedores como Cloudflare o Amazon Route 53), que retiran automáticamente la IP de una región caída. Úsalo para el tráfico, pero no para promocionar la base de datos: lee la siguiente sección. Algunos clientes y resolutores ignoran TTL bajos y siguen usando la IP antigua durante un tiempo, así que cuenta con unos minutos de cola tras el cambio. ## Evitar el split-brain El mayor riesgo de una arquitectura en dos regiones no es que el failover falle, sino que ocurra cuando no debe. Si el enlace entre regiones se corta, la región B deja de ver a la A, aunque la A siga sirviendo a los usuarios perfectamente. Si en ese momento la B se promociona sola, tendrás dos primarios aceptando escrituras distintas (*split-brain*), y reconciliar esos datos es manual y doloroso. Con solo dos nodos no hay forma fiable de distinguir "la otra región ha caído" de "no puedo verla". Por eso: - **Con dos regiones, la promoción debe ser una decisión humana**, apoyada en comprobaciones desde un tercer punto (tu monitorización externa, pruebas desde varias ubicaciones). - **Para failover automático necesitas un tercer voto**: un clúster de consenso (por ejemplo, etcd con Patroni) con miembros en tres ubicaciones, de modo que solo el lado con mayoría pueda tener el primario. - **Antes de promocionar, aísla el primario antiguo** (*fencing*): detén PostgreSQL o bloquea el tráfico en la región A si todavía es accesible, para que no pueda seguir aceptando escrituras. ## Paso 6: Ejecutar un failover Este es el procedimiento para mover el servicio a la región B. Ensáyalo en una ventana de mantenimiento antes de necesitarlo. 1. **Confirma la caída** desde fuera: el servicio no responde desde varias ubicaciones y la región A no es accesible, o has decidido moverla de forma planificada. 2. **Aísla la región A.** Si puedes acceder a ella, detén la aplicación y PostgreSQL: ```bash sudo systemctl stop postgresql@16-main ``` 3. **Comprueba en la región B** hasta dónde ha llegado la réplica, para conocer los datos perdidos: ```bash sudo -u postgres psql -c "SELECT pg_last_xact_replay_timestamp();" ``` 4. **Promociona la réplica** de la región B a primario: ```bash sudo -u postgres psql -c "SELECT pg_promote();" ``` Verifica que ya acepta escrituras; `pg_is_in_recovery()` debe devolver `f`: ```bash sudo -u postgres psql -c "SELECT pg_is_in_recovery();" ``` 5. **Arranca o reconfigura la aplicación** de la región B para escribir en su base de datos local, y comprueba que responde: ```bash curl -I -H "Host: your_domain" http://region_b_ip/ ``` 6. **Cambia el registro A** de `your_domain` a `region_b_ip` en tu proveedor DNS y verifica la propagación: ```bash dig +short your_domain A @1.1.1.1 ``` Mide el tiempo desde el paso 1 hasta que `dig` devuelve la IP nueva: ese es tu RTO real. ## Paso 7: Recuperar la región A como réplica Cuando la región A vuelva, **no la arranques como primario**: sus datos han divergido de la región B. Reconstrúyela como réplica de la nueva primaria repitiendo los pasos 2 y 3 con los papeles intercambiados: en la región B, añade a `pg_hba.conf` la entrada de replicación para `10.10.0.1` y abre el puerto 5432 en `wg0` para esa IP; en la región A, vacía el directorio de datos y ejecuta `pg_basebackup` contra `10.10.0.2` con un slot nuevo, por ejemplo `replica_region_a`. El slot `replica_region_b` existía solo en el primario antiguo, así que desaparece al vaciar su directorio de datos. Comprueba en la región B que la nueva réplica está conectada con `SELECT client_addr, state, replay_lag FROM pg_stat_replication;`. Para bases de datos grandes, `pg_rewind` puede resincronizar la región A sin copiarla entera, pero requiere haber activado `wal_log_hints = on` o las sumas de verificación de datos de antemano. Si quieres volver a servir desde la región A, haz más adelante un failover planificado siguiendo el paso 6 en sentido inverso. ## Solución de problemas **`pg_basebackup: error: connection to server ... failed: no pg_hba.conf entry for replication connection`.** La entrada de `pg_hba.conf` no coincide con la IP de origen o con el usuario. Comprueba que la conexión llega por el túnel (`10.10.0.x`) y recarga PostgreSQL tras editar el archivo. **`replay_lag` crece sin parar.** El enlace entre regiones no da abasto o la réplica tiene disco lento. Revisa el ancho de banda del túnel y el uso de disco de la réplica con `iostat -x 5` (paquete `sysstat`). **El disco del primario se llena de WAL.** Hay un slot de replicación sin réplica conectada. Lista los slots con `SELECT slot_name, active FROM pg_replication_slots;` y elimina los inactivos que ya no uses. **`wg show` no muestra ningún handshake.** El puerto UDP 51820 está bloqueado o las claves públicas están intercambiadas. Revisa `sudo ufw status` en ambos lados y que cada `[Peer]` lleve la clave pública del otro servidor. ## Conclusión Tienes dos regiones conectadas por un túnel cifrado, una réplica de PostgreSQL que va unos milisegundos por detrás del primario y un procedimiento de failover con un RPO y un RTO medidos. La clave de esta arquitectura no está en la tecnología sino en ensayarla: programa un simulacro de failover cada trimestre y actualiza el procedimiento con lo que aprendas. Como siguientes pasos, añade monitorización externa del retraso de replicación, automatiza el cambio de DNS con la API de tu proveedor y, cuando necesites failover automático, evalúa Patroni con un clúster etcd repartido en tres ubicaciones.