Proyecto Cluster PostgreSQL, PatronI, Etcd y Haproxy
Proyecto e implementacion de un cluster de PostgreSQL con PatronI, Etcd y Haproxy
Cluster de Base de Datos Postgres con Patroni
Descripción
Este proyecto comenzo como una prueba de concepto (PoC) que demuestra la implementación de una base de datos PostgreSQL con alta disponibilidad utilizando Patroni, etcd y Haproxy. El objetivo era simular un entorno PostgreSQL que luego se implemento en produccion, asegurando una alta disponibilidad.
Tecnologías Utilizadas (PoC)
PostgreSQL: Sistema de gestión de bases de datos relacional.
Docker: Contenedores para empaquetar y ejecutar la aplicación.
Docker Compose: Herramienta para definir y ejecutar aplicaciones multi-contenedor.
Patroni: Herramienta para gestionar la replicación y alta disponibilidad de PostgreSQL.
etcd: Almacén de clave-valor distribuido utilizado para la coordinación en Patroni.
Diagrama del Cluster Postgres
graph TD
subgraph Docker Compose
subgraph Networks
postgres-cluster[postgres-cluster Network]
end
subgraph Services
etcd[etcd]
patroni-master[patroni-master]
patroni-replica-1[patroni-replica-1]
patroni-replica-2[patroni-replica-2]
end
subgraph Volumes
patroni-data-master[patroni-data-master]
patroni-data-replica-1[patroni-data-replica-1]
patroni-data-replica-2[patroni-data-replica-2]
end
etcd -->|Networks| postgres-cluster
patroni-master -->|Networks| postgres-cluster
patroni-replica-1 -->|Networks| postgres-cluster
patroni-replica-2 -->|Networks| postgres-cluster
patroni-master -->|depends_on| etcd
patroni-replica-1 -->|depends_on| etcd
patroni-replica-1 -->|depends_on| patroni-master
patroni-replica-2 -->|depends_on| etcd
patroni-replica-2 -->|depends_on| patroni-master
patroni-master -->|Volumes| patroni-data-master
patroni-replica-1 -->|Volumes| patroni-data-replica-1
patroni-replica-2 -->|Volumes| patroni-data-replica-2
end
Instalación
Crear imagen custom de Postgres
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FROM postgres:17.4
# Instalar dependencias
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
# Instalar Patroni
RUN mv /usr/lib/python3.11/EXTERNALLY-MANAGED /usr/lib/python3.11/EXTERNALLY-MANAGED.old && pip3 install psycopg2-binary patroni[etcd]
# Copiar el archivo de configuración de Patroni
COPY patroni.yml /etc/patroni.yml
RUN chown postgres:postgres /etc/patroni.yml
# Copiar el script de inicio
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
USER postgres
# Establecer el script de inicio como comando de inicio
CMD ["/entrypoint.sh"]
Crear entrypoint
1
2
3
4
5
6
7
8
9
#!/bin/bash
if [ -d "/var/lib/postgresql/data" ]; then
chmod 750 /var/lib/postgresql/data
chown -R postgres:postgres /var/lib/postgresql/data
fi
# Iniciar Patroni
exec patroni /etc/patroni.yml
Archivo de configuración Patroni
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
scope: postgres-cluster
name: patroni-master
restapi:
listen: 0.0.0.0:8008
connect_address: patroni-master:8008
etcd:
hosts: etcd:2379
protocol: http
api_version: v3
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
wal_level: replica
hot_standby: "on"
wal_keep_size: 128MB
max_wal_senders: 10
max_replication_slots: 10
checkpoint_timeout: 15
initdb:
- encoding: UTF8
- locale: en_US.UTF-8
pg_hba:
- host replication replicator 0.0.0.0/0 md5
- host all all 0.0.0.0/0 md5
postgresql:
listen: 0.0.0.0:5432
connect_address: haproxy:5000
data_dir: /var/lib/postgresql/data
bin_dir: /usr/lib/postgresql/17/bin
pgpass: /tmp/pgpass
authentication:
replication:
username: replicator
password: replicatorpass
superuser:
username: admin
password: secret
Levantar el cluster con docker compose
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
networks:
postgres-cluster:
services:
etcd:
image: quay.io/coreos/etcd:v3.4.14
container_name: etcd
environment:
ETCD_LISTEN_PEER_URLS: http://0.0.0.0:2380
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://etcd:2379
ETCD_INITIAL_CLUSTER: etcd=http://etcd:2380
ETCD_INITIAL_CLUSTER_STATE: new
ETCD_INITIAL_CLUSTER_TOKEN: poc
ETCD_ENABLE_V2: "true"
networks:
- postgres-cluster
command: etcd --name etcd --initial-advertise-peer-urls http://etcd:2380
patroni-master:
build: .
container_name: patroni-master
environment:
PATRONI_NAME: patroni-master
PATRONI_ETCD3_HOSTS: "'etcd:2379'"
volumes:
- patroni-data-master:/var/lib/postgresql/data
ports:
- "5432:5432"
- "8008:8008"
networks:
- postgres-cluster
depends_on:
- etcd
patroni-replica-1:
build: .
container_name: patroni-replica-1
environment:
PATRONI_NAME: patroni-replica-1
PATRONI_ETCD3_HOSTS: "'etcd:2379'"
volumes:
- patroni-data-replica-1:/var/lib/postgresql/data
ports:
- "5433:5432"
- "8009:8008"
networks:
- postgres-cluster
depends_on:
- etcd
- patroni-master
patroni-replica-2:
build: .
container_name: patroni-replica-2
environment:
PATRONI_NAME: patroni-replica-2
PATRONI_ETCD3_HOSTS: "'etcd:2379'"
volumes:
- patroni-data-replica-2:/var/lib/postgresql/data
ports:
- "5434:5432"
- "8010:8008"
networks:
- postgres-cluster
depends_on:
- etcd
- patroni-master
haproxy:
image: haproxy:2.4
container_name: haproxy
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
ports:
- "5000:5000"
networks:
- postgres-cluster
depends_on:
- patroni-master
- patroni-replica-1
- patroni-replica-2
volumes:
patroni-data-master:
patroni-data-replica-1:
patroni-data-replica-2:
Uso
conectate al nodo master
1
docker exec -it patroni-master psql -U admin -d postgres
Crea una tabla e inserta datos
1
2
CREATE TABLE test_table (id SERIAL PRIMARY KEY, name TEXT);
INSERT INTO test_table (name) VALUES ('Test 1'), ('Test 2');
Verifica en las réplicas
1
docker exec -it patroni-replica-1 psql -U admin -d postgres -c "SELECT * FROM test_table;"
1
docker exec -it patroni-replica-2 psql -U admin -d postgres -c "SELECT * FROM test_table;"
Cluster en Producción
flowchart TB
%% Nivel 1: DDNS
postgres-cluster["DDNS"]
%% Nivel 2: HAProxy en hosts
subgraph "HAProxy Layer"
haproxy1["HAProxy 1"]
haproxy2["HAProxy 2"]
haproxy3["HAProxy 3"]
end
postgres-cluster --> haproxy1
postgres-cluster --> haproxy2
postgres-cluster --> haproxy3
%% Nivel 3: ETCD
subgraph "ETCD Cluster"
etcd1[("etcd 1")]
etcd2[("etcd 2")]
etcd3[("etcd 3")]
end
etcd1 --> etcd2 --> etcd3 --> etcd1
etcd1 ~~~ etcd2 ~~~ etcd3
etcd2 --> patroni-master
patroni-master --> etcd2
%% Nivel 4: Patroni nodes
subgraph "Patroni Cluster"
patroni-master[("patroni-1")]
patroni-2[("patroni-2")]
patroni-3[("patroni-3")]
end
patroni-2 --> patroni-master
patroni-3 --> patroni-master
patroni-master --> patroni-2
patroni-master --> patroni-3
%% HAProxy conecta a Patroni master
haproxy1 --> patroni-master
haproxy2 --> patroni-master
haproxy3 --> patroni-master
Cambios para que se implementarion en produccion
Para implementar este modelo de base de datos se tuvieron que realizar diversos ajustes para proporcionar redundancia y alta disponibilidad:
Implementaciones
X3 Haproxy: Adicion dos servicios de Haproxy mas, conectados al ddns para poder garantizar el acceso cluster con hasta dos servicios en falla. Ademas el Ddns balancea la carga entre los tres utilizando Round Robin. (Los servicios de Haproxy son individuales, no hay cluster)
Cluster Etcd: Aplicación de un Cluster Etcd, para respaldar la integridad del cluster Postgres. Ya que una falla en el servicio del almacén ocasiona un desarme del cluster
Instalacion on premise: Los servicios fueron instalados en servidores distribuidos de distintos proveedores