Manual de Instalación de PostgreSQL en Alta Disponibilidad con Patroni y etcd

1. Introducción

Este manual está diseñado para guiarte a través del proceso de instalación y configuración de una base de datos PostgreSQL en alta disponibilidad utilizando Patroni y etcd como servicio de coordinación. Patroni facilita la gestión del clúster de bases de datos PostgreSQL y asegura la conmutación por error automática, mientras que etcd proporciona la sincronización de la configuración entre los nodos del clúster.

Este tipo de configuración es ideal para entornos de producción donde se necesita garantizar alta disponibilidad y resistencia ante fallos de nodo.

2. Requisitos Previos

Antes de comenzar con la instalación, asegúrate de cumplir con los siguientes requisitos previos:

  • Sistema Operativo: GNU/Linux (En este caso emplearemos Debian).
  • Privilegios de root: Acceso a una cuenta con permisos de superusuario para realizar instalaciones y configuraciones del sistema.
  • Red: Tres servidores (o más) en la misma red con direcciones IP estáticas.
  • Conectividad SSH: Asegúrate de que todos los nodos puedan comunicarse entre sí mediante SSH.
  • Dependencias: Necesitarás herramientas como curl, wget, y otros paquetes necesarios para descargar e instalar componentes.

2.1 Elección de la Cantidad de Nodos en Alta Disponibilidad

Cuando se configura un clúster de PostgreSQL en alta disponibilidad con Patroni y etcd, una de las decisiones clave es el número de nodos que formarán parte del clúster. La cantidad de nodos tiene un impacto directo en la tolerancia a fallos, la capacidad de realizar conmutaciones por error y la disponibilidad general del sistema. A continuación, explicamos cómo calcular el número adecuado de nodos y por qué se recomienda utilizar un número impar de nodos en algunos casos.

¿Cuántos Nodos Necesito?

El número mínimo recomendado para configurar un clúster de alta disponibilidad es de tres nodos. Este número de nodos asegura que el sistema sea lo suficientemente robusto como para tolerar fallos de al menos un nodo sin que se pierda la disponibilidad o el consenso en el clúster.

Para calcular el número de nodos necesarios en un clúster con etcd, se sigue una fórmula relacionada con el quorum. El quorum es la cantidad mínima de nodos que deben estar disponibles para que el sistema tome decisiones de manera confiable. Esta fórmula es la siguiente:

quorum=nodos2+1quorum = \frac{nodos}{2} + 1

Por ejemplo:

  • Con 3 nodos: el quorum es de 2 nodos, lo que permite que el sistema siga funcionando si uno de los nodos falla.
  • Con 5 nodos: el quorum es de 3 nodos, permitiendo que el sistema siga operativo incluso si dos nodos fallan.
  • Con 7 nodos: el quorum es de 4 nodos, lo que permite una mayor tolerancia a fallos.

¿Por qué es importante tener un número impar de nodos?

El uso de un número impar de nodos es fundamental cuando se utilizan tecnologías de consenso, como etcd o Raft, que son responsables de mantener la consistencia de los datos en el clúster. Esto se debe a que el consenso depende de un proceso de votación entre los nodos para tomar decisiones. Si hay un número par de nodos, existe el riesgo de que no se pueda alcanzar un consenso en caso de un fallo, ya que no se podría determinar qué nodo tiene la mayoría.

Por ejemplo:

  • Si el clúster tiene 2 nodos y uno de ellos falla, el otro nodo quedaría sin quorum, ya que no puede decidir por sí mismo si el nodo caído debe ser promovido o no.
  • Con 3 nodos, si uno falla, los otros dos pueden seguir trabajando y decidir qué hacer con el nodo caído.

Por lo tanto, siempre que se utilicen servicios de coordinación y consenso (como etcd o Consul), es recomendable tener un número impar de nodos en el clúster para garantizar que siempre se pueda alcanzar el quorum y evitar problemas de «split-brain» (división del clúster).

¿Cuándo se podrían usar nodos pares?

En algunos casos, los nodos pares pueden ser adecuados, pero solo para ciertos tipos de servicios que no dependen de un proceso de consenso, como en la distribución de carga o en clústeres de Kubernetes donde los nodos de worker no participan en decisiones críticas de consenso. Algunos ejemplos de estos casos son:

  • Balanceadores de carga: En servicios como HAProxy o NGINX, los nodos de balanceo pueden ser pares, ya que su objetivo es distribuir el tráfico sin necesidad de consenso.
  • Servidores de aplicaciones: Si el servicio solo necesita alta disponibilidad pero no se requiere un proceso de consenso entre los nodos, los nodos de aplicaciones pueden ser pares.

