postgresql 17高可用集群用patroni实现自动故障转移,必须部署etcd(或consul/zookeeper)作为强制前置的分布式协调器;三节点etcd为最小安全配置,需先启动并验证healthy,再启patroni,且配置中postgresql.use_pg_rewind、tags.nofailover等关键开关须正确设置。

直接上结论:PostgreSQL 17 高可用集群用 Patroni 实现自动故障转移,**不能只装 Patroni 和 PostgreSQL**,必须搭好 etcd(或 Consul/ZooKeeper)作为分布式协调器,否则 Patroni 启动后连状态都存不进去,patronictl list 会一直报 DCS is not accessible。
etcd 集群必须先跑起来,且节点数得是奇数
Patroni 依赖 etcd 做选主和状态同步,它不是“可选组件”,而是强制前置条件。三节点是最小安全配置,两节点在 etcd 自身故障时会卡死——因为 Raft 要求多数派(quorum),2 节点中只要挂 1 个,剩下 1 个无法达成 2/2 多数,整个集群失能。
- 所有 etcd 节点都要开
listen-client-urls和advertise-client-urls,地址必须是其他 Patroni 节点能直连的 IP,别写127.0.0.1 -
initial-cluster字符串里每个节点名必须和对应节点的ETCD_NAME完全一致,大小写、下划线都不能错 - 启动顺序:先启所有 etcd 节点,再用
etcdctl endpoint health确认全部healthy,最后才起 Patroni - 常见错误现象:
patronictl -c patroni.yml list显示空列表或报Connection refused,大概率是 Patroni 的etcd.hosts指向了未监听的端口,或防火墙拦了 2379
Patroni 配置里最关键的三个布尔开关
很多故障转移失败,不是因为 etcd 没通,而是这几个布尔值设错了。它们控制 Patroni 是否敢升主、是否敢重放 WAL、是否允许你手动切:
-
postgresql.use_pg_rewind: true—— 必开。主库异常下线后,如果 WAL 位置落后太多,备库升主前必须用pg_rewind回退数据目录,否则会脑裂。关了它,Patroni 在检测到复制延迟大时直接拒绝升主 -
tags.nofailover: false—— 必须为false。设成true就等于给这个节点贴了“禁止升主”标签,哪怕它是唯一存活节点也不会被选 -
bootstrap.dcs.ttl: 30和bootstrap.dcs.retry_timeout: 10—— 这俩要满足retry_timeout * 2 ,否则 Patroni 会因心跳超时频繁失联,误判为故障
PostgreSQL 17 的 pg_hba.conf 和 recovery.signal 要彻底删干净
Patroni 启动时会自己生成 recovery.signal 或删掉它,也会覆盖 pg_hba.conf。如果你手动生成过 standby,残留的文件会导致 Patroni 初始化失败,日志里出现 cluster has no members 或 could not open file "recovery.signal"。
- 初始化前,进到
data_dir目录,手动删掉:recovery.signal、standby.signal、postmaster.pid、pg_wal/archive_status/下所有文件 -
pg_hba.conf里别留任何硬编码的replication条目,Patroni 会在运行时动态注入,冲突会导致连接拒绝 - 确保
postgresql.bin_dir指向的是 PostgreSQL 17 的真实路径,比如/usr/lib/postgresql/17/bin,而不是 14 或 15 的旧路径——版本错配会导致pg_ctl promote失败
验证自动故障转移,别只看 patronictl list
patronictl list 显示新 Leader 是 running,只是表面正常。真正要确认是否生效,得查三件事:
- 新主库的
pg_is_in_recovery()返回false,老主库(恢复后)返回true,且 WAL 接收进程在跑 - 用
SELECT * FROM pg_stat_replication;在新主上查,应看到至少一个state = 'streaming'的连接 - 客户端连 HAProxy/VIP 写入一条带时间戳的数据,停掉当前 Leader,等 30 秒,再查这条数据是否已同步到新主——别只查 select,要测写入链路
- 最容易被忽略的一点:
postgresql.parameters.max_connections在 Patroni 配置里改了,但 PostgreSQL 进程没 reload,实际生效值还是旧的。每次改完记得patronictl reload或重启 Patroni











