跳至内容

32.3 隔离恢复策略

隔离恢复的目标不是“找一台空机器”,而是让恢复候选同时满足:

cannot overwrite source evidence
cannot join source HA control plane
cannot receive application traffic
cannot emit unintended external effects
cannot contaminate source WAL archive
can still read the exact backup/WAL it needs
can be identified, validated, stopped and deleted exactly

这六条不是同一个开关。listen_addresses='' 能关闭 TCP 入站,不会自动阻止逻辑订阅、 HTTP 扩展或脚本向外连接;custom PGDATA 不接入 Patroni,也不会自动得到只读 repository 凭据。隔离要逐层证明。

32.3.1 不覆盖仍可取证的原集群

原集群同时是现场、差异源和回退资产

即使原数据已经错误,它仍保存:

  • 错误之后的合法提交;
  • 当前业务 key、version 与外部事件身份;
  • 未归档 WAL 和最近 commit 线索;
  • session、job、slot、subscription 与路由状态;
  • 与恢复候选进行差异比较的基准;
  • 如果目标判断错误,重新选择候选所需的证据。

因此默认拓扑应是:

live source --------------------------> preserved
    |                                      |
    +--> backup/WAL --> candidate A        +--> good-after audit
                   \--> candidate B        +--> current-state manifest

而不是:

live source PGDATA --overwrite--> guessed target

PostgreSQL 官方恢复流程也建议在空间允许时先复制整个 cluster data directory 与 tablespace;至少保存可能尚未归档的 pg_walRecovering Using a Continuous Archive Backup)。 对仍在线的 source,不应把这理解成随意复制活动 PGDATA;应通过受支持的备份、存储快照 或停机证据流程完成。

先决定 source 的运行状态

三种常见状态:

source 状态何时选择必须控制
继续服务影响局部,可围住错误对象bad writer、受影响 key、事后写审计
只读/全局写围栏影响广,good-after 难追踪所有入口、后台作业、长事务
停止并保全完整性未知或仍快速恶化WAL、内存外证据、启动权限

“source 继续服务”不能写成“什么都不做”。至少要冻结错误 job/service identity,记录围栏 时间,保存合法写 manifest。反之,也不要因为要做 PITR 就自动停全站;停写是业务可用性 决策,必须由影响范围与对账能力支撑。

保存事实,不保存秘密扩散

事故证据包可以记录:

cluster and member identity
system identifier relation or protected digest
timeline and LSN
backup label and archive range
effective recovery parameters
object/row manifests and business-key digests
UTC action timeline
source-file and evidence hashes

不应把这些内容直接放进公开工单:

SCRAM verifiers
TLS private keys
cloud/repository credentials
raw connection URIs
unredacted business rows
unbounded query text or bind values

私有证据目录需要最小权限、保留期限与访问日志;公开摘要只保留足以复核结论的环境轮廓、 计数、状态和散列。

任何覆盖动作都要单独授权

managed PGDATA restore 至少会:

  1. 停止或绕开 HA 生命周期;
  2. 替换当前物理数据;
  3. 改变 timeline 与可继续恢复的路径;
  4. 使旧节点与 DCS/其他成员产生身份冲突风险;
  5. 让回到当前状态依赖另一条恢复路径。

它是独立的高风险状态迁移,不应因为 side restore 验证通过就自动获批。本章实验从不 执行 managed restore,也不把 side candidate 注册为 Patroni member。

32.3.2 选择备份、WAL 与目标环境

备份必须早于目标并能走到目标

对候选 backup $B_i$,最低条件是:

$$ \operatorname{stop}(B_i) < T_{\text{target}} $$

并且从 backup 一致点到 target 的所有必需 WAL 与 timeline history 都可读。选择最近的 合格 backup 通常减少重放量,但不能只按 label 字符串或“最新”按钮决定:

stanza identity matches source
database system lineage matches
PostgreSQL major and block/checksum properties compatible
backup status has no error
full/diff/incr dependencies all retained
backup stop point is before target
archive min/max and history cover target timeline
tablespace mapping is available
repository key and credentials are usable