Resumen

  • Para clústeres de alta disponibilidad que utilizan Patroni y etcd, 3 nodos es el mínimo recomendado, y se debe utilizar un número impar para garantizar el consenso y la disponibilidad en caso de fallos.
  • 5 nodos es ideal para entornos más grandes o donde se espera una mayor carga, ya que proporciona más tolerancia a fallos.
  • El número de nodos puede ser par en servicios que no requieren consenso, como balanceadores de carga o servidores de aplicaciones.

3. Instalación de la Base de Datos en Alta Disponibilidad

Este paso te guiará a través de la instalación de PostgreSQL y Patroni en tres nodos (servidores) que formarán parte del clúster de alta disponibilidad.

3.0 Instalación de PostgreSQL, Patroni y etcd

Para comenzar, actualiza los paquetes del sistema e instala las herramientas necesarias:

sudo apt update && sudo apt install -y postgresql postgresql-contrib patroni etcd-server etcd-client curl wget pgbackrest jq

Descargaremos e instalaremos las herramientas de gestión de certificados, necesarias para asegurar las comunicaciones entre los nodos.

sudo wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -O /bin/cfssl 
sudo wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -O /bin/cfssljson 
sudo wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -O /bin/cfssl-certinfo
sudo chmod +x /bin/cfssl*

A continuación, modifica el archivo /etc/hosts de cada nodo para que se reconozcan los demás servidores del clúster.

sudo bash -c 'cat <<EOF >> /etc/hosts
192.168.1.214 srvlropsql01
192.168.1.215 srvlropsql02
192.168.1.216 srvlropsql03
EOF'

3.1 Crear un grupo común para compartir acceso a certificados y claves

Para permitir que los servicios como postgres y etcd compartan acceso a los archivos de certificados y claves necesarias, puedes crear un grupo común y añadir los usuarios correspondientes a ese grupo.

Crear el grupo común

Puedes crear un grupo llamado, por ejemplo, db-etcd, que tendrá acceso a los certificados y claves necesarias para ambos servicios.

sudo groupadd db-etcd

Añadir los usuarios al grupo

Añade los usuarios postgres y etcd al grupo db-etcd. Esto permitirá que ambos servicios tengan acceso a los archivos de claves de forma compartida.

sudo usermod -aG db-etcd postgres
sudo usermod -aG db-etcd etcd

3.2 Configuración de etcd-Server

Generación de Certificados (Manual)

Para asegurar las comunicaciones entre los nodos del clúster, es necesario generar certificados SSL. Los certificados se generan mediante cfssl (CloudFlare’s PKI toolkit).

  • Crea los directorios necesarios y entra en el directorio donde almacenarás los certificados.

mkdir /etc/etcd  &&  mkdir ~/etcd-ca && cd ~/etcd-ca
  • Crea la Autoridad Certificadora (CA) utilizando cfssl.
echo '{"CN":"CA","key":{"algo":"rsa","size":2048}}' | cfssl gencert -initca - | cfssljson -bare ca -
  • Crea la configuración para la firma de certificados.
echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","server auth","client auth"]}}}' > ca-config.json
  • Repite el proceso para cada nodo en el clúster, usando la siguiente secuencia de comandos, reemplazando los valores de NAME y ADDRESS para cada servidor:
export NAME=srvlropsql01
export ADDRESS=192.168.1.214,$NAME
echo '{"CN":"'$NAME'","hosts":[""],"key":{"algo":"rsa","size":2048}}' | cfssl gencert -config=ca-config.json -ca=ca.pem -ca-key=ca-key.pem -hostname="$ADDRESS" - | cfssljson -bare $NAME

Luego, transfiere los certificados al directorio adecuado y asegúrate de establecer los permisos correctos:

