멀티 리전에서 Patroni H/A 운용하기 — Part 2: 동기 복제 멀티 DC 구성 실습
Part 1에서 정리한 패턴 A를 실제로 구현한다. 서울·도쿄·싱가포르 3개 DC에 PostgreSQL 17, Patroni 4.1.x, etcd 3.5.x를 배치하고 TLS 인증서 발급부터 동기 복제 설정, HAProxy 라우팅, 자동 페일오버 검증까지 단계별로 실습한다.
시리즈 구성 — 멀티 리전에서 Patroni H/A 운용하기
- Part 1 — 기초 개념과 아키텍처 설계 원칙
- Part 2 — 동기 복제 멀티 DC 구성 실습 (현재 편)
- Part 3 — 비동기 복제(Asynchronous) + Standby Cluster 구성 실습
- Part 4 — Split-Brain 방지 전략 (STONITH, Watchdog, Quorum)
- Part 5 — 장애 대응 Runbook 및 DR 훈련 시나리오
- Part 6 — 모니터링, 운영 자동화 및 Best Practices
목차
- 실습 환경 및 전제 조건
- Step 1 — TLS 인증서 발급
- Step 2 — etcd 클러스터 구성
- Step 3 — PostgreSQL 초기화 및 Patroni 설치
- Step 4 — patroni.yml 작성
- Step 5 — HAProxy 커넥션 라우팅
- Step 6 — 클러스터 검증 및 자동 페일오버 테스트
- 흔히 겪는 트러블슈팅
- 참고 자료
1. 실습 환경 및 전제 조건
이번 파트에서는 Part 1에서 정리한 패턴 A(3개 DC, 동기 복제, 자동 페일오버)를 실제로 구성한다. 아래 환경을 기준으로 작성하였으나, AWS/GCP/Azure 멀티 리전 VM이나 온프레미스 환경에도 동일하게 적용 가능하다.
구성 노드 목록
| 노드명 | 리전 | IP (예시) | 역할 |
|---|---|---|---|
| pg-seoul-1 | ap-northeast-2 (서울) | 10.1.0.10 | PostgreSQL Primary + etcd |
| pg-tokyo-1 | ap-northeast-1 (도쿄) | 10.2.0.10 | PostgreSQL Replica + etcd |
| pg-singapore-1 | ap-southeast-1 (싱가포르) | 10.3.0.10 | PostgreSQL Replica + etcd |
| haproxy-1 | ap-northeast-2 (서울, 예시) | 10.1.0.20 | HAProxy (애플리케이션 엔드포인트) |
권장 사양 (노드당): 4 vCPU, 8 GB RAM, NVMe SSD OS: Ubuntu 22.04 LTS (또는 RHEL 9) 소프트웨어: PostgreSQL 17, Patroni 4.1.x, etcd 3.5.x, HAProxy 2.8+
리전 간 포트 허용 목록
멀티 리전 구성 전, 보안 그룹(Security Group) 또는 방화벽에서 아래 포트를 반드시 허용해야 한다.
| 포트 | 용도 | 통신 방향 |
|---|---|---|
| 5432 | PostgreSQL | 노드 ↔ 노드, 앱 → 노드 |
| 8008 | Patroni REST API | 노드 ↔ 노드, HAProxy → 노드 |
| 2379 | etcd client | 노드 ↔ etcd |
| 2380 | etcd peer | etcd ↔ etcd |
2. Step 1 — TLS 인증서 발급
리전 간 통신은 퍼블릭 인터넷을 경유할 수 있으므로 etcd와 Patroni REST API 모두 TLS가 전제 조건이다. 선택 사항이 아니다. 여기서는 cfssl을 사용해 자체 CA를 구성한다. 프로덕션에서는 HashiCorp Vault나 AWS ACM Private CA를 활용하는 것이 좋다.
인증서를 발급할 때 반드시 Subject Alternative Name(SAN)에 IP 주소를 포함해야 한다. SAN에 IP가 빠지면 etcd TLS 연결이 실패하고 실습 초반에 막힌다.
CA 및 인증서 생성
# cfssl 설치 (모든 노드에서 실행)
apt-get install -y golang-cfssl
# 작업 디렉토리 생성
mkdir -p /etc/etcd/ssl && cd /etc/etcd/ssl
# CA 설정 파일 생성
cat > ca-config.json <<'EOF'
{
"signing": {
"default": { "expiry": "87600h" },
"profiles": {
"etcd": {
"expiry": "87600h",
"usages": ["signing", "key encipherment", "server auth", "client auth"]
}
}
}
}
EOF
# CA CSR 생성
cat > ca-csr.json <<'EOF'
{
"CN": "etcd-ca",
"key": { "algo": "rsa", "size": 2048 },
"names": [{ "O": "MyOrg", "OU": "DBA Team" }]
}
EOF
# CA 인증서 발급
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
# 각 etcd 노드용 인증서 발급 (서울 예시)
cat > etcd-seoul-csr.json <<'EOF'
{
"CN": "etcd-seoul",
"hosts": ["10.1.0.10", "pg-seoul-1", "127.0.0.1"],
"key": { "algo": "rsa", "size": 2048 }
}
EOF
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=etcd \
etcd-seoul-csr.json | cfssljson -bare etcd-seoul
# 도쿄, 싱가포르도 동일하게 발급 (hosts 항목의 IP와 호스트명만 변경)
발급한 ca.pem, etcd-{region}.pem, etcd-{region}-key.pem 파일을 각 해당 노드의 /etc/etcd/ssl/ 경로에 배포한다.
3. Step 2 — etcd 클러스터 구성
각 DC에 1개씩 총 3개의 etcd 노드를 구성한다. etcd는 Raft 합의 알고리즘을 사용하며, 3개 노드 중 과반(2개)이 살아 있으면 클러스터가 정상 동작한다.
etcd 설치
# 모든 노드에서 실행
ETCD_VER=v3.5.17
curl -LO https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzf etcd-${ETCD_VER}-linux-amd64.tar.gz
cp etcd-${ETCD_VER}-linux-amd64/etcd* /usr/local/bin/
etcd 설정 파일 — 서울 노드 (pg-seoul-1)
initial-cluster에는 세 노드를 모두 명시해야 한다. 도쿄/싱가포르 노드는 name, listen-client-urls, listen-peer-urls, advertise-* 항목의 IP만 해당 노드 IP로 변경하고, initial-cluster는 동일하게 유지한다.
# /etc/etcd/etcd.conf.yml (서울 노드)
name: etcd-seoul
data-dir: /var/lib/etcd/data
# 클라이언트 통신 (Patroni -> etcd)
listen-client-urls: https://10.1.0.10:2379,https://127.0.0.1:2379
advertise-client-urls: https://10.1.0.10:2379
# Peer 통신 (etcd <-> etcd, 리전 간)
listen-peer-urls: https://10.1.0.10:2380
initial-advertise-peer-urls: https://10.1.0.10:2380
# 초기 클러스터 구성 (3개 노드 모두 명시)
initial-cluster: >
etcd-seoul=https://10.1.0.10:2380,
etcd-tokyo=https://10.2.0.10:2380,
etcd-singapore=https://10.3.0.10:2380
initial-cluster-state: new
initial-cluster-token: pg-multi-region-cluster-v1
# TLS 설정 (클라이언트 <-> etcd)
client-transport-security:
cert-file: /etc/etcd/ssl/etcd-seoul.pem
key-file: /etc/etcd/ssl/etcd-seoul-key.pem
trusted-ca-file: /etc/etcd/ssl/ca.pem
client-cert-auth: true
# TLS 설정 (etcd <-> etcd Peer)
peer-transport-security:
cert-file: /etc/etcd/ssl/etcd-seoul.pem
key-file: /etc/etcd/ssl/etcd-seoul-key.pem
trusted-ca-file: /etc/etcd/ssl/ca.pem
peer-client-cert-auth: true
# 멀티 리전 환경에서 heartbeat/election timeout 조정
# 기본값(100ms/1000ms)은 리전 간 RTT가 높을 때 불안정할 수 있음
heartbeat-interval: 500
election-timeout: 5000
# 자동 압축 (디스크 사용량 관리)
auto-compaction-mode: revision
auto-compaction-retention: "1000"
etcd 서비스 등록 및 시작
cat > /etc/systemd/system/etcd.service <<'EOF'
[Unit]
Description=etcd key-value store
After=network.target
[Service]
Type=notify
User=etcd
ExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd.conf.yml
Restart=always
RestartSec=5s
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now etcd
etcd 클러스터 상태 확인
# 클러스터 멤버 목록 확인
etcdctl \
--endpoints=https://10.1.0.10:2379,https://10.2.0.10:2379,https://10.3.0.10:2379 \
--cacert=/etc/etcd/ssl/ca.pem \
--cert=/etc/etcd/ssl/etcd-seoul.pem \
--key=/etc/etcd/ssl/etcd-seoul-key.pem \
member list -w table
# 예상 출력:
# +------------------+---------+----------------+------------------------+------------------------+
# | ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
# +------------------+---------+----------------+------------------------+------------------------+
# | 8e9e05c52164694d | started | etcd-seoul | https://10.1.0.10:2380 | https://10.1.0.10:2379 |
# | 9b8e05c12164694a | started | etcd-tokyo | https://10.2.0.10:2380 | https://10.2.0.10:2379 |
# | ab8e05c12164694c | started | etcd-singapore | https://10.3.0.10:2380 | https://10.3.0.10:2379 |
# +------------------+---------+----------------+------------------------+------------------------+
4. Step 3 — PostgreSQL 초기화 및 Patroni 설치
PostgreSQL 설치
# 모든 PG 노드에서 실행 (Ubuntu 22.04 기준)
apt-get install -y postgresql-common
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
apt-get install -y postgresql-17
# Patroni가 직접 초기화를 담당하므로 기본 클러스터는 삭제
pg_dropcluster --stop 17 main
Patroni 설치
# Python 가상환경 활용 권장
apt-get install -y python3-pip python3-venv
python3 -m venv /opt/patroni
/opt/patroni/bin/pip install patroni[etcd3] psycopg[binary]
# 심볼릭 링크
ln -s /opt/patroni/bin/patroni /usr/local/bin/patroni
ln -s /opt/patroni/bin/patronictl /usr/local/bin/patronictl
5. Step 4 — patroni.yml 작성
동기 복제 멀티 DC 구성의 핵심은 synchronous_mode: true 설정이다. 이 옵션을 활성화하면 Primary는 Synchronous Replica 중 하나에 WAL이 기록되어야 트랜잭션을 커밋으로 인정한다.
patroni.yml — 서울 노드 (Primary 후보)
# /etc/patroni/patroni.yml (pg-seoul-1)
scope: pg-multiregion # 클러스터 이름 (모든 노드 동일)
namespace: /db/ # etcd 키 네임스페이스
name: pg-seoul-1 # 노드 고유 이름 (각 노드마다 다르게)
# -- REST API -------------------------------------------------------
restapi:
listen: 0.0.0.0:8008
connect_address: 10.1.0.10:8008
# TLS 설정 (HAProxy가 HTTPS로 헬스 체크)
certfile: /etc/patroni/ssl/patroni.pem
keyfile: /etc/patroni/ssl/patroni-key.pem
cafile: /etc/patroni/ssl/ca.pem
# -- DCS: etcd3 ----------------------------------------------------
etcd3:
hosts:
- 10.1.0.10:2379 # 서울 etcd
- 10.2.0.10:2379 # 도쿄 etcd
- 10.3.0.10:2379 # 싱가포르 etcd
protocol: https
cacert: /etc/etcd/ssl/ca.pem
cert: /etc/etcd/ssl/etcd-seoul.pem
key: /etc/etcd/ssl/etcd-seoul-key.pem
# -- Bootstrap (최초 클러스터 초기화 시에만 사용) ------------------
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576 # 1MB 이상 뒤처진 replica는 failover 후보 제외
# 핵심: 동기 복제 활성화
synchronous_mode: true
synchronous_mode_strict: false # true 시 sync replica 없으면 쓰기 중단
synchronous_node_count: 1 # 최소 1개의 sync replica 유지
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
wal_level: replica
hot_standby: "on"
max_wal_senders: 10
max_replication_slots: 10
wal_log_hints: "on" # pg_rewind를 위해 필수
archive_mode: "on"
archive_command: 'pgbackrest --stanza=main archive-push %p'
initdb:
- encoding: UTF8
- data-checksums # pg_rewind 신뢰성 향상
pg_hba:
- local all all trust
- host all all 127.0.0.1/32 md5
- host replication replicator 10.0.0.0/8 md5
- hostssl all all 10.0.0.0/8 md5
# -- PostgreSQL ----------------------------------------------------
postgresql:
listen: 0.0.0.0:5432
connect_address: 10.1.0.10:5432
data_dir: /var/lib/postgresql/17/main
bin_dir: /usr/lib/postgresql/17/bin
config_dir: /etc/postgresql/17/main
authentication:
replication:
username: replicator
password: "SecureRepPass123!"
superuser:
username: postgres
password: "SecureSuperPass123!"
rewind:
username: rewind_user
password: "SecureRewindPass123!"
parameters:
unix_socket_directories: '/var/run/postgresql'
# -- Tags ----------------------------------------------------------
tags:
nofailover: false
noloadbalance: false
clonefrom: false
nosync: false
dc: seoul # 사용자 정의 태그 (HAProxy 리전별 라우팅용)
도쿄/싱가포르 노드는 name, restapi.connect_address, postgresql.connect_address, tags.dc 항목을 각 노드에 맞게 변경한다. etcd3.cert/key도 해당 리전 인증서로 교체한다.
archive_command는 pgbackrest가 사전에 설치·구성되어 있어야 동작한다. WAL 아카이빙이 당장 필요 없다면 archive_mode: "off"로 비활성화하고 이후 구성할 수 있다.
Patroni 서비스 등록 및 시작
cat > /etc/systemd/system/patroni.service <<'EOF'
[Unit]
Description=Patroni PostgreSQL HA
After=syslog.target network.target etcd.service
Requires=etcd.service
[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/usr/local/bin/patroni /etc/patroni/patroni.yml
Restart=on-failure
RestartSec=5s
KillMode=process
TimeoutStopSec=30
LimitNOFILE=65536
Environment="MALLOC_ARENA_MAX=1"
[Install]
WantedBy=multi-user.target
EOF
# 서울 노드 먼저 시작 (초기 Primary 지정)
systemctl enable --now patroni
# 서울이 Primary로 올라온 것을 확인 후, 도쿄 -> 싱가포르 순으로 시작
ssh pg-tokyo-1 "systemctl enable --now patroni"
ssh pg-singapore-1 "systemctl enable --now patroni"
6. Step 5 — HAProxy 커넥션 라우팅
Patroni의 REST API는 HAProxy 헬스 체크와 최적화되어 있다. HAProxy는 Patroni REST API 헬스 체크를 통해 PostgreSQL 상태를 판별한다. PostgreSQL 프로세스에 직접 접속해 상태를 확인하는 것이 아니다.
/primary: Leader Lock을 보유한 Primary일 때만 HTTP 200 반환/replica: 정상 실행 중인 Replica일 때 200 반환/synchronous: Synchronous Replica 노드에서만 200 반환
동기 복제 환경에서 /synchronous 엔드포인트를 활용하면 Synchronous Replica 노드만 대상으로 읽기를 라우팅할 수 있어 읽기 내구성을 보장할 수 있다.
haproxy.cfg
# /etc/haproxy/haproxy.cfg
global
maxconn 200
log /dev/log local0
defaults
log global
mode tcp
retries 2
timeout client 30m
timeout connect 4s
timeout server 30m
timeout check 5s
# -- 통계 대시보드 -----------------------------------------------
listen stats
bind *:7000
mode http
stats enable
stats uri /
stats refresh 10s
# -- 쓰기 트래픽 -> Primary 전용 (포트 5000) ---------------------
listen pg_primary
bind *:5000
option httpchk OPTIONS /primary
http-check expect status 200
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server pg-seoul-1 10.1.0.10:5432 check port 8008 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem
server pg-tokyo-1 10.2.0.10:5432 check port 8008 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem
server pg-singapore-1 10.3.0.10:5432 check port 8008 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem
# -- 읽기 트래픽 -> Replica 라운드로빈 (포트 5001) ---------------
listen pg_replicas
bind *:5001
balance roundrobin
option httpchk OPTIONS /replica
http-check expect status 200
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server pg-seoul-1 10.1.0.10:5432 check port 8008 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem
server pg-tokyo-1 10.2.0.10:5432 check port 8008 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem
server pg-singapore-1 10.3.0.10:5432 check port 8008 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem
# -- 동기 Replica 전용 읽기 (포트 5002, 읽기 내구성 보장) --------
listen pg_sync_replicas
bind *:5002
balance roundrobin
option httpchk OPTIONS /synchronous
http-check expect status 200
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server pg-seoul-1 10.1.0.10:5432 check port 8008 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem
server pg-tokyo-1 10.2.0.10:5432 check port 8008 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem
server pg-singapore-1 10.3.0.10:5432 check port 8008 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem
/primary 체크에서 오류를 반환하는 노드를 비정상으로 표시하고, 200을 반환하는 유일한 노드(Primary)로만 쓰기 연결을 라우팅한다. /replica 또는 /synchronous 백엔드 그룹에서는 Primary가 503을 반환하므로 자연스럽게 읽기 전용 노드로만 분산된다.
7. Step 6 — 클러스터 검증 및 자동 페일오버 테스트
클러스터 상태 확인
# 전체 클러스터 토폴로지 확인
patronictl -c /etc/patroni/patroni.yml topology
# 예상 출력:
# + Cluster: pg-multiregion (7891234567890123456) +--------------+-----------+
# | Member | Host | Role | State | TL | Lag in MB |
# +-----------------+-----------------+--------------+---------+----+-----------+
# | pg-seoul-1 | 10.1.0.10:5432 | Leader | running | 1 | |
# | pg-tokyo-1 | 10.2.0.10:5432 | Sync Standby | running | 1 | 0.0 |
# | pg-singapore-1 | 10.3.0.10:5432 | Replica | running | 1 | 0.0 |
# +-----------------+-----------------+--------------+---------+----+-----------+
# 동기 복제 상태 확인 (Primary에서 실행)
psql -U postgres -c "
SELECT application_name, sync_state, write_lag, flush_lag, replay_lag
FROM pg_stat_replication;
"
sync_state가 sync인 노드가 Synchronous Replica다. async로 표시된 노드는 Asynchronous Replica로 동작 중이므로 synchronous_node_count 설정이 의도대로 적용됐는지 확인한다.
자동 페일오버 시뮬레이션
# -- 테스트 1: Primary 노드(서울) 강제 종료 -----------------------
# 서울 노드에서:
systemctl stop patroni
# 다른 노드에서 페일오버 감지 모니터링
watch -n1 "patronictl -c /etc/patroni/patroni.yml list"
# 약 10-30초 내에 도쿄 또는 싱가포르가 Leader로 승격됨
# (Synchronous Replica였던 도쿄가 우선 승격됨)
# -- 테스트 2: patronictl을 이용한 계획된 Switchover --------------
patronictl -c /etc/patroni/patroni.yml switchover \
--master pg-seoul-1 \
--candidate pg-tokyo-1 \
--scheduled now \
--force
# -- 테스트 3: etcd 노드 1개 장애 (Quorum 유지 확인) -------------
# 싱가포르 etcd 노드 중단
ssh pg-singapore-1 "systemctl stop etcd"
# etcd 클러스터가 2/3 quorum으로 정상 운영되는지 확인
etcdctl \
--endpoints=https://10.1.0.10:2379,https://10.2.0.10:2379 \
--cacert=/etc/etcd/ssl/ca.pem \
--cert=/etc/etcd/ssl/etcd-seoul.pem \
--key=/etc/etcd/ssl/etcd-seoul-key.pem \
endpoint health
페일오버 후 원복 절차
# 서울 노드 복구 후 Replica로 재합류
systemctl start patroni
# Patroni가 자동으로 pg_basebackup을 통해 현재 Primary와 동기화함
# 진행 상황 확인:
patronictl -c /etc/patroni/patroni.yml list
# 자동 재합류가 실패하면 수동으로 reinitialize
patronictl -c /etc/patroni/patroni.yml reinit pg-multiregion pg-seoul-1
8. 흔히 겪는 트러블슈팅
문제 1: etcd TLS 연결 실패
CRITICAL: get_cluster
etcd3 exception: <class 'etcd3.exceptions.ConnectionFailedError'>
SAN에 IP 주소가 포함되어 있지 않은 경우 이 오류가 발생한다.
# 인증서 Subject Alternative Name(SAN) 확인
openssl x509 -in /etc/etcd/ssl/etcd-seoul.pem -text | grep -A2 "Subject Alternative"
# etcd 클라이언트 직접 연결 테스트
etcdctl --endpoints=https://10.1.0.10:2379 \
--cacert=/etc/etcd/ssl/ca.pem \
--cert=/etc/etcd/ssl/etcd-seoul.pem \
--key=/etc/etcd/ssl/etcd-seoul-key.pem \
get / --prefix --keys-only
문제 2: HAProxy에서 REST API TLS 헬스 체크 실패
HAProxy 설정에서 check-ssl 옵션은 반드시 host 항목 뒤에 위치해야 한다. 순서가 잘못되면 SSL 핸드셰이크 오류가 발생한다.
# 잘못된 예 (check-ssl이 host 앞에 위치)
server pg-seoul-1 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem 10.1.0.10:5432 check port 8008
# 올바른 예 (host 뒤에 check-ssl 위치)
server pg-seoul-1 10.1.0.10:5432 check port 8008 check-ssl ssl ca-file /etc/haproxy/ssl/ca.pem
문제 3: synchronous_mode에서 쓰기가 블로킹됨
synchronous_mode_strict: true로 설정했을 때 Sync Replica가 모두 다운되면 Primary도 쓰기를 거부한다. 긴급 상황에서는 임시로 strict 모드를 비활성화한다.
# 현재 동적 설정 확인
patronictl -c /etc/patroni/patroni.yml show-config
# 긴급 상황 시 strict 모드 임시 비활성화
patronictl -c /etc/patroni/patroni.yml edit-config
# edit synchronous_mode_strict: false and save
문제 4: 리전 간 RTT로 인한 etcd 리더 선출 불안정
# etcd 로그에서 election timeout 관련 경고 확인
journalctl -u etcd | grep -i "election\|timeout\|heartbeat"
# heartbeat/election timeout 조정 원칙
# heartbeat-interval = RTT의 2-3배, election-timeout = heartbeat의 10배
# 서울-싱가포르 RTT ~70ms 기준 예시:
# heartbeat-interval: 200
# election-timeout: 2000
RTT를 먼저 실측(ping -c 20 <remote-ip>)한 뒤 타임아웃 값을 결정한다. 기본값(heartbeat 100ms, election timeout 1000ms)은 리전 간 RTT가 50ms를 넘으면 불안정해질 수 있다.
참고 자료
- Patroni 공식 문서 — Multi-Datacenter HA Configuration
- Patroni 공식 문서 — REST API Health Check Endpoints
- Percona — HAProxy with Patroni Health Check Endpoints and Debugging
- pgEdge — Using Patroni to Build a Highly Available Postgres Cluster: HAProxy
- CYBERTEC — Patroni etcd Clusters: Introduction and How-To