问题现象:3节点的Patroni + PostgreSQL集群 postgresql-0 节点异常,一直处于starting状态,Lag in MB 显示 unknown,postgresql-0使用的pv、etcd集群排查都没有问题,集群状态及日志报错如下图所示,输出排查步骤及恢复方案
基于您提供的截图和描述,这是一个典型的 Patroni + PostgreSQL 集群中备库(Replica)启动失败/卡住 的问题。
1. 现象分析
状态异常:
postgresql-0节点在patronictl list中显示为starting,且Lag in MB为unknown。这意味着 Patroni 无法成功连接到该节点的 PostgreSQL 进程来查询复制延迟。日志关键信息(第二张图):
Still starting up as a standby.:Patroni 认为 PG 正在作为备库启动。Error communicating with PostgreSQL... /var/run/postgresql:5432 - rejecting connections:这是核心错误。PostgreSQL 进程虽然可能启动了,但它处于“拒绝连接”的状态。通常发生在 PG 正在进行崩溃恢复(Crash Recovery)、正在重放 WAL 日志尚未达到一致性状态,或者启动过程中卡住了。Failed to determine PostgreSQL state from the connection:因为连不上数据库,Patroni 无法判断其真实状态,只能回退到缓存的角色信息。拓扑结构:
postgresql-2是 Leader (TL 266),postgresql-1是健康的 Replica (TL 135, Lag 752MB),postgresql-0是故障节点。手动连接报错:
FATAL: the database system is starting up。这证实了 PostgreSQL 进程确实活着,但它还没有完成启动/恢复过程,因此拒绝任何业务连接。这也是为什么 Patroni 报rejecting connections和Lag in MB: unknown的根本原因。进程状态 (
ps -ef):PID 61 是主进程
postgres -D ...。关键进程 PID 64:
postgresql: startup recovering 00000008E000000000000077。结论:您的
postgresql-0节点当前正处于 WAL 日志回放(Crash Recovery / Standby Recovery)阶段。它正在努力应用 WAL 日志以达到一致性状态。在startup进程完成之前,数据库无法接受连接,Patroni 也就只能一直显示starting。
2. 为什么会卡在这里?
结合您第一张图的信息:
集群整体延迟大:健康的备库
postgresql-1也有 752 MB 的延迟。这说明主库写入量很大,或者网络/磁盘 IO 存在瓶颈。Timeline (TL) 差异巨大:Leader (
postgresql-2) 的 TL 是 266,而postgresql-1的 TL 是 135。这说明集群历史上发生过非常多次的故障切换(Failover)。当前恢复位置:
postgresql-0正在恢复的 WAL 段是00000008E000000000000077(注意前缀00000008代表 Timeline 8)。
核心矛盾:postgresql-0 本地的数据还停留在非常古老的 Timeline 8,而当前的 Leader 已经在 Timeline 266 上了。
PostgreSQL 备库在启动时,必须先从本地 WAL 或归档中把历史日志回放完,直到追上当前的 Timeline。由于跨度太大(从 8 到 266),它可能需要回放海量的 WAL 日志,或者正在尝试寻找跨越这些 Timeline 的历史记录,导致启动过程极其漫长,看起来像是“卡住”了。
3. 排错步骤
请按顺序在 postgresql-0 (172.24.0.107) 节点上执行以下操作:
第一步:检查 PostgreSQL 原生日志(最重要)
Patroni 的日志只告诉我们“连不上”,PG 自己的日志才会告诉我们“为什么连不上”。
# 找到 PG 日志路径,通常在 /var/log/postgresql/ 或数据目录下的 pg_log/ # 如果是容器化部署,使用 kubectl logs 或 docker logs tail -f /var/log/postgresql/postgresql-*.log # 或者 tail -f /home/postgres/data/pg_log/postgresql-*.log
重点关注日志中的以下关键词:
redo starts at .../redo done:看是否在进行恢复。如果一直卡在redo starts且没有进度,说明 WAL 回放有问题。could not connect to the primary server:看是否能连通 Leader (172.24.0.116)。FATAL或PANIC:任何致命错误。waiting for WAL to become available:等待 WAL 日志。
第二步:检查进程状态
确认 PG 进程是否存在,以及处于什么状态。
ps -ef | grep postgres
如果没有
postgres进程:说明 PG 根本没起来,或者是被 Patroni 反复重启。如果有进程,但状态是
D(不可中断睡眠) 或R(运行中) 且 CPU 占用高:可能正在大量回放 WAL。检查是否有残留的
postmaster.pid:
ls -l /home/postgres/data/postmaster.pid # 路径根据实际情况调整
注意:不要随意删除此文件,除非你确定 PG 进程已经完全不存在。
第三步:手动尝试连接(绕过 Patroni)
尝试直接用 pg_isready 或 psql 连接本地 socket,看具体报错。
# 检查端口是否监听 netstat -tlnp | grep 5432 # 尝试本地连接 su - postgres psql -h /var/run/postgresql -p 5432 -U postgres -c "select 1;"
如果报错
the database system is starting up:证实了日志中的猜测,PG 还在恢复中,需要等待。如果报错
no pg_hba.conf entry:配置问题。如果连接直接被拒绝且无进程:PG 启动失败。
第四步:检查网络连通性
确保 postgresql-0 能访问 Leader (postgresql-2, 172.24.0.116) 的 5432 端口。
telnet 172.24.0.116 5432 # 或 nc -zv 172.24.0.116 5432
如果不通,检查防火墙、安全组或 K8s NetworkPolicy。
4. 解决方案
根据上述排查结果,选择对应的方案:
方案 A:如果只是 WAL 回放慢(最常见)
如果 PG 日志显示正在 redo 且没有报错,只是速度慢(特别是看到 pg-1 也有 700多MB 延迟,说明集群整体写入压力大或网络带宽受限):
耐心等待:不要强制重启。让 PG 完成恢复。
监控进度:观察日志中 LSN 的变化。
优化:如果长期如此,考虑增加
max_wal_senders,调整wal_receiver_status_interval,或检查磁盘 IO 性能。
方案 B:如果 PG 进程卡死或数据损坏(推荐尝试)
如果日志显示 PANIC,或者长时间(超过 30 分钟)无任何进展,且 psql 始终报 starting up,则需要重建该副本。由于是 Patroni 集群,重建非常安全。
处理方案
针对这种情况,有两种处理策略。强烈建议直接采用方案 B,因为等待一个跨越 200 多个 Timeline 的备库自行追平几乎是不现实的,且极易出错。
方案 A:继续等待(仅适用于刚重启不久)
如果您刚刚重启该节点不到 10-20 分钟,可以观察一下 PostgreSQL 的原生日志,看 LSN 是否在跳动。
# 查看 PG 日志,确认是否在持续回放 tail -f /home/postgres/pgdata/pgroot/data/log/*.log # (路径根据您的实际 pg_log 位置调整)
如果日志里不断有 redo at ... 且数字在变大,说明它在干活。但考虑到 TL 差距,不建议死等。
方案 B:重建该副本(推荐,最快最安全)
既然 PV 和 etcd 都没问题,利用 Patroni 的自动克隆功能,让它直接从当前的 Leader (postgresql-2) 重新全量同步一份最新的数据,是解决此问题的标准做法。
操作步骤:
1.停止 Patroni 服务
为了防止 Patroni 在我们清理数据时反复尝试拉起 PG,先停掉它。
# 如果是 systemd 管理 systemctl stop patroni # 如果是容器环境 (如 K8s/Docker),请通过编排工具停止该 Pod/Container
2.确认并清理数据目录
从截图中可以看到,您的数据目录是:/home/postgres/pgdata/pgroot/data。
警告:请务必核对路径,删除错误目录会导致灾难性后果!
# 再次确认路径 ls -ld /home/postgres/pgdata/pgroot/data # 清空该目录下的所有内容 (保留目录本身) rm -rf /home/postgres/pgdata/pgroot/data/* # 确认已清空 ls -A /home/postgres/pgdata/pgroot/data
3.重新启动 Patroni
systemctl start patroni # 或启动对应的容器/Pod
4.观察重建进度
Patroni 启动后,会发现数据目录为空,自动触发 pg_basebackup 从 Leader (172.24.0.116) 克隆数据。
查看 Patroni 日志:
tail -f /var/log/patroni/patroni.log # 或 journalctl -u patroni -f
您应该会看到类似 replicating from leader、running pg_basebackup 的日志。
查看集群状态:
patronictl list
状态变化预期:
starting(正在克隆)running(克隆完成,开始流复制)Lag in MB会从unknown变成一个具体的数字,并逐渐减小到 0 或很小的值。TL(Timeline) 会变成 266,与 Leader 一致。
总结
您遇到的不是故障,而是备库数据太旧,正在艰难地进行跨 Timeline 恢复。由于落后太多(TL 8 vs TL 266),自行恢复效率极低。清空数据目录让 Patroni 重新克隆是解决此问题的最佳实践。