scp ca.pem $NAME:/etc/etcd/etcd-ca.crt
scp $NAME.pem $NAME:/etc/etcd/server.crt
scp $NAME-key.pem $NAME:/etc/etcd/server.key
scp ca* $NAME:/etc/etcd/
ssh $NAME chmod 600 /etc/etcd/server.key
ssh $NAME  sudo chown etcd:db-etcd /etc/etcd/*

Repite este proceso para cada nodo en el clúster, cambiando los valores de NAME y ADDRESS según sea necesario.

3.3 Configuración de etcd (Automatizado)

Si prefieres automatizar el proceso de creación y distribución de los certificados, puedes usar el siguiente script. Este script crea los certificados y los distribuye a todos los nodos de forma automática.

#!/bin/bash

# Crear la CA
echo "Generando la CA"
echo '{"CN":"CA","key":{"algo":"rsa","size":2048}}' | cfssl gencert -initca - | cfssljson -bare ca -
echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","server auth","client auth"]}}}' > ca-config.json

# Lista de servidores y sus direcciones IP
declare -A SERVERS
SERVERS=(
  ["srvlropsql01"]="192.168.1.214"
  ["srvlropsql02"]="192.168.1.215"
  ["srvlropsql03"]="192.168.1.216"
)

# Ruta del archivo CA
CA_PATH="/etc/etcd/"

# Certificados generados
CA_PEM="ca.pem"
CA_KEY="ca-key.pem"

# Iterar sobre cada servidor
for NAME in "${!SERVERS[@]}"; do
  ADDRESS="${SERVERS[$NAME]},$NAME"
   
  # Generar certificado
  echo "Generando certificado de $NAME"
  echo '{"CN":"'$NAME'","hosts":[""],"key":{"algo":"rsa","size":2048}}' | cfssl gencert -config=ca-config.json -ca=$CA_PEM -ca-key=$CA_KEY -hostname="$ADDRESS" - | cfssljson -bare $NAME

  if [ "$NAME" == "srvlropsql01" ]; then
    # Copiar archivos al servidor local
    sudo cp $CA_PEM $CA_PATH/etcd-ca.crt
    sudo cp $NAME.pem $CA_PATH/server.crt
    sudo cp $NAME-key.pem $CA_PATH/server.key
    sudo cp ca* $CA_PATH/
    sudo chmod 600 $CA_PATH/server.key
    sudo chown etcd:db-etcd $CA_PATH/*
  else
    # Copiar archivos a servidores remotos
    scp $CA_PEM $NAME:$CA_PATH/etcd-ca.crt
    scp $NAME.pem $NAME:$CA_PATH/server.crt
    scp $NAME-key.pem $NAME:$CA_PATH/server.key
    scp ca* $NAME:$CA_PATH/
    ssh $NAME "sudo chmod 600 $CA_PATH/server.key"
    ssh $NAME "sudo chown etcd:db-etcd $CA_PATH/*"
  fi
done

Este script automatiza todo el proceso de creación y distribución de los certificados, asegurando que cada nodo tenga los certificados adecuados y que los permisos estén correctamente configurados.

3.4 Verificación de los Certificados

Una vez que hayas configurado los certificados y los permisos, verifica la validez de los certificados generados en cada nodo.

openssl x509 -in /etc/etcd/server.crt -text -noout | grep -E "CN=|DNS:|IP Address:"

Este comando muestra los detalles del certificado para asegurarte de que todo esté configurado correctamente.

3.5 Configuración de Etcd

Configuración de los archivos de Etcd en cada nodo


En cada uno de los nodos que forman parte del clúster, debes configurar el archivo /etc/default/etcd con los siguientes parámetros:

Para el nodo srvlropsql01:

ETCD_NAME="srvlropsql01"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.1.214:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.1.214:2379"
ETCD_INITIAL_CLUSTER="srvlropsql01=https://srvlropsql01:2380,srvlropsql02=https://srvlropsql02:2380,srvlropsql03=https://srvlropsql03:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.1.214:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.1.214:2379"
ETCD_TRUSTED_CA_FILE="/etc/etcd/etcd-ca.crt"
ETCD_CERT_FILE="/etc/etcd/server.crt"
ETCD_KEY_FILE="/etc/etcd/server.key"
ETCD_PEER_CLIENT_CERT_AUTH=true
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/etcd-ca.crt"
ETCD_PEER_KEY_FILE="/etc/etcd/server.key"
ETCD_PEER_CERT_FILE="/etc/etcd/server.crt"
ETCD_LOG_LEVEL="info"
ETCD_ENABLE_V2="true"

Para el nodo srvlropsql02:

ETCD_NAME="srvlropsql02"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.1.215:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.1.215:2379"
ETCD_INITIAL_CLUSTER="srvlropsql01=https://srvlropsql01:2380,srvlropsql02=https://srvlropsql02:2380,srvlropsql03=https://srvlropsql03:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.1.215:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.1.215:2379"
ETCD_TRUSTED_CA_FILE="/etc/etcd/etcd-ca.crt"
ETCD_CERT_FILE="/etc/etcd/server.crt"
ETCD_KEY_FILE="/etc/etcd/server.key"
ETCD_PEER_CLIENT_CERT_AUTH=true
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/etcd-ca.crt"
ETCD_PEER_KEY_FILE="/etc/etcd/server.key"
ETCD_PEER_CERT_FILE="/etc/etcd/server.crt"
ETCD_LOG_LEVEL="info"
ETCD_ENABLE_V2="true"

Para el nodo srvlropsql03:

ETCD_NAME="srvlropsql03"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.1.216:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.1.216:2379"
ETCD_INITIAL_CLUSTER="srvlropsql01=https://srvlropsql01:2380,srvlropsql02=https://srvlropsql02:2380,srvlropsql03=https://srvlropsql03:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.1.216:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.1.216:2379"
ETCD_TRUSTED_CA_FILE="/etc/etcd/etcd-ca.crt"
ETCD_CERT_FILE="/etc/etcd/server.crt"
ETCD_KEY_FILE="/etc/etcd/server.key"
ETCD_PEER_CLIENT_CERT_AUTH=true
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/etcd-ca.crt"
ETCD_PEER_KEY_FILE="/etc/etcd/server.key"
ETCD_PEER_CERT_FILE="/etc/etcd/server.crt"
ETCD_LOG_LEVEL="info"
ETCD_ENABLE_V2="true"

Reiniciar y habilitar Etcd en cada nodo

En cada nodo, ejecuta los siguientes comandos para reiniciar y habilitar el servicio Etcd para que inicie automáticamente al arranque:

sudo systemctl restart etcd
sudo systemctl enable etcd

Verificación del estado de Etcd

Una vez que Etcd esté configurado y en funcionamiento, puedes verificar la salud de los nodos con el siguiente comando:

sudo etcdctl --endpoints=https://srvlropsql01:2379,https://srvlropsql02:2379,https://srvlropsql03:2379 --cacert=/etc/etcd/ca.pem --cert=/etc/etcd/server.crt --key=/etc/etcd/server.key endpoint health

Output

https://srvlropsql01:2379 is healthy: successfully committed proposal: took = 27.537946ms
https://srvlropsql03:2379 is healthy: successfully committed proposal: took = 38.517279ms
https://srvlropsql02:2379 is healthy: successfully committed proposal: took = 35.582262ms

Comprobación del estado de los endpoints de Etcd

Puedes verificar el estado de los endpoints de Etcd utilizando el siguiente comando:

sudo etcdctl --endpoints=https://srvlropsql01:2379,https://srvlropsql02:2379,https://srvlropsql03:2379 --cacert=/etc/etcd/ca.pem --cert=/etc/etcd/server.crt --key=/etc/etcd/server.key endpoint status --write-out=table

Output

+---------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|         ENDPOINT          |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+---------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://srvlropsql01:2379 | 1d73939a04e3e9e0 |  3.4.23 |   20 kB |      true |      false |         6 |          9 |                  9 |        |
| https://srvlropsql02:2379 | bce2231f6867959d |  3.4.23 |   25 kB |     false |      false |         6 |          9 |                  9 |        |
| https://srvlropsql03:2379 | 2ce6d020bc93183f |  3.4.23 |   20 kB |     false |      false |         6 |          9 |                  9 |        |
+---------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

Revisión de los logs de Etcd

Para verificar si hay errores en el servicio Etcd, puedes consultar los logs ejecutando:

sudo journalctl -u etcd --no-pager | tail -n 20

En caso de tener problemas con el certificado cambiar la configuración a la siguiente y realizar pruebas

ETCD_PEER_CLIENT_CERT_AUTH=false
CLIENT-CERT-AUTH=false
ETCD_AUTO_TLS=false

Configuración de Patroni

Detener el servicio PostgreSQL

Antes de iniciar la configuración de Patroni, es necesario detener el servicio de PostgreSQL para evitar conflictos.

sudo systemctl stop postgresql.service

Acceso al usuario PostgreSQL

Se cambia al usuario postgres para ejecutar comandos dentro de PostgreSQL.

sudo su - postgres
psql

Configuración de roles y usuarios en PostgreSQL

Se crean y configuran los roles necesarios para la replicación y la copia de seguridad con pgBackRest.

Creación del usuario replicador

Se crea un rol con permisos de replicación.

CREATE ROLE replicator WITH REPLICATION LOGIN ENCRYPTED PASSWORD 'Temporal01.';
ALTER ROLE replicator WITH REPLICATION;

Configuración del usuario administrador

Se cambia la contraseña del usuario postgres para garantizar la autenticación segura.

 

ALTER USER postgres WITH PASSWORD 'Temporal01.';

Creación del usuario para pgBackRest

Se crea un usuario con permisos de superusuario y se otorgan privilegios de conexión y acceso a secuencias y tablas. 

CREATE ROLE pgbackrest WITH LOGIN PASSWORD 'Temporal01.' SUPERUSER;
GRANT CONNECT ON DATABASE postgres TO pgbackrest;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO pgbackrest;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO pgbackrest;
GRANT CONNECT ON DATABASE postgres TO pgbackrest;

Creación y ajuste de permisos

touch /var/lib/postgresql/.pgpass
chmod 600 /var/lib/postgresql/.pgpass
chown postgres:postgres /var/lib/postgresql/.pgpass

Edición del archivo .pgpass

Se agregan las credenciales para la replicación en cada nodo del clúster.

vi  /var/lib/postgresql/.pgpass
Contenido del archivo:
192.168.1.214:5432:*:replicator:Temporal01.
192.168.1.215:5432:*:replicator:Temporal01.
192.168.1.216:5432:*:replicator:Temporal01.

192.168.1.214:5432:*:postgres:Temporal01.
192.168.1.215:5432:*:postgres:Temporal01.
192.168.1.216:5432:*:postgres:Temporal01.

 Configuración de la variable de entorno PGPASSFILE

Es necesario exportar la variable de entorno que apunta al archivo .pgpass para que PostgreSQL la utilice de forma automática.

vi ~/.bashrc

Agregar la siguiente línea:

export PGPASSFILE=/var/lib/postgresql/.pgpass

Aplicar los cambios:

source ~/.bashrc

Eliminación de datos previos y reinicio del servicio

Antes de iniciar Patroni, es recomendable eliminar los datos previos de PostgreSQL y detener el servicio nuevamente.

 sudo systemctl stop postgresql.service
sudo rm -rf /var/lib/postgresql/15/main

 Configuración de los archivos de Patroni en cada nodo

Cada nodo tiene su propio archivo de configuración config.yml, en el que se especifican los detalles de la configuración, como la dirección del restapi, la configuración de etcd para la coordinación del clúster, y los parámetros de PostgreSQL. Este es un ejemplo de configuración para tres nodos en un clúster de Patroni:

 Para el nodo srvlropsql01:


  scope: keycloak-cluster
namespace: /service/
name: srvlropsql01

restapi:
  listen: 192.168.1.214:8008
  connect_address: 192.168.1.214:8008
  authentication:
    username: admin
    password: admin_password

etcd:
  hosts: 192.168.1.214:2379,192.168.1.215:2379,192.168.1.216:2379
  protocol: https
  cacert: /etc/etcd/etcd-ca.crt
  cert: /etc/etcd/server.crt
  key: /etc/etcd/server.key

bootstrap:
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576
    postgresql:
      use_pg_rewind: true
      parameters:
        wal_level: replica
        hot_standby: "on"
        max_wal_senders: 10
        max_replication_slots: 10
        wal_keep_size: 256MB
  initdb:
    - encoding: UTF8
    - data-checksums
  users:
    replication:
      password: replicator_password
      options:
        - replication
    admin:
      password: admin_password
      options:
        - createrole
        - createdb
  post_init: 
    - createuser --superuser admin

postgresql:
  listen: 0.0.0.0:5432
  connect_address: 192.168.1.214:5432
  data_dir: /var/lib/postgresql/15/main
  bin_dir: /usr/lib/postgresql/15/bin
  pgpass: /tmp/pgpass
  authentication:
    replication:
      username: replicator
      password: replicator_password
    superuser:
      username: postgres
      password: postgres_password
  parameters:
    archive_mode: "on"
    archive-command: 'pgbackrest --stanza=postgres archive-push %f'
    wal_level: replica
    max_wal_senders: 10
    max_replication_slots: 10
    wal_keep_size: 256MB
  pg_hba:
    - local all postgres trust
    - hostssl replication replicator 127.0.0.1/32 trust
    - hostssl replication replicator 192.168.1.215/32 trust
    - hostssl replication replicator 192.168.1.216/32 trust
    - hostssl replication replicator 192.168.1.214/32 trust
    - host replication replicator 127.0.0.1/32 trust
    - host replication replicator 192.168.1.215/32 trust
    - host replication replicator 192.168.1.216/32 trust
    - host replication replicator 192.168.1.214/32 trust
    - hostssl all all 192.168.1.0/24 trust
    - host all all 192.168.1.0/24 trust
    - hostssl all pgbackrest 192.168.1.0/24 trust
    - host all pgbackrest 192.168.1.0/24 trust
    - hostssl all all 0.0.0.0/0 md5
    - host all all 0.0.0.0/0 md5
tags:
  nofailover: false
  noloadbalance: false
  clonefrom: false

 Para el nodo srvlropsql02:



scope: keycloak-cluster
namespace: /service/
name: srvlropsql02

restapi:
  listen: 192.168.1.215:8008
  connect_address: 192.168.1.215:8008
  authentication:
    username: admin
    password: admin_password

etcd:
  hosts: 192.168.1.214:2379,192.168.1.215:2379,192.168.1.216:2379
  protocol: https
  cacert: /etc/etcd/etcd-ca.crt
  cert: /etc/etcd/server.crt
  key: /etc/etcd/server.key

bootstrap:
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576
    postgresql:
      use_pg_rewind: true
      parameters:
        wal_level: replica
        hot_standby: "on"
        max_wal_senders: 10
        max_replication_slots: 10
        wal_keep_size: 256MB
  initdb:
    - encoding: UTF8
    - data-checksums
  users:
    replication:
      password: replicator_password
      options:
        - replication
    admin:
      password: admin_password
      options:
        - createrole
        - createdb
  post_init: 
    - createuser --superuser admin

postgresql:
  listen: 0.0.0.0:5432
  connect_address: 192.168.1.215:5432
  data_dir: /var/lib/postgresql/15/main
  bin_dir: /usr/lib/postgresql/15/bin
  pgpass: /tmp/pgpass
  authentication:
    replication:
      username: replicator
      password: replicator_password
    superuser:
      username: postgres
      password: postgres_password
  parameters:
    archive_mode: "on"
    archive-command: 'pgbackrest --stanza=postgres archive-push %f'
    wal_level: replica
    max_wal_senders: 10
    max_replication_slots: 10
    wal_keep_size: 256MB
  pg_hba:
    - local all postgres trust
    - hostssl replication replicator 127.0.0.1/32 trust
    - hostssl replication replicator 192.168.1.215/32 trust
    - hostssl replication replicator 192.168.1.216/32 trust
    - hostssl replication replicator 192.168.1.214/32 trust
    - host replication replicator 127.0.0.1/32 trust
    - host replication replicator 192.168.1.215/32 trust
    - host replication replicator 192.168.1.216/32 trust
    - host replication replicator 192.168.1.214/32 trust
    - hostssl all all 192.168.1.0/24 trust
    - host all all 192.168.1.0/24 trust
    - hostssl all pgbackrest 192.168.1.0/24 trust
    - host all pgbackrest 192.168.1.0/24 trust
    - hostssl all all 0.0.0.0/0 md5
    - host all all 0.0.0.0/0 md5
tags:
  nofailover: false
  noloadbalance: false
  clonefrom: false

 Para el nodo srvlropsql03:

scope: keycloak-cluster
namespace: /service/
name: srvlropsql03

restapi:
  listen: 192.168.1.216:8008
  connect_address: 192.168.1.216:8008
  authentication:
    username: admin
    password: admin_password

etcd:
  hosts: 192.168.1.214:2379,192.168.1.215:2379,192.168.1.216:2379
  protocol: https
  cacert: /etc/etcd/etcd-ca.crt
  cert: /etc/etcd/server.crt
  key: /etc/etcd/server.key

bootstrap:
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576
    postgresql:
      use_pg_rewind: true
      parameters:
        wal_level: replica
        hot_standby: "on"
        max_wal_senders: 10
        max_replication_slots: 10
        wal_keep_size: 256MB
  initdb:
    - encoding: UTF8
    - data-checksums
  users:
    replication:
      password: replicator_password
      options:
        - replication
    admin:
      password: admin_password
      options:
        - createrole
        - createdb
  post_init: 
    - createuser --superuser admin

postgresql:
  listen: 0.0.0.0:5432
  connect_address: 192.168.1.216:5432
  data_dir: /var/lib/postgresql/15/main
  bin_dir: /usr/lib/postgresql/15/bin
  pgpass: /tmp/pgpass
  authentication:
    replication:
      username: replicator
      password: replicator_password
    superuser:
      username: postgres
      password: postgres_password
  parameters:
    archive_mode: "on"
    archive-command: 'pgbackrest --stanza=postgres archive-push %f'
    wal_level: replica
    max_wal_senders: 10
    max_replication_slots: 10
    wal_keep_size: 256MB
  pg_hba:
    - local all postgres trust
    - hostssl replication replicator 127.0.0.1/32 trust
    - hostssl replication replicator 192.168.1.215/32 trust
    - hostssl replication replicator 192.168.1.216/32 trust
    - hostssl replication replicator 192.168.1.214/32 trust
    - host replication replicator 127.0.0.1/32 trust
    - host replication replicator 192.168.1.215/32 trust
    - host replication replicator 192.168.1.216/32 trust
    - host replication replicator 192.168.1.214/32 trust
    - hostssl all all 192.168.1.0/24 trust
    - host all all 192.168.1.0/24 trust
    - hostssl all pgbackrest 192.168.1.0/24 trust
    - host all pgbackrest 192.168.1.0/24 trust
    - hostssl all all 0.0.0.0/0 md5
    - host all all 0.0.0.0/0 md5
tags:
  nofailover: false
  noloadbalance: false
  clonefrom: false

Configuración de pgBackRest

Después de configurar Patroni, es necesario configurar pgBackRest, una herramienta utilizada para gestionar los respaldos de PostgreSQL. Este es el archivo de configuración pgbackrest.conf que se debe utilizar:

[global]
repo1-path=/var/lib/pgbackrest  # Directorio donde se guardarán los respaldos
log-level-console=info
log-level-file=detail
compress=y
start-fast=y
log-path=/var/log/pgbackrest  # Ruta para logs detallados

# Nodo 1 (Líder)
[nodo1]
pg1-path=/var/lib/postgresql/15/main
pg1-port=5432
pg1-host=localhost  # El nodo líder se encuentra en el localhost
stanza=postgres
archive-mode=on
archive-command='pgbackrest --stanza=postgres archive-push %p'

# Nodo 2 (Réplika)
[nodo2]
pg1-path=/var/lib/postgresql/15/main
pg1-port=5432
pg1-host=192.168.1.215  # Dirección IP del nodo 2
stanza=postgres
archive-mode=on
archive-command='pgbackrest --stanza=postgres archive-push %p'

# Nodo 3 (Réplika)
[nodo3]
pg1-path=/var/lib/postgresql/15/main
pg1-port=5432
pg1-host=192.168.1.216  # Dirección IP del nodo 3
stanza=postgres
archive-mode=on
archive-command='pgbackrest --stanza=postgres archive-push %p'
  • Se configura el directorio donde se almacenarán los respaldos (repo1-path).
  • Se habilitan los respaldos rápidos (start-fast=y) y la compresión (compress=y).
  • Cada nodo tiene su propia configuración de pg1-path y pg1-host, y los parámetros de archive-command se ajustan para realizar los respaldos con pgBackRest.

Iniciando los Servicios

Una vez que la configuración de Patroni y pgBackRest está lista, se deben iniciar los servicios correspondientes:

sudo systemctl start patroni.service
sudo systemctl enable patroni.service

Esto iniciará el servicio de Patroni en los tres nodos y lo habilitará para que se inicie automáticamente en cada reinicio.

Verificando el Estado de Patroni

Puedes verificar el estado de Patroni usando una herramienta como curl para realizar una solicitud HTTP a la API REST de Patroni en el nodo líder:

 Para el nodo srvlropsql01:

curl http://192.168.1.214:8008/patroni  | jq .

Output

{
  "state": "running",
  "postmaster_start_time": "2025-02-27 10:38:12.906725+00:00",
  "role": "master",
  "server_version": 150010,
  "xlog": {
    "location": 285213000
  },
  "timeline": 14,
  "replication": [
    {
      "usename": "replicator",
      "application_name": "srvlropsql02",
      "client_addr": "192.168.1.215",
      "state": "streaming",
      "sync_state": "async",
      "sync_priority": 0
    },
    {
      "usename": "replicator",
      "application_name": "srvlropsql03",
      "client_addr": "192.168.1.216",
      "state": "streaming",
      "sync_state": "async",
      "sync_priority": 0
    }
  ],
  "dcs_last_seen": 1740653075,
  "database_system_identifier": "7476020745627849320",
  "patroni": {
    "version": "3.0.2",
    "scope": "keycloak-cluster"
  }
}

 Para el nodo srvlropsql01:

curl http://192.168.1.215:8008/patroni | jq .

Output

{
  "state": "running",
  "postmaster_start_time": "2025-02-27 10:38:20.602283+00:00",
  "role": "replica",
  "server_version": 150010,
  "xlog": {
    "received_location": 285213000,
    "replayed_location": 285213000,
    "replayed_timestamp": null,
    "paused": false
  },
  "timeline": 14,
  "dcs_last_seen": 1740653115,
  "database_system_identifier": "7476020745627849320",
  "patroni": {
    "version": "3.0.2",
    "scope": "keycloak-cluster"
  }
}

 Para el nodo srvlropsql03:


curl http://192.168.1.216:8008/patroni | jq .

Output

{
  "state": "running",
  "postmaster_start_time": "2025-02-27 10:38:36.525932+00:00",
  "role": "replica",
  "server_version": 150010,
  "xlog": {
    "received_location": 285213000,
    "replayed_location": 285213000,
    "replayed_timestamp": null,
    "paused": false
  },
  "timeline": 14,
  "dcs_last_seen": 1740653125,
  "database_system_identifier": "7476020745627849320",
  "patroni": {
    "version": "3.0.2",
    "scope": "keycloak-cluster"
  }
}

Verificación del estado de la recuperación en PostgreSQL

  • Cambia al usuario postgres:

sudo su - postgres

Verifica si el servidor está en modo de recuperación utilizando el comando pg_is_in_recovery:

psql -U postgres -c "SELECT pg_is_in_recovery();"

Output

 pg_is_in_recovery 
-------------------
 f
(1 row)
  • f: El servidor está en modo operativo normal (no en recuperación).
  • t: El servidor está en modo de recuperación (probablemente en una réplica o recuperando de una falla).

Comprobar el estado de la replicación

  • Consulta el estado de la replicación en el servidor utilizando el siguiente comando:
psql -U postgres -c "SELECT * FROM pg_stat_replication;"

Output

pid  | usesysid |  usename   | application_name |  client_addr  | client_hostname | client_port |         backend_start         | backend_xmin |   state   |  sent_lsn  | write_lsn  | flush_lsn  | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state |          reply_time           
------+----------+------------+------------------+---------------+-----------------+-------------+-------------------------------+--------------+-----------+------------+------------+------------+------------+-----------+-----------+------------+---------------+------------+-------------------------------
1602 |    16384 | replicator | srvlropsql02     | 192.168.1.215 |                 |       38202 | 2025-02-27 10:38:20.756912+00 |              | streaming | 0/11000060 | 0/11000060 | 0/11000060 | 0/11000060 |           |           |            |             0 | async      | 2025-02-27 10:41:53.23572+00
1609 |    16384 | replicator | srvlropsql03     | 192.168.1.216 |                 |       53922 | 2025-02-27 10:38:46.672521+00 |              | streaming | 0/11000060 | 0/11000060 | 0/11000060 | 0/11000060 |           |           |            |             0 | async      | 2025-02-27 10:41:56.916286+00
(2 rows)

Esto muestra los detalles de los procesos de replicación, incluyendo los nombres de las aplicaciones (srvlropsql02, srvlropsql03), las direcciones IP de los clientes, los estados de replicación (streaming), y las posiciones de los registros de la réplica.

Comprobar los slots de replicación

Consulta los slots de replicación con el siguiente comando:

psql -U postgres -c "SELECT * FROM pg_replication_slots;"

Output

slot_name   | plugin | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn | wal_status | safe_wal_size | two_phase 
--------------+--------+-----------+--------+----------+-----------+--------+------------+------+--------------+-------------+---------------------+------------+---------------+-----------
srvlropsql02 |        | physical  |        |          | f         | t      |       1602 |      |              | 0/11000060  |                     | reserved   |               | f
srvlropsql03 |        | physical  |        |          | f         | t      |       1609 |      |              | 0/11000060  |                     | reserved   |               | f
(2 rows)

Los slots de replicación muestran el nombre del slot (srvlropsql02, srvlropsql03), el tipo de slot (físico), y si están activos. Los slots están reservados, lo que indica que la replicación está en marcha.

Ver los registros de Patroni

  • Para revisar los registros del servicio Patroni, puedes usar journalctl:
sudo journalctl -u patroni --no-pager | tail -n 20

Recomendaciones y Mejores Prácticas

  1. Monitoreo: Implementa herramientas como Prometheus y Grafana para supervisar el estado del clúster y configurar alertas ante fallos.

  2. Respaldos: Realiza copias de seguridad regulares con herramientas como pgBackRest y asegúrate de almacenarlas de forma segura.

  3. Escalabilidad: Añade nodos de lectura y ajusta la configuración para escalar horizontalmente según la carga.

  4. Seguridad: Usa TLS para cifrar la comunicación entre nodos y clientes, y aplica políticas de acceso estrictas en PostgreSQL.

  5. etcd: Mantén etcd replicado y monitoreado para asegurar la consistencia entre nodos.

  6. Failover: Realiza pruebas periódicas de failover para asegurar la disponibilidad en caso de fallos.

  7. Mantenimiento: Actualiza regularmente los componentes del sistema, verificando compatibilidad entre versiones.

  8. Redundancia: Asegura redes y hardware redundantes para evitar interrupciones.

  9. Optimización: Ajusta los parámetros de PostgreSQL según la carga y los recursos disponibles.

Conclusión

Implementar un sistema de PostgreSQL con Patroni, etcd y TLS ofrece una solución robusta para alta disponibilidad y escalabilidad. Patroni proporciona una gestión automatizada de failover y promoción de nodos, asegurando una disponibilidad continua de la base de datos. etcd garantiza una coordinación eficiente entre nodos, lo que permite mantener el consenso y la configuración distribuida. Además, TLS asegura las comunicaciones entre nodos, protegiendo los datos en tránsito con cifrado de alto nivel. Esta arquitectura asegura una base de datos altamente disponible, segura y fácil de escalar, ideal para entornos de producción críticos.

 

:wq!

 

 

 

Deja una respuesta

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