文章目录
-
- 一、核心概念与架构
-
- [1.1 Patroni 是什么?](#1.1 Patroni 是什么?)
- [1.2 架构组成](#1.2 架构组成)
- 二、环境规划
-
- [2.1 节点角色与 IP](#2.1 节点角色与 IP)
- [三、部署 etcd 集群](#三、部署 etcd 集群)
-
- [3.1 安装 etcd(以 CentOS 为例)](#3.1 安装 etcd(以 CentOS 为例))
- [3.2 创建配置文件](#3.2 创建配置文件)
- [3.3 启动 etcd](#3.3 启动 etcd)
- [3.4 验证 etcd 集群](#3.4 验证 etcd 集群)
- [四、部署 PostgreSQL 与 Patroni](#四、部署 PostgreSQL 与 Patroni)
-
- [4.1 安装 PostgreSQL(所有 PG 节点)](#4.1 安装 PostgreSQL(所有 PG 节点))
- [4.2 安装 Patroni](#4.2 安装 Patroni)
- [4.3 配置 Patroni(以 pg1 为例)](#4.3 配置 Patroni(以 pg1 为例))
- [4.4 创建数据目录并授权](#4.4 创建数据目录并授权)
- [4.5 启动 Patroni](#4.5 启动 Patroni)
- 五、集群状态验证
-
- [5.1 使用 patronictl 查看集群](#5.1 使用 patronictl 查看集群)
- [5.2 访问 REST API](#5.2 访问 REST API)
- [5.3 验证流复制](#5.3 验证流复制)
- 六、自动故障转移演练
-
- [6.1 模拟主库宕机](#6.1 模拟主库宕机)
- [6.2 观察自动切换](#6.2 观察自动切换)
- [6.3 验证新主库](#6.3 验证新主库)
- [6.4 恢复原主库](#6.4 恢复原主库)
- 七、应用连接与负载均衡(HAProxy)
-
- [7.1 安装 HAProxy](#7.1 安装 HAProxy)
- [7.2 配置 HAProxy(/etc/haproxy/haproxy.cfg)](#7.2 配置 HAProxy(/etc/haproxy/haproxy.cfg))
- [7.3 启动 HAProxy](#7.3 启动 HAProxy)
- 八、高级特性与运维
-
- [8.1 手动切换(Switchover)](#8.1 手动切换(Switchover))
- [8.2 配置动态重载](#8.2 配置动态重载)
- [8.3 备份集成](#8.3 备份集成)
- [8.4 监控与告警](#8.4 监控与告警)
- 九、常见问题与实践建议
-
- [9.1 脑裂防护](#9.1 脑裂防护)
- [9.2 网络分区(Split-Brain)](#9.2 网络分区(Split-Brain))
- [9.3 性能建议](#9.3 性能建议)
- [9.4 安全加固](#9.4 安全加固)
PostgreSQL 本身具备强大的流复制能力,但缺乏内置的自动故障转移(Automatic Failover)机制。为实现生产级高可用(High Availability, HA),社区广泛采用 Patroni 配合分布式协调服务(如 etcd、Consul、ZooKeeper)构建自动主备切换集群。本文将从原理、架构、部署、配置、演练到运维,详解基于 etcd 的 Patroni 高可用集群。
一、核心概念与架构
1.1 Patroni 是什么?
Patroni 是一个用 Python 编写的 PostgreSQL 高可用模板(Template),由 Zalando 开发并开源。它不修改 PostgreSQL 源码,而是通过外部守护进程(Daemon)管理 PostgreSQL 实例的生命周期,结合分布式协调器(DCS)实现:
- 自动主节点选举(Leader Election)
- 故障检测与自动 Failover
- 在线配置重载
- RESTful API 管理接口
- 支持多副本、同步/异步复制、级联复制等
1.2 架构组成
一个典型的 Patroni + etcd 高可用集群包含以下组件:
| 组件 | 作用 |
|---|---|
| PostgreSQL 实例 | 数据库服务,运行在每个节点 |
| Patroni 进程 | 每个节点运行一个,监控本地 PG 状态,与 DCS 通信 |
| etcd 集群 | 分布式键值存储,用于存储集群元数据、锁、Leader 信息(至少 3 节点以容忍 1 节点故障) |
| HAProxy / PgBouncer(可选) | 应用连接代理,自动路由读写请求 |
| VIP / DNS / Service Discovery(可选) | 提供统一访问入口 |
典型拓扑(3 节点 PG + 3 节点 etcd):
+----------------+ +----------------+ +----------------+
| PG Node 1 | | PG Node 2 | | PG Node 3 |
| - PostgreSQL | | - PostgreSQL | | - PostgreSQL |
| - Patroni |<--->| - Patroni |<--->| - Patroni |
+----------------+ +----------------+ +----------------+
^ ^ ^
| | |
+----------------------+----------------------+
|
+-------------+
| etcd Cluster (3 nodes) |
+-------------+
注意:etcd 可独立部署,也可与 PG 节点共存(不推荐生产环境混部)。
二、环境规划
2.1 节点角色与 IP
| 主机名 | IP 地址 | 角色 | 说明 |
|---|---|---|---|
| etcd1 | 192.168.10.10 | etcd | etcd 集群成员 |
| etcd2 | 192.168.10.11 | etcd | etcd 集群成员 |
| etcd3 | 192.168.10.12 | etcd | etcd 集群成员 |
| pg1 | 192.168.10.20 | PostgreSQL + Patroni | 数据库节点 |
| pg2 | 192.168.10.21 | PostgreSQL + Patroni | 数据库节点 |
| pg3 | 192.168.10.22 | PostgreSQL + Patroni | 数据库节点 |
| haproxy | 192.168.10.30 | HAProxy | 应用连接代理(可选) |
操作系统:CentOS 7 或 Ubuntu 20.04+
PostgreSQL 版本:14(所有 PG 节点版本必须一致)
Python 版本:3.6+
三、部署 etcd 集群
etcd 是 Patroni 的"大脑",用于存储集群状态。需部署奇数节点(3、5...)以支持容错。
3.1 安装 etcd(以 CentOS 为例)
在 etcd1、etcd2、etcd3 上执行:
bash
# 下载 etcd
ETCD_VER=v3.5.10
wget https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf etcd-${ETCD_VER}-linux-amd64.tar.gz
sudo cp etcd-${ETCD_VER}-linux-amd64/etcd* /usr/local/bin/
3.2 创建配置文件
以 etcd1 为例(/etc/etcd/etcd.conf.yml):
yaml
name: etcd1
data-dir: /var/lib/etcd
listen-peer-urls: http://192.168.10.10:2380
listen-client-urls: http://192.168.10.10:2379,http://127.0.0.1:2379
initial-advertise-peer-urls: http://192.168.10.10:2380
advertise-client-urls: http://192.168.10.10:2379
initial-cluster: etcd1=http://192.168.10.10:2380,etcd2=http://192.168.10.11:2380,etcd3=http://192.168.10.12:2380
initial-cluster-token: pg-etcd-token
initial-cluster-state: new
etcd2 和 etcd3 修改 name、IP 即可。
3.3 启动 etcd
创建 systemd 服务(/etc/systemd/system/etcd.service):
ini
[Unit]
Description=etcd
After=network.target
[Service]
Type=exec
ExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd.conf.yml
Restart=always
User=root
[Install]
WantedBy=multi-user.target
启动:
bash
sudo systemctl daemon-reload
sudo systemctl enable --now etcd
3.4 验证 etcd 集群
在任意 etcd 节点执行:
bash
etcdctl --endpoints=http://192.168.10.10:2379,http://192.168.10.11:2379,http://192.168.10.12:2379 endpoint health
应全部返回 healthy。
四、部署 PostgreSQL 与 Patroni
4.1 安装 PostgreSQL(所有 PG 节点)
bash
# CentOS
sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo yum install -y postgresql14-server postgresql14-contrib
# 不要初始化!Patroni 会自动初始化
4.2 安装 Patroni
bash
sudo yum install -y python3-pip
pip3 install patroni[etcd] psycopg2-binary
验证安装:
bash
patroni --version
4.3 配置 Patroni(以 pg1 为例)
创建 /etc/patroni/patroni.yml:
yaml
scope: pg-ha-cluster # 集群名称,所有节点必须一致
namespace: /service/ # etcd 中的命名空间
name: pg1 # 本节点名称,必须唯一
restapi:
listen: 192.168.10.20:8008 # Patroni API 监听地址
connect_address: 192.168.10.20:8008
etcd:
hosts: # etcd 集群地址
- 192.168.10.10:2379
- 192.168.10.11:2379
- 192.168.10.12:2379
bootstrap:
dcs:
ttl: 30 # Leader 锁 TTL(秒)
loop_wait: 10 # 主循环间隔
retry_timeout: 10 # 操作超时
maximum_lag_on_failover: 1048576 # 最大允许延迟(字节),约 1MB
synchronous_mode: false # 是否启用同步复制
postgresql:
use_pg_rewind: true # 故障恢复时使用 pg_rewind
use_slots: true # 使用复制槽防止 WAL 过早清理
parameters:
wal_level: replica
hot_standby: "on"
max_connections: 200
max_wal_senders: 10
wal_keep_size: 1GB
max_replication_slots: 10
checkpoint_completion_target: 0.9
archive_mode: "off"
initdb:
- encoding: UTF8
- data-checksums # 启用数据页校验,pg_rewind 必需
users:
admin: # 初始化超级用户
password: admin123
options:
- createrole
- createdb
postgresql:
listen: 192.168.10.20:5432 # PostgreSQL 监听地址
connect_address: 192.168.10.20:5432
data_dir: /var/lib/pgsql/14/data
bin_dir: /usr/pgsql-14/bin
authentication:
replication:
username: replicator
password: replpass123
superuser:
username: postgres
password: postgres123
parameters:
unix_socket_directories: '/tmp'
tags:
nofailover: false # 允许参与 Failover
noloadbalance: false # 允许负载均衡(读)
clonefrom: true # 允许其他节点从此节点克隆
关键说明:
scope必须全局唯一,用于隔离多个 Patroni 集群;use_pg_rewind: true要求data-checksums或wal_log_hints = on;- 所有 PG 节点的
patroni.yml仅需修改name、listen、connect_address。
4.4 创建数据目录并授权
bash
sudo mkdir -p /var/lib/pgsql/14/data
sudo chown postgres:postgres /var/lib/pgsql/14/data
4.5 启动 Patroni
切换到 postgres 用户启动(避免权限问题):
bash
sudo -u postgres patroni /etc/patroni/patroni.yml
首次启动时,Patroni 会:
- 在 etcd 中注册集群;
- 执行
initdb初始化数据库; - 设置复制用户和参数;
- 自动选举第一个启动的节点为主库(Leader)。
建议使用 systemd 管理 Patroni 进程(见附录)。
五、集群状态验证
5.1 使用 patronictl 查看集群
在任意 PG 节点执行:
bash
patronictl -c /etc/patroni/patroni.yml list
输出示例:
+ Cluster: pg-ha-cluster (7073733813877921290) -------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+--------+----------------+---------+---------+----+-----------+
| pg1 | 192.168.10.20 | Leader | running | 1 | 0 |
| pg2 | 192.168.10.21 | Replica | running | 1 | 0 |
| pg3 | 192.168.10.22 | Replica | running | 1 | 0 |
+--------+----------------+---------+---------+----+-----------+
5.2 访问 REST API
bash
curl http://192.168.10.20:8008/cluster
curl http://192.168.10.20:8008/primary # 返回 200 表示是主库
5.3 验证流复制
在主库执行:
sql
SELECT * FROM pg_stat_replication;
应看到两个备库连接。
六、自动故障转移演练
6.1 模拟主库宕机
在 pg1 上 kill Patroni 进程:
bash
sudo pkill -f "patroni /etc/patroni/patroni.yml"
6.2 观察自动切换
- etcd 在 30 秒(ttl)内未收到 pg1 的心跳;
- Patroni 在 pg2/pg3 上检测到 Leader 失联;
- 触发选举,其中一个备库(如 pg2)自动 promote 为主库;
- 原主库(pg1)重启后自动 rejoin 为备库。
查看日志(/var/log/patroni.log 或终端输出)可看到:
2026-02-10 19:30:00,000 INFO: Lock owner: pg1; I am pg2
2026-02-10 19:30:05,000 INFO: does not have lock
2026-02-10 19:30:10,000 INFO: demoting self because leader is not available
2026-02-10 19:30:15,000 INFO: promoting to leader
6.3 验证新主库
bash
patronictl -c /etc/patroni/patroni.yml list
# pg2 应变为 Leader
# 在 pg2 上写入数据
psql -h 192.168.10.21 -U admin -d postgres -c "CREATE TABLE test(id int);"
6.4 恢复原主库
重启 pg1 上的 Patroni:
bash
sudo -u postgres patroni /etc/patroni/patroni.yml
Patroni 会自动:
- 检测到自己不是 Leader;
- 使用
pg_rewind同步差异(因use_pg_rewind: true); - 启动为备库。
若未启用 checksums,Patroni 会使用
pg_basebackup重建,耗时较长。
七、应用连接与负载均衡(HAProxy)
为避免应用硬编码主库 IP,使用 HAProxy 提供统一入口。
7.1 安装 HAProxy
在 haproxy 节点:
bash
sudo yum install -y haproxy
7.2 配置 HAProxy(/etc/haproxy/haproxy.cfg)
cfg
global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
defaults
mode tcp
log global
option tcplog
timeout connect 10s
timeout client 1h
timeout server 1h
# 写请求:只路由到主库
frontend pg_write
bind *:5000
default_backend pg_primary
# 读请求:可路由到主库或备库
frontend pg_read
bind *:5001
default_backend pg_replicas
backend pg_primary
option httpchk GET /primary
http-check expect status 200
server pg1 192.168.10.20:5432 maxconn 100 check port 8008
server pg2 192.168.10.21:5432 maxconn 100 check port 8008
server pg3 192.168.10.22:5432 maxconn 100 check port 8008
backend pg_replicas
option httpchk GET /replica
http-check expect status 200
server pg1 192.168.10.20:5432 maxconn 100 check port 8008
server pg2 192.168.10.21:5432 maxconn 100 check port 8008
server pg3 192.168.10.22:5432 maxconn 100 check port 8008
7.3 启动 HAProxy
bash
sudo systemctl enable --now haproxy
应用连接:
- 写:
host=192.168.10.30 port=5000 - 读:
host=192.168.10.30 port=5001
八、高级特性与运维
8.1 手动切换(Switchover)
计划内维护时,安全切换主库:
bash
patronictl -c /etc/patroni/patroni.yml switchover
# 交互式选择新主库
或指定目标:
bash
patronictl -c /etc/patroni/patroni.yml switchover --candidate pg2
8.2 配置动态重载
修改 patroni.yml 后无需重启:
bash
patronictl -c /etc/patroni/patroni.yml reload pg1
8.3 备份集成
Patroni 支持与 WAL-G、pgBackRest 集成,实现 PITR。
8.4 监控与告警
- 使用
patronictl list或 API 获取状态; - Prometheus exporter:
patroni --config-file ... --prometheus-port 9369; - 告警指标:复制延迟、主库宕机、etcd 不可用。
九、常见问题与实践建议
9.1 脑裂防护
- etcd 的 TTL 机制确保同一时间只有一个 Leader;
- 不要手动干预 PostgreSQL 角色(如直接执行
pg_ctl promote)。
9.2 网络分区(Split-Brain)
- etcd 需多数派存活(3 节点最多容忍 1 节点故障);
- 避免将 etcd 与 PG 混部在同一物理机。
9.3 性能建议
- etcd 使用 SSD 存储;
- Patroni 节点时间同步(NTP);
- 合理设置
ttl、loop_wait(通常 ttl > 2 * loop_wait)。
9.4 安全加固
- etcd 启用 TLS;
- Patroni API 启用认证;
- PostgreSQL 使用 scram-sha-256 密码加密。
总结:基于 Patroni + etcd 的 PostgreSQL 高可用架构具备以下优势:
- 自动化:故障检测、Failover、Rejoin 全自动;
- 安全:通过 DCS 避免脑裂;
- 灵活:支持同步/异步、多副本、级联;
- 可观测:提供 REST API 和 CLI 工具;
- 生态友好:与 Kubernetes(Spilo)、Prometheus、HAProxy 无缝集成。