本章正式实验在 fixture base 创建后新做一份 full backup,再提交 safe、damage 与 post-target 三笔事务;这样既测试 backup 后 WAL 重放,也避免选到目标之后才完成的 backup。

“archive max 大于目标”只是必要条件

WAL segment 名的范围比较可以证明仓库至少走到某个 segment,却不能单独证明:

  • 中间每一个 segment 都存在;
  • 对象内容未损坏;
  • 所需 timeline history 存在;
  • repository key 可用;
  • restore command 权限和网络可用;
  • 目标记录确实在宣称的 segment 中。

所以先运行 repository check,再实际恢复。测试环境可以主动 pg_switch_wal() 缩短等待, 生产不能把频繁 switch 当作归档性能修复;archive_timeout 太短会生成大量未填满但仍为 完整尺寸的 segment。

固定 exact backup,而不是让工具临场猜

恢复票据应保存:

stanza             pg-test
repo               1
backup label       20260730-030910F
backup type        full
backup stop        timestamp + LSN + WAL
target             XID/time/LSN/name
inclusive          true/false
timeline           current/latest/N + rationale
target action      pause/promote/shutdown

运行 pig pitr 时使用 -b/--set 指定这份 backup。若 plan 解析出的 effective label、 target、timeline 或 data directory 与票据不一致,应在 restore 前阻断。

目标环境要匹配物理要求

物理恢复不是把 data directory 交给任意 PostgreSQL:

  • server major 必须与物理格式匹配;
  • 所需 extension shared libraries、locale/collation provider 要可用;
  • data checksums、page/block/WAL segment 属性要被理解;
  • tablespace 目标必须存在、映射正确且容量足够;
  • 恢复关键参数不能低于源端需要;
  • OS 用户、owner、mode 与文件系统语义要正确。

恢复关键参数常包括:

max_connections
max_worker_processes
max_wal_senders
max_prepared_transactions
max_locks_per_transaction

如果恢复主机当前配置比 backup 要求低,recovery 可能拒绝启动。正式实验读取 source effective settings,再显式传给隔离 postmaster;这不是把整个 source 配置无审查复制过来。

先算空间,再决定保留策略

至少预算:

$$ \text{space} \ge \text{restored PGDATA}

  • \text{tablespaces}
  • \text{temporary WAL}
  • \text{export/intermediate data}
  • \text{safety margin} $$

若同时恢复 inclusive 与 exclusive,可顺序复用主机端口和空间,但应保留各自 manifest 与日志;不要同时启动两个继承相同 identity、port 和外部配置的副本。

空间不足时,禁止以 expire 旧 backup 或删除 archive WAL 作为脚本的隐式副作用。本章 实验会留下新 full backup,因为清理 repository 不在授权范围内。

32.3.3 控制网络、凭据和外部副作用

六层隔离清单

本章正式实验真实生产演练还要考虑
文件随机 marker 下的 custom -D独立卷、tablespace、快照权限
进程手工 pg_ctl,一次只启一个cgroup/container/systemd identity
入站listen_addresses='',mode-0700 Unix sockethost firewall/security group
身份private HBA,仅本机 postgres peersecrets replacement、least privilege
控制面不加入 Patroni/DCS/HAProxy/PgBouncerDNS/VIP/controller admission
出站/副作用fixture 没有 dispatcher,archive offegress deny、subscription/job/webhook 隔离

最后一行最容易被漏掉。listen_addresses='' 只是不接受 TCP 连接,不会阻止数据库或宿主 进程主动向外连接。真正的取证环境应在网络层默认 deny egress,再为只读 backup/WAL 读取开精确 allowlist。

恢复出来的凭据仍然有效

物理 backup 包含当时的:

  • database roles 与 password verifier;
  • pg_hba.conf、证书引用与部分服务配置;
  • extension、job、subscription 和 FDW metadata;
  • application-owned secret(若错误地存进业务表)。

因此不能让恢复实例监听原端口或加载原 HBA。本章在 command line 覆盖:

