Patroni 集群恢复
本页是 Patroni 集群备份 的恢复侧姊妹页。集群一旦有了带 WAL
归档的 stanza(pg backup setup + pg ha snapshot),pg ha restore 就能把整个集群
回到某个时间点——它是单实例 pg restore 的 Patroni 集群对应物。本页
讲清机制(为什么集群 PITR 不是简单跑一次 pgbackrest restore)、关于 leader 所在主机
的安全性预检、端到端操作流程,以及恢复之后必须做的重新基线步骤。命令 / flag 细节同时见
恢复 → Patroni 集群;本页把它们串成一条可照着跑的流程。
前提:本机要有带 WAL 归档的 stanza
PITR 只能重放到已经归档的部分。恢复前,集群必须已经具备:
- 针对 S3 仓库预置好的集群 stanza——
pg backup setup --s3-endpoint ...(见 备份 与 HA 备份); - 至少一个全量快照,加上覆盖目标时间点的 WAL 段
(用
pg ha snapshot create <scope> --type full产生)。
目标时间点被这段历史框住:早于最新全量备份的 stop 时间、或晚于最后归档的 WAL,恢复 都落不到那里。
pg backup setup 必须在本机跑过,且 backup 容器要在运行。 对跨主机集群而言,
这条比听起来更严格:
- 承载 bootstrap 的成员从 S3 恢复,靠的是挂进它容器里的仓库配置——
pgbackrest-archive.conf挂到/etc/pgbackrest.conf,外加 S3 CA 挂到/etc/pgbackrest/ca.crt。两者都由pg backup setup生成,且容器只在文件存在时才挂载。 从没跑过 setup 的主机没有仓库配置可挂,pgbackrest restore的 bootstrap 就连不上 S3、 起不来。 - leader 本机性与 stop 时间的预检,都是在 backup 容器内跑
pgbackrest ... info。pg ha restore只会 best-effort 刷新共享的pgbackrest.conf,不会生成成员的 archive 配置,也不会启动 backup 容器——所以要先把容器拉起来(pg backup setup, 或用pg backup status确认它是Up)。backup 容器没起时,预检会被跳过,真正的失败 要拖到 bootstrap 阶段才暴露;而恢复之后的新快照本来就依赖它在运行。
一句话:在已用 pg backup setup 预置好 S3 仓库、且 backup 容器为 Up 的主机上
运行 pg ha restore。
机制:自定义 bootstrap PITR,而非裸跑 pgbackrest restore
Patroni 集群不能在存活的 postmaster 下直接 pgbackrest restore 来做 PITR——数据目录、
时间线、DCS 身份都归 Patroni 管。pg ha restore 走的是 Patroni 的自定义 bootstrap
配方:
-
先 pause 集群(
patronictl pause)。在任何东西被停止之前先冻结 failover:集群存活时停掉本机 leader,会让 Patroni 把一台本机停不掉的远端 副本 promote 成新 leader,那个远端 leader 随后会抢着在旧时间线上重新夺回 DCS——从破坏路径内部瓦解了 leader 本机性预检。pause 正是为了堵住这一点。 若上一次尝试已经把集群拆掉(DCS 已清、成员已停),重跑的 restore 没有可 pause 的活集群;那本就是我们要的"已冻结"状态,因此空 DCS 下的 pause 失败被容忍, 流程继续。 -
清除集群的 DCS 身份(
patronictl remove)。Patroni 的bootstrap.dcs只在 DCS 没有 config key 时才执行,所以移除它正是重新武装 bootstrap 路径的动作。 -
选一个本机成员,改写它的
patroni.yml,把默认的 initdb bootstrap 替换成一条指向 目标时间点的pgbackrest restoremethod:no_params: true阻止 Patroni 追加--scope/--datadir(pgbackrest会以[031] invalid option拒绝它们);keep_existing_recovery_conf: true保留pgbackrest restore自己写下的recovery.signal+restore_command+recovery_target_*。method/pgbackrest这两个键是bootstrap.dcs的兄弟,绝不嵌进它里面——恢复相关的 GUC 若泄漏进 DCS 配置,会在 promote 后的 leader 上作为运行时参数生效。 -
清空该成员的 PGDATA 并重建其容器。Patroni 面对空数据目录 + 无 DCS 配置,跑该 method,在新时间线上恢复到目标点,并借
--target-action=promote把自己 promote 成可写 leader。 -
其余成员重新加入新 leader:本机其它成员被清空并作为标准副本重启(对新 leader 做
pg_basebackup);跨主机成员经 DCS 重新加入(或被 reinit)。
leader 本机性预检
除非当前 leader 是本机的一个成员,否则 pg ha restore 拒绝执行。 动手之前它先从 DCS
读 leader,判断它是不是本机可控成员之一。
原因。 第 1 步清掉 DCS、第 3 步让一个本机成员在新时间线上重新 bootstrap。活在另一台 主机上的 leader 其 Patroni daemon 一直在跑——本机停不掉它——所以 DCS key 一旦被移除,那个 远端 leader 会抢着在旧时间线上重新夺回 leadership,与本机 bootstrap 相撞。要求 leadership 在本机(从而可被停止)就消除了这个竞态。
你会看到什么。
怎么办。 把 leadership 挪到你正站着的这台主机的某个成员上,再重试:
或者改到 leader 自己所在的主机上跑 pg ha restore。leader 为空 / 读不到时(例如最早
的 bootstrap、还没有任何成员 publish 自己)不阻断执行。
与单实例 pg restore 的差异
- 总是 promote。 没有"只读挂起、检查、再换个时间重试"的两步——bootstrap 恢复以一个
新时间线上的可写 leader 收场。执行前先用
--dry-run确认目标时间。(机制第 1 步的patronictl pause与此无关:它是在恢复期间冻结 failover,不是只读检查窗口;而且 DCS 被清除后由新 bootstrap 重新发布配置,其中没有 pause 键,集群自动处于未暂停状态。) - 没有
--promoteflag——promote 是自动的(它烧进了 bootstrap 命令里)。 - 必须在拥有成员的主机上运行,且 leader 必须是本机的(见上文预检)。
操作流程:把集群恢复到某个时间点
完整 flag 集:--time(必填,格式见恢复)、
--member、--dry-run、--tail-logs、--force。
1. 用 dry-run 确认目标安全。 什么都不碰;你能看到 stanza、选中的 bootstrap 成员、确切的
pgbackrest 命令,以及(若相关)leader 本机性告警。
2. 确保 leader 在本机(仅当 dry-run 告警时)。switchover,然后复查状态。
3. 恢复。 默认会弹确认提示,列出 scope、stanza、目标时间、bootstrap 成员,以及
“PERMANENTLY LOST” 警告。用 --tail-logs 流式输出恢复日志;用 --force 跳过提示以便
自动化。
4. 确认新集群起来。 waitForLeader 轮询 DCS 直到出现 leader(上限 15 分钟),随后
重新加入的成员作为副本启动。
恢复之后:给新时间线重新基线
恢复不是最后一步。数据目录被重建过,所以在集群重新具备备份能力之前,有两件收尾必须做:
-
建一个新的全量快照,让后续 PITR 在新时间线上有基点:
这步通常直接成功:同一 stanza 的 pgBackRest 恢复在时间线切换时保持 PostgreSQL 的 system-id 不变(实测验证),所以恢复后的首个快照不会撞上
[051] system-id ... do not match stanza。集群恢复之后不需要pg backup stanza-upgrade。(真正会改 system-id 的是往同一个 stanza 里重新 initdb——比如全新集群复用了旧 stanza 名——那才是[051]和 stanza-upgrade 的适用场景。) -
拉回任何没有自动重新加入的成员——通常是丢了旧 leader 的跨主机成员。从新 leader 重新初始化它(只破坏该副本的数据目录):
(完整细节见 HA 备份 的"卡住的副本 → reinit"一节。)
故障排查
- “no local member on this host”——你在一台不拥有该 scope 任何成员的主机上运行。
pg ha restore只能重建本机的数据目录 + 容器。到拥有成员的主机、或 leader 所在主机上运行。 - “target time is before the latest backup stop time”——最早可用点是最新全量备份的 stop
时间;报错会打印它并给出建议的
--time。若需要更晚的基点,先做一个更新的全量快照。 - 恢复超出最后归档的 WAL——不能恢复到没归档过的时间点。调早目标时间,或先确保
archive_command在正常投递(pg backup status)再重试。 - 恢复后首个快照报
[051]——按新结论不该出现:同 stanza 恢复会保持 system-id 不变(见"重新基线"一节)。万一真撞上,pg backup stanza-upgrade能非破坏性地修好。 - 对同一仓库重复做 PITR 时报
FATAL: recovery ended before configured recovery target was reached——stanza 里已经住着一条此前 promote 出来的时间线,默认的recovery_target_timeline=latest会跳到那条分支上,而它在目标时间点之前没有提交。 恢复时显式钉住较旧的时间线,即在成员patroni.yml的 bootstrappgbackrest ... restore命令上加--recovery-option=recovery_target_timeline=<旧TL>。 目标时间点不跨越已 promote 分支的首次 PITR 不会遇到这个。