文章目录
- 一、核心概念与架构
- 1.1 Patroni 是什么?
- 1.2 架构组成
- 二、环境规划
- 2.1 节点角色与 IP
- 三、部署 etcd 集群
- 3.1 安装 etcd(以 CentOS 为例)
- 3.2 创建配置文件
- 3.3 启动 etcd
- 3.4 验证 etcd 集群
- 四、部署 PostgreSQL 与 Patroni
- 4.1 安装 PostgreSQL(所有 PG 节点)
- 4.2 安装 Patroni
- 4.3 配置 Patroni(以 pg1 为例)
- 4.4 创建数据目录并授权
- 4.5 启动 Patroni
- 五、集群状态验证
- 5.1 使用 patronictl 查看集群
- 5.2 访问 REST API
- 5.3 验证流复制
- 六、自动故障转移演练
- 6.1 模拟主库宕机
- 6.2 观察自动切换
- 6.3 验证新主库
- 6.4 恢复原主库
- 七、应用连接与负载均衡(HAProxy)
- 7.1 安装 HAProxy
- 7.2 配置 HAProxy(/etc/haproxy/haproxy.cfg)
- 7.3 启动 HAProxy
- 八、高级特性与运维
- 8.1 手动切换(Switchover)
- 8.2 配置动态重载
- 8.3 备份集成
- 8.4 监控与告警
- 九、常见问题与实践建议
- 9.1 脑裂防护
- 9.2 网络分区(Split-Brain)
- 9.3 性能建议
- 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 上执行:
# 下载 etcdETCD_VER=v3.5.10wgethttps://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gztarxzvf etcd-${ETCD_VER}-linux-amd64.tar.gzsudocpetcd-${ETCD_VER}-linux-amd64/etcd* /usr/local/bin/3.2 创建配置文件
以 etcd1 为例(/etc/etcd/etcd.conf.yml):
name:etcd1data-dir:/var/lib/etcdlisten-peer-urls:http://192.168.10.10:2380listen-client-urls:http://192.168.10.10:2379,http://127.0.0.1:2379initial-advertise-peer-urls:http://192.168.10.10:2380advertise-client-urls:http://192.168.10.10:2379initial-cluster:etcd1=http://192.168.10.10:2380,etcd2=http://192.168.10.11:2380,etcd3=http://192.168.10.12:2380initial-cluster-token:pg-etcd-tokeninitial-cluster-state:newetcd2 和 etcd3 修改name、IP 即可。
3.3 启动 etcd
创建 systemd 服务(/etc/systemd/system/etcd.service):
[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启动:
sudosystemctl daemon-reloadsudosystemctlenable--now etcd3.4 验证 etcd 集群
在任意 etcd 节点执行:
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 节点)
# CentOSsudoyuminstall-y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpmsudoyuminstall-y postgresql14-server postgresql14-contrib# 不要初始化!Patroni 会自动初始化4.2 安装 Patroni
sudoyuminstall-y python3-pip pip3installpatroni[etcd]psycopg2-binary验证安装:
patroni --version4.3 配置 Patroni(以 pg1 为例)
创建/etc/patroni/patroni.yml:
scope:pg-ha-cluster# 集群名称,所有节点必须一致namespace:/service/# etcd 中的命名空间name:pg1# 本节点名称,必须唯一restapi:listen:192.168.10.20:8008# Patroni API 监听地址connect_address:192.168.10.20:8008etcd:hosts:# etcd 集群地址-192.168.10.10:2379-192.168.10.11:2379-192.168.10.12:2379bootstrap:dcs:ttl:30# Leader 锁 TTL(秒)loop_wait:10# 主循环间隔retry_timeout:10# 操作超时maximum_lag_on_failover:1048576# 最大允许延迟(字节),约 1MBsynchronous_mode:false# 是否启用同步复制postgresql:use_pg_rewind:true# 故障恢复时使用 pg_rewinduse_slots:true# 使用复制槽防止 WAL 过早清理parameters:wal_level:replicahot_standby:"on"max_connections:200max_wal_senders:10wal_keep_size:1GBmax_replication_slots:10checkpoint_completion_target:0.9archive_mode:"off"initdb:-encoding:UTF8-data-checksums# 启用数据页校验,pg_rewind 必需users:admin:# 初始化超级用户password:admin123options:-createrole-createdbpostgresql:listen:192.168.10.20:5432# PostgreSQL 监听地址connect_address:192.168.10.20:5432data_dir:/var/lib/pgsql/14/databin_dir:/usr/pgsql-14/binauthentication:replication:username:replicatorpassword:replpass123superuser:username:postgrespassword:postgres123parameters:unix_socket_directories:'/tmp'tags:nofailover:false# 允许参与 Failovernoloadbalance:false# 允许负载均衡(读)clonefrom:true# 允许其他节点从此节点克隆关键说明:
scope必须全局唯一,用于隔离多个 Patroni 集群;use_pg_rewind: true要求data-checksums或wal_log_hints = on;- 所有 PG 节点的
patroni.yml仅需修改name、listen、connect_address。
4.4 创建数据目录并授权
sudomkdir-p /var/lib/pgsql/14/datasudochownpostgres:postgres /var/lib/pgsql/14/data4.5 启动 Patroni
切换到 postgres 用户启动(避免权限问题):
sudo-u postgres patroni /etc/patroni/patroni.yml首次启动时,Patroni 会:
- 在 etcd 中注册集群;
- 执行
initdb初始化数据库; - 设置复制用户和参数;
- 自动选举第一个启动的节点为主库(Leader)。
建议使用 systemd 管理 Patroni 进程(见附录)。
五、集群状态验证
5.1 使用 patronictl 查看集群
在任意 PG 节点执行:
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
curlhttp://192.168.10.20:8008/clustercurlhttp://192.168.10.20:8008/primary# 返回 200 表示是主库5.3 验证流复制
在主库执行:
SELECT*FROMpg_stat_replication;应看到两个备库连接。
六、自动故障转移演练
6.1 模拟主库宕机
在 pg1 上 kill Patroni 进程:
sudopkill-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 leader6.3 验证新主库
patronictl -c /etc/patroni/patroni.yml list# pg2 应变为 Leader# 在 pg2 上写入数据psql -h192.168.10.21 -U admin -d postgres -c"CREATE TABLE test(id int);"6.4 恢复原主库
重启 pg1 上的 Patroni:
sudo-u postgres patroni /etc/patroni/patroni.ymlPatroni 会自动:
- 检测到自己不是 Leader;
- 使用
pg_rewind同步差异(因use_pg_rewind: true); - 启动为备库。
若未启用 checksums,Patroni 会使用
pg_basebackup重建,耗时较长。
七、应用连接与负载均衡(HAProxy)
为避免应用硬编码主库 IP,使用 HAProxy 提供统一入口。
7.1 安装 HAProxy
在 haproxy 节点:
sudoyuminstall-y haproxy7.2 配置 HAProxy(/etc/haproxy/haproxy.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 80087.3 启动 HAProxy
sudosystemctlenable--now haproxy应用连接:
- 写:
host=192.168.10.30 port=5000 - 读:
host=192.168.10.30 port=5001
八、高级特性与运维
8.1 手动切换(Switchover)
计划内维护时,安全切换主库:
patronictl -c /etc/patroni/patroni.yml switchover# 交互式选择新主库或指定目标:
patronictl -c /etc/patroni/patroni.yml switchover --candidate pg28.2 配置动态重载
修改patroni.yml后无需重启:
patronictl -c /etc/patroni/patroni.yml reload pg18.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 无缝集成。