listen_addresses=''
unix_socket_directories=<private-root>/socket
unix_socket_permissions=0700
hba_file=<private-root>/pg_hba.restore.conf
ssl=off
shared_preload_libraries=''
primary_conninfo=''
primary_slot_name=''
archive_mode=off

private HBA:

local all postgres peer
local all all      reject

这些设置适合本章沙箱,不是所有生产恢复的通用模板。例如需要业务验证账号时,应新建 一次性、最小权限的验证入口,而不是重新开放原应用角色。

archive_mode=off 防止污染 source 历史

隔离 candidate promote 后会产生新 timeline 和新 WAL。若它继承 archive_mode=always/on 与 source repository 写凭据,实验分支可能写入共享 archive, 增加 timeline 选择歧义,甚至与 retention 发生交互。

本章通过 pgBackRest restore 参数和 postmaster command line 双重确认 archive_mode=off。这不影响 restore_command 在 recovery 期间读取所需 WAL; 它只禁止候选成为新的 archive producer。

更严格的设计还应让恢复凭据 repository read-only。因为具备 restore 权限的 host 往往 也配置了 backup 写权限,单靠操作人员承诺不是权限隔离。

控制数据库内的自动执行器

恢复实例中可能存在:

pg_cron / pgAgent jobs
logical subscriptions
background worker extensions
LISTEN/NOTIFY consumers
outbox relay
trigger-driven HTTP calls
foreign data wrappers
application sidecars and local service units

应在启动前列出它们,并从三层阻断:

  1. 网络层:默认拒绝出站;
  2. 进程层:不启动 application/relay/agent,按评审禁用 background workers;
  3. 数据层:把 outbox/queue 置隔离状态,使用幂等键和 sandbox endpoint。

本章只证明 synthetic fixture 的 external_dispatch_count=0,没有宣称通用 egress 隔离 已经通过。读者把实验迁移到真实系统时,必须补这一门。

Pigsty 的 managed 与 side restore 边界

当前 pig pitr 有两种生命周期:

managed data directory
  may stop Patroni
  ensures PostgreSQL is stopped
  restores managed PGDATA
  may start PostgreSQL
  leaves Patroni stopped
  does not rejoin HA or switch traffic

custom -D side restore
  requires pre-created postgres-owned directory
  requires --no-restart
  does not stop Patroni
  does not manage default PostgreSQL service
  operator starts isolated postmaster manually

完整语义见 pig pitr。这里最重要的不是记选项,而是从 plan 核对实际 boundary。路径是否为 side restore 由有效 managed PGDATA 与解析后的路径决定, 不是简单看字符串里有没有 /pg/data

正式实验先执行:

pig pitr \
  -s pg-test -r 1 \
  -b "$BACKUP_LABEL" \
  --xid "$DAMAGE_XID" \
  --target-action=promote \
  --target-timeline=current \
  -D "$CANDIDATE/data" \
  --no-restart \
  --plan \
  -o json \
  -- \
  --archive-mode=off

plan 必须明确:

boundary       pitr:side-restore
data directory exact candidate path
service lifecycle not-managed
backup set     exact reviewed label
target         exact source-audited XID
timeline       current
confirmation   required

结构化执行不会交互询问;只有审批完成后才使用 --yes。不要把 --yes 写进没有 guard、 没有 exact target、会指向 managed PGDATA 的通用脚本。

隔离验收

启动候选后,同时从 OS 与 SQL 两侧检查:

OS:
  no TCP listener on candidate port
  private socket exists and mode is 0700
  postmaster.pid belongs to exact candidate
  managed Patroni process remains unchanged

SQL:
  cluster_name identifies candidate
  listen_addresses is empty
  archive_mode is off
  ssl is off for private socket-only lab
  shared_preload_libraries is empty in this fixture
  pg_is_in_recovery() eventually becomes false

停止时只允许:

pg_ctl -D <exact-marker-root>/data stop
verify PID and socket absent
delete <exact-marker-root>

宽泛 pkill postgres、按端口杀进程、删除未解析变量路径都不属于可接受清理。


上一节:恢复目标与时间线 · 返回本章目录 · 下一节:执行恢复并观察进度 · 查看全书目录 · 查看索引中心

最后更新于