跳转到主要内容

19 开天辟地:环境规划与部署基线

上卷回答了 PostgreSQL 能做什么;下卷从一个更苛刻的问题开始:

我们准备把什么服务,交付给谁,承诺到什么程度;声明的版本、主机、 初始化与拓扑,是否真的存在?

本章先写需求与资源合同,再冻结 PostgreSQL 初始化选择,最后用 exact Pigsty v4.5.0 在四台 Linux VM 上完成 PostgreSQL 18 部署和 L2 沙箱验收。正式 结果是“带六项例外的沙箱通过”,不是生产批准。

本章目标

读完并完成实验后,你应当能够:

  1. 从业务损失、数据分类、owner、workload 与 RPO/RTO 写服务需求;
  2. 将 CPU、memory、storage、network 与可观察饱和/故障条件连接;
  3. 建立 OS baseline,而不是照抄 sysctl 模板;
  4. 冻结并验证 locale/provider、encoding、checksum、page/WAL、auth 等 PostgreSQL 初始化契约;
  5. 严格区分 node、instance、database cluster、HA cluster、database、 service、pool 与 DCS;
  6. 用 secret-safe Pigsty inventory 声明两个 service unit;
  7. 从 inventory、host、SQL、Patroni/service 四面交叉验收;
  8. 区分 sandbox acceptance、exception 与 production gate;
  9. 为 destructive reset 建立显式 guard,而不把它混入正常检查。

前置与后续

前置:

后续:

  • 第 20 章 高可用拓扑与容灾目标 使用本章保留 baseline 验证 election、fencing、RTO 与数据损失边界;
  • 第 21 章验证 backup/restore;
  • 第 22 章验证 service routing、pooling 与 client semantics;
  • 后续容量、安全、变更和升级 gate 不由本章安装结果代替。

学习路径

service requirement
    -> resource/failure model
        -> host observed baseline
            -> irreversible PostgreSQL initialization contract
                -> topology and stable identity
                    -> secret-safe Pigsty declaration
                        -> live four-plane acceptance
                            -> exceptions + next gates

前三节解决“为什么、需要什么、机器是什么”;19.4–19.6 解决“哪些选择必须 提前冻结、如何声明”;19.7 只读验证声明与事实是否一致。

正式实验结果

target               pg36-l2-vagrant
Pigsty               v4.5.0 exact tag
PostgreSQL           18.6 observed
hosts                4 distinct Ubuntu 24.04/aarch64 guests
service units        pg-meta + pg-test
pg-test              one primary + two streaming replicas
normal validation    passed
negative tests       9/9 rejected as designed
sandbox L2           accepted-with-exceptions
production ch19      pending
mutation by lab      none
reset                not executed

六项例外:

shared hypervisor/power/storage
single etcd
single local backup target
unqualified virtual storage
temporary inventory-based secret handling
three pg-test guests below recommended 2-vCPU/2-GiB floor

这些例外不是脚注;它们逐项阻止 failure-domain、DCS、DR、durability、 secret lifecycle 与 capacity 的生产结论。

本章目录

19.1 先写服务需求

19.2 计算、内存、存储与网络

19.3 操作系统与主机基线

19.4 版本与数据库初始化契约

19.5 拓扑、命名与故障域

19.6 用声明式清单交付两个服务单元

19.7 实战:L2 部署验收

实验入口

正常 all 是只读验收,不包含 deploy、failover、restore 或 reset。

本章最重要的判断

declaration != observation
playbook success != service acceptance
node count != independent failure domains
replica exists != RPO achieved
port open != routing contract
SSL on != transport security complete
sandbox passed != production approved

如果只记住一个方法,就记住:

同一事实必须由合适的 authority 证明;差异必须被拒绝或登记为有范围的 exception,不能被一句“看起来正常”吞掉。


上一章:万法归宗:PostgreSQL 数据平台与替代边界 · 返回下卷导读 · 下一章:狡兔三窟:高可用拓扑与容灾目标 · 查看全书目录 · 查看索引中心

19.1 先写服务需求

部署的第一行不应是:

pg_version: 18

而应是:

这个服务为何存在?
什么数据不能丢?
什么操作不能错?
谁承担结果?
失败多久开始造成不可接受损失?

如果这些问题没有答案,CPU、节点数、同步复制和备份频率都只能靠猜。

19.1.1 业务重要性、数据分类与所有者

先定义业务能力

pg36_shop 提供的不是“一个 database”,而是:

商品浏览与检索
客户身份与地址
订单创建和状态转换
库存与支付事实
配送时空事件
分析与外部投影源

不同能力的故障损失不同:

能力 失败影响 可接受降级
创建订单 直接收入/履约风险 拒绝比重复创建安全
查询支付状态 财务与客服风险 不可用旧缓存猜测
商品描述 转化下降 可短时有标签陈旧
搜索排序 发现能力下降 可回退简单检索
分析报表 决策延迟 可显示 last watermark
图片读取 体验下降 placeholder/重试

一个统一 database 可以承载它们,但 service objective 不能只写一个“重要”。

业务重要性需要损失模型

常用分级:

tier 0 / critical
  停止即产生重大安全、财务或合规损失

tier 1 / important
  核心业务中断,短时可人工/降级

tier 2 / standard
  影响明显,但可在较长窗口恢复

tier 3 / development
  无生产承诺,可重建

分级必须连接动作:

critical standard development
owner/on-call 24×7 明确 支持窗口明确 工作时间
HA 多故障域并演练 按目标设计 可无
backup 跨域、频繁演练 标准恢复 可选且声明
change 强审批/回退 标准流程 自助边界
capacity 高 headroom 正常 headroom best effort
incident 快速升级 标准升级 issue

若分级只改变标签颜色,就没有价值。

数据分类决定安全与恢复边界

分类至少包含:

public
internal
confidential
restricted / regulated
credentials and cryptographic material

对每类记录:

  • 数据 owner;
  • 合法用途;
  • 可访问角色;
  • 是否可复制到 dev/test;
  • 加密与审计要求;
  • 地域限制;
  • retention;
  • 删除时限;
  • backup 中如何处理;
  • incident 通知义务。

本章正式 sandbox 只允许:

synthetic teaching data
production_data_permitted=false
production_traffic_permitted=false

这是硬边界。VM 看起来像生产拓扑,也不能把生产数据“临时导入测试”。

数据 owner 与平台 owner 不同

建议至少区分:

角色 责任
business/data owner 数据用途、正确性、分类、保留、损失
service owner SLO、错误预算、依赖、发布、事件
database platform PostgreSQL/Pigsty、HA、备份、接入、容量
security/privacy 威胁、控制、审计、合规
application team schema/SQL、连接、重试、兼容、流量
incident commander 事件期协调和决策

一个人可以兼任,责任不能消失。

owner 要能作决定

在事故中,DBA 可以说:

“当前恢复到 10:31 会丢 47 秒提交,
 恢复到最新可能保留错误写入。”

但通常不能独自决定哪种业务损失更小。需要提前指定有权选择:

availability versus consistency
restore point
degraded operation
data deletion
customer communication
error-budget spend

“负责人:数据库团队”太宽泛。最终要映射到值班角色、升级路径和替代人。

数据清单要包含派生副本

第 18 章已经定义:

PostgreSQL authoritative business state
cache projection
event delivery/replay log
object bytes
search projection
analytical projection
backup/WAL copies

部署规划不能只列 primary 数据目录。数据流 inventory 应记录:

副本 authority freshness retention deletion rebuild
replica PostgreSQL replay lag current follows WAL base backup
backup historical recovery backup/WAL policy retention expiry restore
cache derived TTL/version eviction tombstone/TTL source refill
event bus delivery log publish lag broker policy workflow replay/snapshot
lake/search projection watermark generation tombstone rebuild

这些副本会影响存储、网络、密钥和故障域规划。

分类要进入 inventory 与 evidence,但不要泄密

配置可保存:

service_class: pg-ha-standard
data_class: confidential
owner: shop-order-team

不应把:

customer data samples
passwords
private keys
recovery secrets
token values

写入基线报告。

本章的 inventory projection 只输出安全 allowlist,记录:

source mode
secret-bearing source fingerprint withheld
safe projection sha256
secret fields redacted count
secret values exported = 0

它证明审计过程知道 secret 存在,同时不把值复制到 evidence。

未知 owner 是阻断项

没有 owner 的 service 不应进入生产。原因很现实:

  • 谁批准 maintenance?
  • 谁判断数据正确?
  • 谁接 incident 电话?
  • 谁接受 RPO?
  • 谁决定退役?
  • 谁支付容量?

可以在 sandbox 用 placeholder,但 production gate 必须把 placeholder 映射到 真实责任人/组和升级路径。

pg36_shop 的当前需求身份

本章 requirements.json 写的是:

service=pg36_shop
data=synthetic teaching only
business owner=placeholder
platform owner=placeholder
target=disposable local Linux sandbox
production approval=false

所以它可以验收部署流程,不能通过真实 pg-ha-standard 的 owner/data gate。

19.1.2 负载形态、增长、峰谷和批处理窗口

“OLTP”不是容量需求

同为 OLTP,可能分别是:

10k tiny point reads/s
500 write transactions/s with 20 indexes
50 large JSON updates/s
100 concurrent long business transactions
bursty checkout traffic
multi-tenant mixed workload

需要一份 workload inventory:

维度
transaction classes browse/order/pay/admin
read/write ratio 分类别
statements/transaction 分布
rows touched P50/P95/P99
connection behavior pool、active、idle
latency P50/P95/P99/timeout
concurrency arrival、active、parallel
WAL bytes/s 与 burst
temp bytes/query 与 aggregate
locks wait、deadlock、long xact
maintenance vacuum/index/backup

第 26 章才正式做容量曲线,本章先确保环境规划有输入。

交易、搜索、空间与分析分开建模

pg36_shop 的四类负载形状:

transactional
  selective, short, latency-sensitive, correctness first

search
  rank/filter, index-heavy, quality + latency

spatiotemporal
  GiST, range, event ingest, retention

analytics
  large scan/aggregate, temp/parallel, freshness-tolerant

如果只用总 QPS,分析扫描和订单写入会互相隐藏。

每类要记录:

endpoint/role
timeout
resource priority
expected concurrency
allowed replica/freshness
degradation
owner

峰值不是日均乘一个系数

峰值来源:

营销活动
整点任务
工资/账单周期
客户端 retry
故障流量转移
缓存失效
批处理重跑
schema migration
backup/checkpoint overlap

真实 peak envelope 至少有:

amplitude
duration
ramp rate
frequency
correlated workloads
recovery tail

一分钟 10 倍峰值与持续 4 小时 3 倍峰值需要不同资源和降级。

流量转移后的单节点峰值

三节点 topology 正常时,读流量可分散;一台 replica 故障后:

[ load_{remaining}

\frac{total\ eligible\ load}{remaining\ eligible\ capacity} ]

若两个 replica 平时各 50%,失去一个后另一个可能接近 100%,也可能 fallback 到 primary。容量必须按 failure state 规划,不只按 happy path。

同理,failover 后:

  • 新 primary 承接全部写;
  • cache 冷;
  • client 重连;
  • replica 重新追赶;
  • backup/maintenance 可能仍在;
  • 旧 primary 需要重建。

增长要分逻辑与物理

逻辑增长:

customers
products
orders/day
items/order
events/day
tenants
retention days

物理增长:

heap
TOAST
indexes
dead tuples/bloat
WAL
temp peak
replicas
backup full/diff/incr
archive retention
monitoring/logs
external projections

一个粗略存储模型:

[ capacity = (heap + toast + indexes + free\ space) \times replica\ factor

  • backup/archive
  • maintenance/upgrade\ headroom ]

不能直接拿业务 CSV 大小乘副本数。

使用增长曲线,不只用线性外推

记录:

daily/weekly sample
seasonality
new feature step changes
largest tenant
retention changes
index additions
compression/archival
confidence interval

容量到达时间:

[ T_{exhaust}

\frac{usable\ capacity - current\ usage - required\ headroom} {growth\ rate} ]

增长率有区间时给出 earliest/expected,而不是一个虚假精确日期。

批处理窗口是一项共享资源预约

批任务包括:

ETL/export
materialized refresh
search/vector rebuild
backup
VACUUM/ANALYZE
index build
partition lifecycle
financial close
data quality reconciliation

每项保存:

字段 意义
earliest start / deadline 可运行窗口
duration distribution 不只平均
CPU/I/O/WAL/temp 资源
locks/snapshot 并发影响
retry 是否会叠加
freshness 延迟后果
owner 谁停止/恢复
conflict priority 与交易冲突时谁让路

“晚上跑”不是窗口。跨时区业务可能没有真正夜间。

维护和业务峰值要画在同一时间轴

建议按 UTC 画一周:

online traffic
batch
backup
checkpoint/WAL archive
autovacuum debt
reporting
deploy
on-call coverage

你可能发现:

业务低谷
  = backup full
  = ETL full scan
  = index maintenance
  = replica lag peak

所谓低谷实际上是数据库最忙时段。

负载可回放性

为了比较环境,保存:

schema/version
data generator or anonymized snapshot identity
query fingerprints
parameter/selectivity distribution
arrival model
connection/pool model
background jobs
warm/cold cache protocol
duration
random seed
success and correctness golden

只保存一条 pgbench -c 100 命令不足以代表业务。

sandbox 的资源结论边界

本章为 Vagrant sandbox 建议每台至少:

>= 2 logical CPUs
>= 2 GiB memory
>= 8 GiB root free
swap = 0

这些只是教学环境的建议下限,不是 pg36_shop 的生产容量规格。正式实验中, 三台 pg-test VM 只有 1 个 vCPU、约 1.9 GiB 内存;部署虽然完成,但必须以 EX19-LAB-RESOURCE-FLOOR 记录偏差,不能把成功运行反推为资源充足。

四台 VM 共享一台 laptop 的:

CPU
memory controller
physical storage
power
hypervisor
host network

因此任何 benchmark 都不能外推生产。

需求表中的 unknown

本章刻意不为以下项目造数字:

production QPS
production data growth
production storage latency
production connection budget
production batch window

它们进入第 24、26、27 章。部署基线的职责是让 unknown 可见,并阻止默认值被 误报为需求。

19.1.3 可用性、RPO、RTO 与维护窗口

四个概念先分开

availability
  服务在测量窗口内按定义成功的比例

RPO
  可接受的数据恢复点损失

RTO
  从场景发生到服务恢复到规定状态的时间

maintenance window
  允许计划变更及其用户影响的时间边界

它们相关,但不能互相替代。

三节点自动 failover 可能有较短可用性中断,却无法恢复昨天误删的数据;一天 一次 full backup 可能可恢复,却不提供当前 primary HA。

先定义成功请求

可用性分母和成功必须明确:

哪些 endpoint
哪些 operation
哪些用户/区域
什么状态码/SQLSTATE
正确性是否计入
延迟阈值
陈旧度阈值
测量位置
计划维护是否排除

如果数据库返回 200/row 但金额错误,不应算可用。

“几个九”换算为时间只是直觉

以 30 天窗口为例:

目标 粗略不可用预算
99% 7h 12m
99.9% 43m 12s
99.95% 21m 36s
99.99% 4m 19s

真正 SLO 仍由事件型 SLI 计算,不能只用服务器 uptime。

RPO 必须绑定故障场景

示例:

single replica loss       RPO 0
primary failover          async lag within measured policy
sync-confirmed commit     RPO 0 only in modeled sync failure domain
operator DROP             PITR target before error
storage corruption        last verified clean recovery point
region loss               cross-domain repository/standby position

“RPO=0”若没有场景和确认语义,就是不完整承诺。

应用还要知道:

client got success -> commit durability promise
client got timeout -> outcome unknown, must query by idempotency key

RTO 从开始点到结束点

RTO 的起点可能是:

physical failure
monitor detects
alert reaches human
incident declared
recovery decision

终点可能是:

database accepts connections
write service healthy
business golden passes
backlog caught up
all clients restored

如果不定义,两个团队报告的 RTO 可以相差整个检测与验证阶段。

建议分解:

[ RTO = detection

  • decision
  • execution
  • validation
  • traffic\ restoration ]

每段都能优化,也都可能失败。

HA RTO 与 restore RTO 不同

场景 路径
primary host loss detect → elect → promote → route → client retry
database deleted stop damage → choose target → restore → replay → validate
corrupt pages preserve evidence → classify → restore/rebuild → validate
region loss activate remote infra → restore/promote → dependencies → DNS

不要用 Patroni failover 的秒数回答整库恢复需要多久。

maintenance 是预算,不是免责

维护窗口应记录:

frequency
duration
notice
allowed impact
rollback deadline
business blackout dates
owner approval
post-check

即使计划维护被 SLO 排除,用户损失仍存在。高成熟度平台会:

  • 滚动维护;
  • 验证连接恢复;
  • 限制每次 blast radius;
  • 保留 rollback;
  • 记录实际中断;
  • 复审窗口是否足够。

升级窗口必须包含回退判断

不只计算安装时间:

preflight
backup/recovery point
traffic drain
package/schema change
restart/failover
application golden
observation
rollback or forward decision

有些 PostgreSQL major upgrade 在数据目录切换后没有简单 rollback;最后可逆 点必须写清。第 30 章专门演练。

服务目标从损失与成本共同推导

目标越强,通常需要:

more independent replicas
synchronous distance/latency trade-off
more recovery copies
more frequent drills
more on-call coverage
more capacity headroom
more change discipline

不能只问“技术上能否做到”,还要问业务是否愿意持续支付,以及组织能否操作。

本章不通过第 20/21 章

第 19 章只验证环境前提:

hosts distinct
versions/initialization uniform
declared and live topology agree
endpoints exist
roles/services active
exceptions explicit

它不注入故障,不执行 failover,不做 restore。因此:

ch20-ha = pending
ch21-backup-restore = pending

sandbox 的准确结论

本章四节点能证明:

one pg-meta member
three pg-test members
one live leader
two live replicas
one offline-query declaration
Pigsty inventory/host/Patroni/SQL facts agree

不能证明:

four production failure domains
production RPO/RTO
production storage durability
production capacity
production secret lifecycle

因为所有 VM 共享物理 laptop,etcd 与 backup target 也各只有一个控制节点。

把例外当结构化结果

requirements.json 固定六项 sandbox exception:

EX19-SHARED-HYPERVISOR
EX19-SINGLE-ETCD
EX19-SINGLE-BACKUP-TARGET
EX19-VIRTUAL-STORAGE
EX19-INVENTORY-SECRETS
EX19-LAB-RESOURCE-FLOOR

通过结果必须写:

sandbox_l2=accepted-with-exceptions
production_ch19_gate=pending

“验收通过”后面没有范围,是一种危险省略。


返回本章目录 · 下一节:计算、内存、存储与网络 · 查看全书目录 · 查看索引中心

19.2 计算、内存、存储与网络

资源规划不是列一张“推荐配置”。它要把 workload 的每种稀缺资源与一个可 观察的饱和点连接起来。

19.2.1 CPU 核数、频率、NUMA 与虚拟化

核数与单核性能解决不同问题

更多核心有利于:

更多并发 backend
parallel query
autovacuum workers
backup compression
replication/application workers
多个实例/服务

更强单核性能有利于:

单条不可并行执行路径
短 OLTP tail latency
锁临界区
表达式/PL 执行
单 WAL 路径中的部分工作

不能用总 vCPU 数替代 CPU 型号、频率、代际与持续性能。

SMT 线程不等于物理核心

操作系统报告 32 CPU,可能是:

16 physical cores × 2 SMT
32 physical cores
oversubscribed 32 vCPU
burstable quota

基线记录:

lscpu --json
nproc
cat /sys/fs/cgroup/cpu.max

以及 hypervisor/cloud 的:

vCPU entitlement
steal time
credit/burst policy
dedicated/shared
pinning

PostgreSQL 并行 worker 数应基于实测吞吐和并发,不按 nproc 自动拉满。

CPU 饱和要看排队

观察:

utilization
runnable queue
steal/throttle
per-process CPU
context switches
frequency
query latency/throughput

CPU 100% 但吞吐继续线性增长,和 CPU 70% 但 cgroup throttling/steal 很高, 不是同一问题。

NUMA 使“总内存/总核心”失去均匀假设

多 socket/NUMA 系统中:

CPU attached to local memory node
remote memory access latency higher
device/interrupt locality differs
kernel allocation can become uneven

记录:

lscpu
numactl --hardware
cat /sys/devices/system/node/node*/meminfo

还要确认 BIOS/VM 的 NUMA 暴露、进程/IRQ/pinning 策略。

不要无条件“禁用 NUMA”。小单节点、超大多 socket、VM、容器的最佳策略可能 不同。变更要有 workload 实验。

本章 Vagrant VM 每台只报告一个 NUMA node,因此只能验证采集与同构,不能 形成大型 NUMA 结论。

虚拟化要记录资源保证

虚拟机的抽象层可能引入:

CPU oversubscription
steal
memory ballooning
host swap
virtual disk cache
noisy neighbor
live migration pause
shared physical failure
time drift

容器还要记录:

CPU quota/cpuset
memory.max
OOM policy
huge page access
ephemeral filesystem
PID/file limits
host network/storage

“8 vCPU/32 GiB”若没有 guarantee 与 failure domain,只是一个接口数字。

指令集与架构是兼容矩阵的一部分

本章正式 sandbox 是:

architecture=aarch64
OS=Ubuntu 24.04.x

所有节点必须相同。生产扩展 package、JIT、compression、加密库与备份恢复 都要覆盖目标架构。不能假设 x86_64 上测试的二进制扩展会在 ARM 恢复目标上 存在。

CPU 基线不是调优

第 19 章只记录:

logical count
model
NUMA node count
virtualization
kernel/cgroup identity

第 26 章测饱和,第 27 章才改变并行和参数。看到核心数不等于知道 max_parallel_workers_per_gather 应设多少。

19.2.2 内存预算、页缓存与 OOM 边界

PostgreSQL 使用多类内存

shared_buffers
WAL buffers
backend private memory
work_mem per operation
maintenance_work_mem
autovacuum_work_mem
temp_buffers per session
extension/background worker memory
connection/process overhead
OS page cache
kernel/network/filesystem
monitoring/backup/proxy

只算 shared_buffers + max_connections × work_mem 仍然过度简化。

页缓存与 shared buffers 共同工作

PostgreSQL 使用自己的 shared buffer cache,也依赖操作系统 cache。官方 Resource Consumption 给出 shared_buffers 的起始建议,同时说明 PostgreSQL 还依赖 OS cache。

这意味着:

  • 不应把全部 RAM 分给 shared_buffers
  • 文件系统/backup/extension 也需要 cache;
  • database cache hit 不等于没有底层 I/O;
  • VM host cache 还可能再加一层;
  • cold/warm benchmark 要定义清楚。

乘法内存必须按峰值并发

近似账本:

[ M_{total}

M_{shared}

  • M_{OS}
  • N_{backend}M_{backend}
  • \sum work\ nodes
  • M_{maintenance}
  • M_{other}
  • headroom ]

work_mem 是每 sort/hash 节点的基础预算;并行 worker 与多个节点会放大。

平台应按 role/workload class 设置,而不是一个全局大值:

OLTP runtime
admin migration
offline analytics
maintenance

max_connections 是内存与调度承诺

每个 PostgreSQL connection 对应 backend process。大量 idle connection 也有:

process/page table
backend state
locks/proc arrays
TLS/socket
extension/session state

大量 active connection 会导致 CPU 排队与 cache 抖动。

规划顺序:

safe active concurrency
  -> backend budget
  -> reserve admin/monitor/replication
  -> pooler server pool
  -> application pool totals
  -> client queue/timeout

不是先把 max_connections 改成 5000。

OOM 不是一种可接受的流量控制

当 Linux OOM killer 选择 PostgreSQL 进程:

  • backend 被杀;
  • postmaster 可能触发所有 backend 重启;
  • client 事务中断;
  • recovery/checkpoint 增加恢复时间;
  • HA 可能触发切换;
  • evidence 可能被噪声覆盖。

应使用:

capacity headroom
cgroup/systemd memory boundary
connection/active query budget
work/temp limit
load shedding
monitoring

在系统被 OOM 前拒绝或排队。

overcommit 与 swap 要显式

记录:

sysctl vm.overcommit_memory
sysctl vm.overcommit_ratio
sysctl vm.swappiness
cat /proc/meminfo
systemctl show ... memory controls

策略取决于宿主/容器/工作负载,但未知是不可接受的。

本章 sandbox 要求 guest SwapTotal=0,同时承认 macOS host 仍可能压缩/交换 VM 内存;VM 内看到无 swap 不证明物理主机不会产生内存压力。

Transparent Huge Pages 与显式 huge pages 不同

PostgreSQL 18 官方 Resource Consumption 说明 Linux THP 在一些环境中会导致性能下降,目前不鼓励;这与 PostgreSQL 显式 huge_pages 不是同一机制。

采集:

cat /sys/kernel/mm/transparent_hugepage/enabled
cat /sys/kernel/mm/transparent_hugepage/defrag
cat /proc/meminfo | grep Huge

Pigsty sandbox 的预期:

THP enabled current=never
THP defrag current=never
explicit hugepage count may remain 0

若要启用显式 huge pages,应按 PostgreSQL Managing Kernel Resourcesshared_memory_size_in_huge_pages 计算并验证,不靠固定百分比模板。

内存 floor 与 production sizing 分离

2 GiB 是本章建议的教学下限;正式沙箱对低于该值的三台 VM 记录了 EX19-LAB-RESOURCE-FLOOR,而没有把建议偷偷降格。production 仍需依据:

working set
peak connections
query node memory
maintenance overlap
HA/failover state
OS/agent budget
growth
headroom

通过 floor 不代表容量 gate 通过。

19.2.3 IOPS、吞吐、时延、容量与冗余

四个存储指标互不等价

IOPS       每秒操作数
throughput 每秒字节
latency    单次完成时间及分布
capacity   可用字节与增长空间

小随机 WAL/fsync、索引随机读、大顺序扫描、backup stream 的瓶颈不同。

“云盘 20,000 IOPS”不说明:

  • block size;
  • read/write mix;
  • queue depth;
  • P99 latency;
  • burst duration;
  • fsync/FUA;
  • shared cap;
  • failure behavior。

PostgreSQL 的典型 I/O 路径

路径 特征
WAL write/flush 小、顺序、durability latency 敏感
checkpoint 大量脏页写、可能 burst
heap/index read random/sequential 混合
temp spill 大量短寿命读写
vacuum 扫描 + index cleanup + WAL
backup 大顺序读 + network + repository write
replica WAL receive/write/replay + data I/O
restore repository read + data write + WAL replay

需要同时测生产 workload 和维护/故障状态。

平均延迟会隐藏 tail

保存:

P50/P95/P99/max
queue depth
utilization
read/write latency
fsync latency
throughput
errors/timeouts

交易 commit 通常对 tail latency 更敏感;一次 2 秒 flush 可能制造级联 queue。

文件系统与设备语义要完整记录

基线:

lsblk --json -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS,ROTA,MODEL
findmnt --json
df -B1
mount

同时从基础设施层记录:

local/network/block/object
RAID/replication
write cache and power-loss protection
discard/TRIM
snapshot behavior
encryption
IO scheduler
cloud volume class
burst/credit
failure domain

数据库内部无法证明底层存储真正持久。

fsync=on 仍依赖硬件诚信

PostgreSQL 发出同步请求后,操作系统/设备必须诚实地把数据持久化。虚假 write cache acknowledgment 会破坏 WAL 设计前提。

验收需要:

  • 合格存储/云服务语义;
  • power-loss protection;
  • 厂商/基础设施保证;
  • 故障测试与恢复;
  • checksums/备份/取证作为纵深。

不要用 fsync=off 解决 production latency;那是在更改持久性合同。

容量要给多个并发动作留空间

磁盘不能规划到 95% 常态:

database growth
index build/reindex
VACUUM FULL/table rewrite
major upgrade copy/link strategy
base backup staging
WAL/archive backlog
logical slot retention
temp spill
log/metrics
filesystem reserve

headroom 应按最坏的已批准维护/故障动作计算。

redundancy 不等于 backup

RAID/云盘副本:

覆盖部分设备故障
不覆盖误删、逻辑错误、很多软件损坏

PostgreSQL replica:

覆盖服务成员故障
复制已提交错误

backup/PITR:

提供历史恢复
依赖仓库、WAL、密钥、过程与时间

三者互补。

检查 checksum,但不要过度解读

PostgreSQL 18 initdb 默认启用 data checksums,可用 --no-data-checksums 关闭。checksum 能检测一类 页面静默损坏;它不纠正错误,也不覆盖:

WAL/archive completeness
application logical errors
所有内存/网络/文件损坏
backup recoverability
replica independence

本章要求每个成员 data_checksums=on,并把实际检测与修复留给第 28/35 章。

Vagrant 存储只能做流程验证

本章采集 root 与 /pg mount、总量/free、类型/options,但明确例外:

EX19-VIRTUAL-STORAGE

它不能为 production IOPS、P99、endurance、power-loss 或冗余签字。

19.2.4 时钟、DNS、带宽、防火墙与故障域

时钟是分布式证据的坐标

时钟影响:

TLS/certificate
日志关联
监控窗口
lease/election
backup/PITR target
业务时间
token expiry
incident timeline

每台记录:

timedatectl show
chronyc tracking
chronyc sources -v

验收:

timezone=UTC or Etc/UTC
NTPSynchronized=yes
source/offset within policy

“时间看起来差不多”不够。

数据库通常用 UTC 保存绝对时刻;用户展示时再按业务时区转换。OS 日志也应 统一可换算。

DNS 是服务依赖

记录:

authoritative zone
resolver path
TTL
negative cache
search domain
split-horizon
failover/update authority
monitoring

不要让 PostgreSQL 成员依赖一个单点、不可观察的外部 resolver。

/etc/hosts 适合固定 sandbox,生产服务发现要有 owner 与变更流程。

IP、hostname、service name 分层

node identity     node-1 / host UUID
instance identity pg-test-2
cluster identity  pg-test
service identity  pg-test-primary / replica / offline
business identity pg36_shop

客户端连 service,不连“当前 primary 的 IP”。IP 可以变,service 语义应 稳定。

带宽要算复制与恢复

网络预算:

client request/response
streaming WAL
base backup/rebuild
archive upload
restore download
monitor/log
external CDC/export
package deployment

恢复或新副本同步可能是最大流量。

若:

rebuild timedata byteseffective bandwidth rebuild\ time \approx \frac{data\ bytes}{effective\ bandwidth}

还要加 checksum、compression、I/O、WAL catch-up 与争用。链路标称带宽不是 effective。

网络延迟进入同步提交

同步副本确认需要跨 failure domain 往返。距离越远,commit latency 越高; 距离太近,则可能不覆盖目标故障。

所以同步位置不是“同城最好”或“跨区最好”,而是 RPO、延迟、可用性和故障域 共同取舍。第 20 章实测。

防火墙从允许关系生成

不要先“关防火墙排障”,再忘记打开。建立 flow matrix:

source destination port/protocol purpose auth
app PG service TCP business SQL TLS + role
PG member PG member TCP streaming replication role
Patroni etcd TCP/TLS DCS cert/credential
LB Patroni REST TCP role health network policy
backup repository TCP/TLS archive/restore scoped secret
monitoring exporters TCP metrics network/auth
admin hosts SSH control admin identity

规则要双向核对:inventory 声明、主机 firewall、cloud security group、实际 probe。

Pigsty 4.5 Node Parameters 说明其 firewall 模式与 intranet/public 策略;具体默认仍要从目标配置与主机 事实确认。

“四台机器”不等于四个故障域

故障域清单:

process
instance/VM
host/hypervisor
disk/controller/storage service
rack/power
switch/network
zone/datacenter
region
DNS/IAM/control plane
operator/configuration
backup repository/key

两个 node ID 可能共享除进程外的所有域。

本章四个 machine-id 确实不同,但它们共享:

one physical laptop
one hypervisor
one power source
one physical storage
one host network

所以 validator 同时要求:

machine identities distinct
EX19-SHARED-HYPERVISOR present
production SLO claim false

只通过前一项会制造错误结论。

DCS 与 backup 也有故障域

三节点 PostgreSQL + 单节点 etcd:

data members=3
DCS members=1

不是完整生产 HA。

备份放在同一 control VM/physical host:

backup copy exists
independent disaster copy does not

拓扑图必须画控制与恢复依赖,不只画 PostgreSQL。

本节的验收产物

主机采集器 remote_host_facts.py 只读输出:

hashed machine identity
OS/kernel/architecture/virtualization
CPU/NUMA
memory/swap/THP/overcommit
mount/free space
clock/NTP
addresses/DNS/firewall/listening ports
service and package versions

它记录事实,不自动执行任何 sysctl、mount 或 firewall 调优。


上一节:先写服务需求 · 返回本章目录 · 下一节:操作系统与主机基线 · 查看全书目录 · 查看索引中心

19.3 操作系统与主机基线

主机调优的最大风险,不是漏掉某个神奇参数,而是不知道当前系统实际是什么, 却批量套用一份来源、版本和目标都不明的模板。

19.3.1 文件系统、挂载、预读与透明大页

文件系统选择是一份兼容与恢复合同

记录:

filesystem type/version
mount source and target
mount options
block/sector size
discard
inode capacity
snapshot/reflink
encryption
quota
repair tooling
backup compatibility

选择 XFS、ext4 或其他支持文件系统,要基于目标 OS、存储、运维能力与 PostgreSQL/Pigsty 支持矩阵,不凭论坛结论。

数据、WAL、日志、备份与临时文件的路径

典型职责:

PGDATA
WAL (可能同盘或独立)
tablespaces
PostgreSQL logs
pgBackRest spool/repository
PgBouncer/Patroni logs
temp files (位于 relation tablespace)
monitoring/log storage
package repository

分盘不是目的。要问:

  • 故障是否独立;
  • I/O 是否真正隔离;
  • capacity 是否独立;
  • backup/restore 是否更复杂;
  • mount 缺失时会不会写进 root;
  • 监控是否覆盖每个 filesystem;
  • 权限和 SELinux/AppArmor 是否一致。

防止“挂载没上,目录还在”

危险场景:

/pg/data intended mount absent
directory exists on root filesystem
PostgreSQL starts and writes root disk
root fills
later mount hides wrong data

保护:

systemd RequiresMountsFor
mountpoint validation before service
expected device/filesystem UUID
directory marker
capacity sanity check
monitor filesystem identity, not only path

本章 capture 同时记录 target、source、fstype、options 与 free,不只记录 /pg 存在。

mount option 不应照抄

常见选项需要理解:

noatime
discard / periodic fstrim
barrier/durability defaults
inode/allocation
network filesystem sync/cache semantics

现代文件系统默认行为会变化。任何影响持久性或恢复的选项,必须有官方 文档、目标版本和故障测试证据。

预读依赖访问形状

OS/block device read-ahead 对顺序扫描可能有益,对随机 OLTP 可能放大无效 I/O。

记录:

lsblk -o NAME,TYPE,ROTA,RA,SIZE,FSTYPE,MOUNTPOINTS
blockdev --getra /dev/...

然后用:

OLTP random
analytics sequential
backup/restore stream
replica replay

分别测。不要把某 SSD 模板的 KB 值复制到所有设备。

I/O scheduler 也要与设备匹配

物理旋转盘、NVMe、virtio、cloud block 的队列和 scheduler 不同。

采集:

cat /sys/block/<dev>/queue/scheduler
cat /sys/block/<dev>/queue/nr_requests

改变前后同时看 workload latency、throughput、queue 和 CPU。

THP 与显式 huge pages

当前值:

grep . /sys/kernel/mm/transparent_hugepage/{enabled,defrag}
grep -i huge /proc/meminfo

本章要求 THP never,与 Pigsty node tuning 预期一致。PostgreSQL 官方 Resource Consumption 区分 THP 与显式 huge pages,并指出 THP 对部分 PostgreSQL 环境会造成性能 下降。

显式 huge pages 需要:

shared memory size
page size
nr_hugepages
postgres OS group/lock permissions
startup policy try/on
reboot/fragmentation behavior
monitoring

huge_pages=on 且数量不足,PostgreSQL 会拒绝启动;这是刻意强约束, 不能未经演练启用。

data directory 权限

PostgreSQL 官方 initdb 要求以最终 server owner 运行,不能 root 运行。目录应由 database OS user 拥有并限制权限。

检查:

namei -l "$PGDATA"
stat -c '%U %G %a %n' "$PGDATA"
findmnt -T "$PGDATA"

父目录权限同样重要。不要让普通应用用户读取 data files、WAL 或 backup。

19.3.2 用户、目录、权限、时间同步与日志

OS 身份与数据库身份分开

OS:

admin automation user
postgres service user
backup/repository user
monitoring/log agents

PostgreSQL:

bootstrap superuser
NOLOGIN object owner
runtime roles
monitor/replication/backup/admin roles

同名不代表同一身份。Peer authentication 才把 OS user 映射为 database role。

admin user 的前提要验收

Pigsty multi-node 部署需要管理用户可以:

passwordless SSH to targets
passwordless sudo
run Python/Ansible modules
write expected directories
reach package sources/infra

这是一项强权限。要用:

dedicated identity
key protection/rotation
source restriction
sudo policy
audit
break-glass
offboarding

本章 disposable Vagrant 用 vagrant + nopass sudo,只能在本地 sandbox 接受;production 必须重新设计。

目录清单是接口

记录每个组件:

owner/mode purpose retention
source/inventory admin IaC versioned/private
/pg/data postgres data authoritative
/pg/log postgres/agents component logs policy
/pg/backup backup repository/spool backup policy
/etc/patroni root/postgres HA config config
/etc/pgbouncer service pool config config
/etc/pgbackrest controlled backup config/secret refs config
/etc/haproxy root routing config

不要把目录路径写死进应用;通过 service/config 接口访问。

文件权限要从 threat model 验证

检查:

find ... -maxdepth ... -printf '%m %u %g %p\n'
systemctl cat <service>
systemctl show <service> -p User -p Group -p EnvironmentFiles
getfacl

特别关注:

private keys
password/config files
.pgpass / service files
backup cipher pass
Patroni REST credentials
HAProxy stats credentials
MinIO keys
Ansible inventory/vault

本章 live inventory 必须 0600,projection 不能含 secret value。

时间同步先于集群判断

chrony/其他 NTP 服务需要:

active
source reachable
synchronized
offset/jitter within policy
boot behavior
monitoring

只看 service active 不够;本章读取 timedatectl NTPSynchronized

时间跳变可能影响日志、lease、TLS、业务 timestamp 与 incident timeline。 数据库业务时间语义仍要用第 16 章的 UTC/event-time 合同。

hostname、machine-id 与 address

基线同时保存:

declared address
live hostname
hashed /etc/machine-id
live interface addresses

原因:

  • IP 可能被错误复用;
  • hostname 可能全部相同;
  • SSH config 可能把不同别名指向同一主机;
  • VM clone 可能复制 machine-id;
  • inventory 拼写可能连错环境。

本章准备部署时就发现,常规 SSH config 把 Vagrant 地址重写为本地转发端口; 只看命令参数不够。正式采集强制 ssh -F /dev/null 直连,并以 machine-id hash 证明四个目标不同。

hash 不让 evidence 暴露原始 machine-id,但仍能检测重复。

日志要有来源身份

每条集中日志至少关联:

timestamp + timezone
host/machine
service/component
cluster/instance
process/session
severity
request/query correlation where safe

不能只按 hostname,如果重装/复用会混淆。

日志容量与敏感性

PostgreSQL 日志可能含:

SQL text
parameters/data
roles/database
client addresses
errors
file paths

控制:

log_statement/log_min_duration choice
parameter logging/redaction
access
encryption
retention
export failure buffer
deletion/legal hold

“为了排障全量记录 SQL 与参数”可能制造数据泄露。

logrotate 与磁盘故障

要验证:

rotation
compression
retention
copytruncate vs reopen semantics
agent backpressure
local buffer
disk alert
component restart

日志不能与 PGDATA 互相填满;集中日志不可达也不应无限占用本地磁盘。

19.3.3 基线检查必须记录事实而非套用调优模板

基线与目标分两列

推荐格式:

observed desired status evidence
OS Ubuntu 24.04.4 Ubuntu 24.04.x pass host JSON
THP never never pass sysfs
swap 0 0 pass meminfo
filesystem ext4 virtual qualified prod storage sandbox exception findmnt
NTP synchronized synchronized pass timedatectl

事实不符合时:

fail
accepted exception with owner/expiry
planned remediation

不能把 desired 覆盖 observed 后再宣布通过。

“最佳实践参数”有上下文

网络文章常给:

vm.swappiness=1
vm.dirty_ratio=...
read_ahead_kb=...
noatime
hugepages=...
max_connections=...

每项必须问:

哪个 OS/kernel/filesystem/device?
哪个 PostgreSQL/version?
哪个 workload?
解决什么测量瓶颈?
副作用?
生效范围与重启?
如何回滚?
如何证明改善?

答不出就先记录,不变更。

配置层级要可追踪

一个值可能来自:

kernel default
distribution sysctl
cloud image
hypervisor
Pigsty role default
global inventory
cluster vars
host vars
manual change
runtime command

审计要保存最终事实和来源。只看 Git inventory 无法发现手工漂移;只看 sysctl -a 又无法知道下次重启会恢复成什么。

一次性探测命令也有风险

基线 collector 应:

read-only
bounded timeout
explicit target
no secret output
stable schema
known command allowlist
stderr/exit preserved

避免:

curl | sh
unbounded find /
dump all environment
print full config
copy /etc wholesale
run benchmark on production

本章 collector 不读取原始 machine-id 以外的 secret 文件;machine-id 只输出 SHA-256。

自动修复与验收分开

部署 playbook 可以改变主机;acceptance collector 只读。

好处:

  • 验收失败不会偷偷修复;
  • 能发现 automation 未覆盖的漂移;
  • 证据可由不同身份运行;
  • 重跑不会增加变更;
  • 故障现场可先保留状态。

task.sh all 不会执行 deploy.yml

运行两次不等于幂等证明

Ansible 第二次 changed=0 是强证据,但不是全部:

  • 外部 API 可能有副作用;
  • task 可能每次重启但未报告 change;
  • 数据初始化可能非幂等;
  • template 中动态值可漂移;
  • service 行为可能改变;
  • secrets/certificates 可能轮换。

需要同时比较:

changed/failed counts
service state
SQL/catalog identity
endpoint behavior
files/config hashes where safe

pending reboot 也是事实

包安装可能提示:

new kernel installed, running kernel still old

不要忽略。记录:

running kernel
installed candidate
reboot required
maintenance plan
post-reboot acceptance

是否立即 reboot 取决于授权和维护窗口,不应由 collector 自动作出。

例外要结构化

例外至少:

{
  "id": "EX19-VIRTUAL-STORAGE",
  "reason": "...",
  "production_impact": "...",
  "owner": "...",
  "expiry_or_scope": "sandbox only",
  "compensating_control": "no production claim"
}

本章 requirements 先固定 ID/reason/impact;production 使用时还必须补真实 owner、expiry 与 control。

基线漂移比较

每次变更前后比较:

host identities
OS/kernel/package
mount/storage
sysctl/limits
service versions/state
PostgreSQL init identity
topology/endpoints
exceptions

某些字段天然变化:

capture time
uptime
LSN
metrics counters
log size

比较器要分类,不能要求整个 JSON byte-identical。

本章采集 schema

remote_host_facts.py 输出稳定分类:

identity
kernel/OS/architecture/virtualization
CPU/NUMA
memory/THP/swap/overcommit
root and /pg mount/free
clock
network/firewall/ports
services
packages

动态运行指标留给第 25/26 章;本章是配置与环境基线。

最后的判断

主机基线的合格结论不是:

所有参数都等于模板。

而是:

对每个与服务目标有关的主机事实,我们知道 observed 值、来源、期望、 差异、证据和 owner;没有任何未解释差异被伪装成通过。


上一节:计算、内存、存储与网络 · 返回本章目录 · 下一节:版本与数据库初始化契约 · 查看全书目录 · 查看索引中心

19.4 版本与数据库初始化契约

安装包可以升级,初始化事实却不一定能在线改。版本与初始化契约要在第一次 创建数据目录前冻结,并在创建后从 PostgreSQL 自己读取回来。

本节正式契约是:

Pigsty release       v4.5.0
PostgreSQL major     18
captured patch       18.6 / server_version_num 180006
encoding             UTF8
locale/provider      C.UTF-8 / builtin
data checksums       on
block size           8192 bytes
WAL segment size     16777216 bytes
timezone             Etc/UTC
password verifier    scram-sha-256
TLS                  on

这里的 captured patch 是验收时观察值,不是允许集群成员长期运行不同小版本。

19.4.1 PostgreSQL、locale、collation、编码与 checksum

“PostgreSQL 18”不是完整版本身份

至少要区分:

major version       18
patch version       18.6
package release     distribution/vendor build revision
server build        configure options, compiler, architecture
client version      psql/libpq/driver
extension version   vector/PostGIS/... each separately
automation release  Pigsty 4.5.0

major 决定磁盘格式、系统目录、SQL/行为兼容边界;patch 主要承载 bug 与安全 修复。扩展和操作系统包又有各自版本。只写“PG18”无法重建环境。

从 server 读取:

SELECT version();
SELECT current_setting('server_version') AS server_version,
       current_setting('server_version_num')::integer AS server_version_num;

从 package manager、容器 image digest 或 artifact repository 记录 package 身份。不要用 psql --version 代替 server 版本;它只说明客户端。

database cluster、database 与 locale 的层次

PostgreSQL 官方 initdb 创建一个 database cluster:数据目录、共享系统目录和 postgrestemplate1template0。随后 CREATE DATABASE 通常从模板 复制。

因此初始化默认会向后传播:

initdb default
    -> template databases
        -> new database defaults
            -> per-column/per-expression COLLATE override

不要把 OS 的 LANG、cluster 默认和某个 database 的 locale 混为一谈。

encoding 解决字节到字符,不解决语言排序

UTF8 定义字符编码。它不自动定义:

  • “ä”排在什么位置;
  • 大小写转换规则;
  • 字符类别;
  • 模糊/前缀查询的索引语义;
  • Unicode 版本变化后的排序稳定性。

这些主要属于 collation/provider。

查当前 database:

SELECT datname,
       pg_encoding_to_char(encoding) AS encoding,
       datlocprovider,
       datcollate,
       datctype,
       datlocale,
       datcollversion
FROM pg_database
WHERE datname = current_database();

PG18 的字段反映 provider 与 locale;跨版本工具不要假设每个字段都存在。

collation 是数据语义,不是显示偏好

PostgreSQL 的 Collation Support 说明 collation 会参与:

ORDER BY / comparison
lower / upper / initcap
pattern matching
collatable expression
index ordering and uniqueness semantics

这会产生一个重要结论:

改变 collation provider 或版本,可能改变既有索引所依据的顺序;不能把 它当成客户端展示参数。

生产选择至少比较:

provider 来源 优势 需要管理的变化
builtin PostgreSQL 内建 跨 OS 可预测,部署依赖少 受 PostgreSQL major 语义约束
libc 操作系统 C library 与 OS locale 集成 OS/glibc locale 版本漂移
icu ICU library 多语言与定制能力强 ICU 版本、包与索引刷新

本章选 PG17+ 可用的内建 C.UTF-8。Pigsty 的 资源准备建议 也推荐 PG17+ 以它作为默认。这个选择偏向稳定、可预测的数据库基础排序; 它不声称提供每种自然语言的用户期望顺序。需要语言相关排序时,应明确 column/expression collation,并有业务样例。

C.UTF-8C 不能只看名字

需要记录:

locale string
locale provider
encoding
collation version
PostgreSQL major

相同 C.UTF-8 字符串由不同 provider 提供时,不应靠名称推断行为完全一致。 本章验收同时要求:

encoding=UTF8
locale_provider=builtin
datlocale/datcollate/datctype contains C.UTF-8

初始化时显式选择,创建后从目录复核

PG18 initdb 的 provider 默认仍是 libc;选 builtin 必须显式指定合法 builtin locale。不要因为 Pigsty wizard 给出了合理结果,就误记成 PostgreSQL 自己的默认。

等价意图可以表达为:

--encoding=UTF8
--locale-provider=builtin
--builtin-locale=C.UTF-8

在 Pigsty 中由 pg_encodingpg_locale 及生成的 Patroni bootstrap 配置承载。最终证据仍是 pg_database,不是模板文件。

checksum 检测静默页损坏,不创造副本

data checksum 在读页时帮助发现 I/O 系统造成的静默损坏。它不能:

  • 修复坏页;
  • 替代 backup;
  • 替代 replica;
  • 防止错误 SQL;
  • 证明 storage 持久性;
  • 覆盖 WAL 文件的所有损坏形态。

PG18 initdb 默认启用 checksum,也允许显式 --no-data-checksums。因此不能用“版本是 18”推断某个现存 cluster 已启用。

读取:

SHOW data_checksums;

SELECT datname, checksum_failures, checksum_last_failure
FROM pg_stat_database
ORDER BY datname;

on 表示检测机制启用,不表示从未发生 storage 问题;failure counter、 日志、scrub/backup 验证要一起看。

checksum 可以通过离线工具改变,但那仍是停机、容量和回退都要规划的 maintenance。对新平台,把它当初始化契约更简单。

本章的验证 SQL

postgresql-facts.sql 一次读取:

cluster_name
server_version_num
pg_is_in_recovery()
system identifier / timeline
checksum, block, WAL segment
timezone, password_encryption, SSL
database encoding/provider/locale
installed extensions

它以 JSON 输出,避免人眼从四台机器复制表格时错列。

19.4.2 WAL、页大小、扩展与认证前提

block size 属于二进制与磁盘格式合同

本章观察:

SHOW block_size;       -- 8192

8 KiB 是常见构建值。它影响 relation page、buffer、部分容量公式和工具 兼容。不要假设所有自编译发行版都相同,也不要把 OS filesystem block size 当成 PostgreSQL page size。

恢复、物理复制、低层工具与扩展需要与 server build 兼容。

WAL segment size 只能在初始化时选择

PG18 initdb --wal-segsize 接受 1–1024 MiB 的 2 次幂,默认 16 MiB,而且只能初始化时设置。

本章冻结:

SHOW wal_segment_size; -- 16MB

segment 大小不改变 WAL 逻辑正确性,但会影响:

archive object granularity
directory file count
shipping/retention operations
monitoring formula
tool assumptions

改变它不是调一个 reload 参数,而是重建/迁移问题。

wal_level 等运行参数不是全部初始化事实

要区分三类:

类型 例子 典型改变方式
compile/init page size、WAL segment、system identifier、默认 locale rebuild/init/migrate
restart shared_preload_libraries、部分 WAL/worker 参数 rolling maintenance
reload/session 许多 planner/logging/timeout 参数 controlled reload/role policy

同样一个配置文件中的两行,变更成本可能完全不同。变更系统应记录 context

SELECT name, setting, unit, context, pending_restart, source, sourcefile
FROM pg_settings
WHERE name IN (
  'wal_level',
  'max_wal_senders',
  'max_replication_slots',
  'shared_preload_libraries',
  'password_encryption',
  'ssl'
);

system identifier 界定物理 cluster 身份

pg_control_system() 可读取 system identifier:

SELECT system_identifier,
       pg_control_version,
       catalog_version_no
FROM pg_control_system();

本章要求:

pg-meta members   one system identifier
pg-test members   another system identifier
two clusters      identifiers differ

它能揭露把同一 data directory/复制链误报成两个服务单元的错误。它不是 secret,但属于运维身份;对外报告可只保存关系或受控 evidence。

timeline 不是 cluster identifier。promotion 后 timeline 可以改变,system identifier 保持。第 20 章会使用这个区别。

扩展契约从 package 开始,不从 CREATE EXTENSION 开始

对每个扩展记录:

supported PG majors
OS/architecture packages
package and extension version
shared_preload requirement
dependencies
superuser/trusted install boundary
backup/restore behavior
physical replica parity
rolling/minor/major upgrade path
license/security owner

四个节点能启动 core PostgreSQL,不表示某个动态库在 future promotion candidate 上存在。package parity 应在部署时验收;catalog 中 pg_extension 只说明当前 database 安装了什么。

SELECT extname, extversion, extnamespace::regnamespace
FROM pg_extension
ORDER BY extname;

第 14 章负责扩展生命周期;本章只冻结部署前提。

shared_preload_libraries 是重启边界

本章观察:

pg_stat_statements, auto_explain

预加载库会进入 server 生命周期。新增/移除通常需要 restart,并必须在每个 可能承接 primary 的节点有兼容库。配置字符串一致还不够:

package file exists
linker dependencies resolve
library matches PG major/architecture
startup succeeds
replica parity holds

authentication method 与 password verifier 是两件事

password_encryption=scram-sha-256 控制新密码保存为什么 verifier;真正 允许哪类连接,由:

listen_addresses / port / TLS
pg_hba.conf rule order
database
role
source address
auth method
client driver support

共同决定。

initdb --auth-* 会生成初始 HBA,但 Pigsty 随后声明式管理业务、复制和 管理访问。不要把 initdb 选项当作最终 access policy。

SCRAM 迁移需要客户端矩阵

本章新环境要求:

password_encryption=scram-sha-256
HBA methods use intended SCRAM policy
all drivers/pools support SCRAM
old MD5 verifier rotation is planned

只改 password_encryption 不会重写既有角色密码。role 需要重新设置密码才 生成新 verifier。不要查询或导出 pg_authid.rolpassword 到 evidence。

TLS on 不是 TLS 完成

SHOW ssl = on 仅证明 server 可接受 TLS。完整合同还要在第 31 章验证:

certificate identity/SAN
trust root
private-key permission
expiry/rotation
minimum protocol/cipher
client sslmode and hostname verification
revocation/incident process
plaintext path policy

本章只把 ssl=on 作为部署前提,不宣称传输安全审计已经完成。

不安全的初始化捷径

生产拒绝:

initdb --auth=trust
initdb --no-sync
unknown locale inherited from shell
checksum disabled without ADR
plaintext/generated default password published in Git
mixed PG major physical replicas
extension binary only installed on current primary

官方文档明确把 --no-sync 定位为测试用途;系统崩溃可能让初始化后的目录 损坏。自动化快几秒不是持久性理由。

19.4.3 版本矩阵、升级窗口与勘误入口

一条“版本号”要展开成兼容矩阵

建议基线:

当前身份 兼容/升级问题
OS Ubuntu 24.04.4 aarch64 kernel、glibc、OpenSSL、locale
automation Pigsty v4.5.0 exact tag inventory/schema/playbook behavior
PostgreSQL 18.6 patch rollout、major upgrade
HA Patroni package/version DCS/API/config compatibility
DCS etcd package/version quorum、snapshot、client compatibility
proxy/pool HAProxy/PgBouncer protocol, auth, routing semantics
backup pgBackRest + repo format restore target/version
extensions per extension PG ABI, SQL update scripts
clients driver/pool version protocol, SCRAM, TLS, type behavior
observability exporter/dashboard rules metric name/label changes

矩阵必须标:

supported
tested
deployed
deprecated
exception
owner
next review

“支持 PG18”不等于“当前所有 extension build、driver 和 restore path 已在 PG18.6 测过”。

primary 与 standby 尽量保持同一 patch

PostgreSQL standby planning 指出 physical log shipping 不能跨 major,并建议尽量保持相同 release level。短暂 rolling patch 差异需要:

  • 官方升级说明;
  • package availability;
  • replica-first 顺序;
  • rollback/forward-only 判断;
  • extension parity;
  • promotion eligibility;
  • 限定窗口与监控。

不能把“minor 通常磁盘格式兼容”扩大成无限期混跑承诺。

patch 与 major 使用不同 runbook

patch upgrade 常见路径:

read release notes
stage package parity
upgrade replicas
restart/rejoin/observe
move service or controlled switchover
upgrade former primary
verify

major upgrade可能需要:

pg_upgrade
logical replication
dump/restore
new cluster + migration
extension upgrade
statistics/index refresh
application compatibility
cutover/rollback boundary

本章只建立矩阵;第 30 章执行版本升级。

maintenance window 不只是“可以重启”

窗口要写:

allowed customer impact
change start / latest abort / end
required replicas and headroom
backup/recovery prerequisite
traffic drain and connection behavior
replication catch-up limit
rollback point
owner and incident escalation
post-change observation

如果升级需要 40 分钟,而窗口只有 30 分钟,“尽量完成”不是计划。

source pinning 与 dirty working tree

本章部署没有直接使用维护者本地 dirty Pigsty checkout,而是:

resolve exact tag v4.5.0
record commit 2d5a45f759274048de0c197829228a71d0182e5c
git archive exact tag to private temporary directory
generate private inventory there
deploy from that archive

这避免把未提交修改偷偷带进正式证据。

source pinning 仍不是 supply-chain 完整方案。生产还要验证:

trusted upstream/repository
tag/release artifact authenticity
artifact checksum/signature
package repository snapshot
SBOM/vulnerability response
internal promotion process
retention/rebuild ability

文档与实验也要有版本范围

书中命令页应标:

last verified date
PostgreSQL major/patch
Pigsty release
OS/architecture
lab schema/release
known exceptions

本节验证日期是 2026-07-29。读者使用更新版本时,应先查官方 release note 和参数文档,不应因为 URL 仍可访问就假设行为不变。

勘误入口是一条可执行路径

发现差异时记录:

claim ID
book page/anchor
observed version and platform
reproduction
expected versus actual
official source
severity
workaround
owner
target release

修订后要更新:

  • 正文;
  • lab requirements/baseline;
  • validator 与 negative case;
  • verified matrix/date;
  • migration note。

只改一句 prose 而不改 validator,会让书和实验分叉。

初始化契约的 release gate

本章正式 validator 要求四个 PostgreSQL 成员都满足:

server major=18
encoding=UTF8
provider=builtin
locale=C.UTF-8
checksums=on
block=8192
WAL segment=16777216
timezone=Etc/UTC
password_encryption=scram-sha-256
ssl=on

并运行反例:

disable-data-checksums -> E_PG_INIT

这证明规则能拒绝一个已知错误,不只证明正常样本能被脚本读出来。

通过后的结论仍是:

initialization_contract=accepted-for-this-sandbox
major_upgrade_readiness=not-tested
production_security_gate=pending
production_ch19_gate=pending

上一节:操作系统与主机基线 · 返回本章目录 · 下一节:拓扑、命名与故障域 · 查看全书目录 · 查看索引中心

19.5 拓扑、命名与故障域

三台服务器不是高可用拓扑,除非我们知道三台分别会因什么而一起失败。 “主库、从库、VIP”也不是足够精确的词表,尤其在 promotion 之后。

19.5.1 主节点、同步副本、异步副本与仲裁

primary 是当前角色,不是机器身份

PostgreSQL physical replication 中:

primary   接受写入并生成 WAL
standby   持续恢复并接收/重放 WAL
hot standby  在恢复中提供只读查询

promotion 后,原 standby 可以成为 primary。于是:

pg-test-1 = stable member identity
primary   = mutable runtime role

把 hostname 叫 prod-primary 会在第一次切换后撒谎。

原生证据:

SELECT pg_is_in_recovery();
false -> 当前可写 primary 形态
true  -> 当前处于 recovery 的 standby

它不单独证明 service routing 正确,也不证明另一台没有同时可写。

physical standby 重放同一条 WAL 历史

同一 cluster 的成员应共享:

system identifier
compatible major/build/storage layout
timeline ancestry
WAL history
tablespace paths
extension binary prerequisites

官方 Log-Shipping Standby 强调 primary/standby 应尽量相似,physical log shipping 不跨 major。

本章用 system identifier 检查:

pg-test-1 == pg-test-2 == pg-test-3
pg-meta != pg-test

异步复制的 commit 不等待副本

streaming replication 默认异步。正常低延迟不等于零 RPO:

client receives commit
WAL may still be in primary-only failure domain
primary suffers unrecoverable loss
promotion candidate lacks last records

数据损失窗口由故障时实际 receive/write/flush/replay 位置决定,而不是 平时 dashboard 上“通常 0 MB”决定。

观察 primary:

SELECT application_name,
       client_addr,
       state,
       sync_state,
       sent_lsn,
       write_lsn,
       flush_lsn,
       replay_lsn,
       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_gap_bytes
FROM pg_stat_replication
ORDER BY application_name;

本章只确认 replica 在 streaming;第 20 章才测 failure-time 数据包络。

同步复制把一部分 commit latency 换成确认

PostgreSQL synchronous replication 可以让 commit 等待一个或多个 standby 反馈。等待层次包括:

remote_write
on / remote flush
remote_apply

它们不是同义词。remote_apply 还等待 replay 可见,延迟与阻塞代价更高。

配置又分:

FIRST n (...)   priority-based
ANY n (...)     quorum-based

同步复制只有在以下条件下才提供期望保护:

  • standby 是真实独立故障域;
  • storage flush 语义可信;
  • synchronous standby 处于 streaming;
  • application 没有降级为更弱 synchronous_commit
  • 超时/降级策略符合业务选择;
  • 失联时是停止写还是降级继续写已经定义。

“有 sync replica”不能省略这些条件。

同步复制也可能让写入不可用

若要求一个同步确认,而没有任何合格 standby:

transaction does work
commit waits
locks may remain held
application timeout/retry
load amplifies

因此 RPO 与 availability 之间要由 owner 选择:

fail closed: wait, preserve durability target
degrade: resume async, accept explicit data-loss risk

自动降级如果没有审计,就是悄悄改变服务合同。

“仲裁”不要与数据副本混淆

PostgreSQL core 没有一个保存业务数据的“仲裁节点”概念。Pigsty 的 HA 参考实现里:

Patroni  管理成员状态、leader lock 与操作
etcd     提供分布式配置存储/共识
PostgreSQL members 保存业务数据/WAL
HAProxy 依据 health checks 路由 service

etcd member:

  • 不保存可 promotion 的 PostgreSQL data;
  • 不确认每个业务 transaction 已持久化;
  • 不替代 backup;
  • 不依据最新 WAL 自动解决所有数据选择;
  • 需要自己的 quorum 与 failure domains。

把一个 etcd 节点叫“第三票”,然后声称两台 PostgreSQL 已实现零数据丢失, 是错误模型。

DCS quorum 与数据库成员数分别计算

三节点 PostgreSQL + 单节点 etcd:

database copies = 3
DCS members      = 1
shared laptop    = 1

三个数字不能互相抵消。单 etcd failure 会影响自动 HA 控制面;同一 laptop failure 会同时失去全部 VM。

本章正式沙箱把这两项登记为:

EX19-SINGLE-ETCD
EX19-SHARED-HYPERVISOR

所以它适合练拓扑与协议,不适合宣称 production availability。

offline replica 是 workload placement

Pigsty 支持专门的 offline instance,也支持在已有 replica 上设置 pg_offline_query: true,把它纳入 offline service。官方 Cluster / Instance 明确说明后一种是资源折中。

本章:

10.10.10.13:
  pg_role: replica
  pg_offline_query: true

它仍是同一 pg-test physical replica,不是独立 analytical authority。 标记不自动限制 CPU/I/O,也不自动阻止用户绕过 service 直连。第 17、22、 26、30 章分别处理分析语义、服务接入、容量和资源治理。

本章拓扑不是第 20 章结论

本章可以证明:

one Patroni leader
two streaming replicas
SQL recovery roles agree
declared endpoints reachable

不能证明:

leader loss detection time
fencing
split-brain exclusion
automatic failover RTO
failure-time RPO
client reconnection semantics
old primary rejoin safety

这些必须用受控 fault drill 取得。

19.5.2 节点、实例、集群、服务的统一词表

同一个“集群”常指四种东西

先建立词表:

本书定义 稳定身份/证据
host/node OS 实体:物理机、VM 或明确容器边界 machine ID、hostname、address
PostgreSQL instance 一个 server process + data directory + port member name、PGDATA、system ID
PostgreSQL database cluster 一个 initdb 产生的数据目录及其中 databases system identifier
HA cluster / Pigsty pg_cluster 一组共享 physical history、可相互接管的 members pg_cluster、Patroni scope
database cluster 内的 SQL database OID/name/owner/locale
service 面向客户端的稳定访问语义 name、port、selector、health check
pool 连接复用/状态边界 PgBouncer endpoint/mode
DCS cluster Patroni 使用的 etcd 共识域 etcd membership/quorum
platform deployment inventory 管理范围 exact inventory/release

PostgreSQL 官方把一个 data directory 称为 database cluster;平台团队又常把 一个 HA group 称 cluster。文档必须说明上下文。

node 不等于 instance

一台 node 可以运行:

PostgreSQL instance
PgBouncer
HAProxy
Patroni
exporters/Vector
infra components
多个独立实例(若平台允许)

一个 PostgreSQL cluster 也可以跨多 node。

“node down”与“Postgres down”故障面不同:

process crash
OS crash
VM pause
host failure
rack/zone loss
network partition
storage loss
control-plane loss

instance identity 不能只用 IP

IP 可以复用,DNS 可以漂移,hostname 可以被自动化改写。本章组合:

declared address
post-convergence hostname
hash(machine-id)
Patroni member name
PostgreSQL system identifier
pg_cluster
pg_seq

实验中发现一个真实陷阱:工作站 ~/.ssh/config 将四个地址映射到本机不同 转发规则,最初所有连接看起来都是 meta。正式采集强制:

ssh -F /dev/null ...

再验证四个不同 machine ID。连接成功不是目标身份成功。

service 是语义,不是 socket

Pigsty 默认 service 可能包括:

5433 primary
5434 replica
5436 primary direct
5438 offline

官方 Service/Access 记录这些默认映射。端口能建立 TCP 只证明 listener/reachability,不证明:

  • primary service 一定落在 current primary;
  • replica service 的 freshness;
  • pool transaction/session 语义;
  • role/database/HBA 正确;
  • TLS hostname 验证;
  • failover 后客户端恢复。

所以本章 endpoint probe 的解释字段明确写:

reachability and Patroni identity only
routing/pooling/failover/saturation belong to chapter 22

cluster name 不应含 current role

推荐稳定层次:

service unit   pg-test
member         pg-test-1 / pg-test-2 / pg-test-3
runtime role   primary / replica
service        pg-test-primary / pg-test-replica / pg-test-offline
database       test
application    pg36_shop

pg-test-primary 可以是 service 名,不应是固定 machine 名。

数据库里的 role 又是另一种 role

避免一句“role 是 replica”同时指:

Pigsty member role       pg_role
Patroni runtime role     Leader/Replica
PostgreSQL recovery role pg_is_in_recovery
SQL authorization role   pg_roles
business owner role      human/team responsibility

表格、变量名和 evidence 都写全称。

声明角色与观察角色都要保存

inventory 声明:

10.10.10.11: { pg_seq: 1, pg_role: primary }
10.10.10.12: { pg_seq: 2, pg_role: replica }
10.10.10.13: { pg_seq: 3, pg_role: replica, pg_offline_query: true }

观察:

Patroni leader/replica + state
SQL in_recovery
service health identity

第一次部署时二者应一致。发生 failover 后,inventory 的 bootstrap role 不应 被天真解释为永恒运行角色;第 20 章会定义 drift 语义。

19.5.3 环境、区域、租户与业务命名

命名要编码稳定属性

适合进入名字的:

environment
service/business domain
service unit
region/site
sequence

不适合进入固定 host 名的:

current primary
healthy
latest
temporary owner
current version
current ticket

稳定属性让名字在 role change 后继续真实。

一份可扩展命名模型

示例:

platform deployment  shop-prod-cn1
service unit         pg-shop-order
members              pg-shop-order-1..3
database             shop
NOLOGIN owner        shop_owner
runtime login        shop_app
services             pg-shop-order-primary
                     pg-shop-order-replica
                     pg-shop-order-offline

名字要满足:

  • PostgreSQL identifier 限制;
  • DNS label 限制;
  • metric label cardinality;
  • certificate SAN;
  • log/search 可读性;
  • automation group syntax;
  • rename cost。

environment 是权限与数据边界

常见:

dev
test
staging
prod
dr

不能只靠名字隔离。还要有:

account/project
network
credentials/CA
backup repository
monitoring tenant
data policy
change authority
failure domain

prod database 和 test database 放在同一个 cluster,通常仍共享 superuser、 WAL、storage、restart、capacity 和 incident blast radius。

region、zone、rack 必须对应可验证故障域

一个 label az-a 不等于独立 zone。登记:

provider/site ID
building/room/rack
power feed
top-of-rack/core network
hypervisor/host group
storage controller/array/replication
DNS/NTP/KMS/CA
DCS and backup dependencies

然后为每个 component 画依赖。相同依赖会形成 correlated failure。

本章四台 VM:

four machine IDs
one physical Mac
one hypervisor
one power source
one storage substrate

所以 failure-domain count 不是四。

tenant 边界不等于 schema 名

租户可能通过:

row
schema
database
cluster
account/project

隔离强度逐步变化,成本也变化。命名中加入 tenant 前,先定义:

  • authentication/authorization;
  • noisy-neighbor;
  • backup/restore granularity;
  • key ownership;
  • data residency;
  • deletion;
  • metrics/audit;
  • exit/migration。

本章 pg-metapg-test 是两个 service unit,不是两个 customer tenant。

业务名与技术名需要映射表

不要让应用团队猜:

业务对象 平台对象
pg36_shop service pg-test teaching service unit
canonical shop DB future pg36_shop provisioning target
control/validation pg-meta
OLTP write endpoint primary service contract
analytical read offline service contract

正式 inventory 当前还会创建 template 自带的 test/meta database。它们是 部署示范对象,不表示上卷的 pg36_shop fixture 已迁入这个下卷沙箱。

name registry 要防冲突与复用

登记:

name
type
immutable ID
environment
owner
created/retired
aliases
DNS/certificate names
monitoring labels
backup stanza
reuse quarantine

立即复用已退役 cluster 名可能让:

  • old DNS/cache 指向新服务;
  • backup stanza 混淆;
  • monitoring time series 串联;
  • client secret 意外生效;
  • automation limit 选错目标。

本章拓扑清单

topology.mmd 表达:

10.10.10.10 pg-meta-1  pg-meta primary + infra/etcd/MinIO
10.10.10.11 pg-test-1  pg-test primary
10.10.10.12 pg-test-2  pg-test replica
10.10.10.13 pg-test-3  pg-test replica + offline query

它同时画出共享 hypervisor。架构图若只画 database arrows、隐藏共同依赖, 会高估 availability。

命名与拓扑验收

validator 交叉检查:

address -> expected post-deployment hostname
address -> distinct machine ID hash
address -> inventory pg_cluster/pg_role/offline
address -> SQL cluster_name/in_recovery/system ID
cluster -> Patroni host/role/state

反例:

reuse-one-machine-identity -> E_HOST_IDENTITY
declare-two-live-leaders   -> E_TOPOLOGY

通过后的精确结论:

四个地址对应四个同构 Linux guest;pg-metapg-test 是两个不同 PostgreSQL system identifier;pg-test 有一个 leader 和两个 streaming replica;全部 guest 仍共享一个物理故障域。


上一节:版本与数据库初始化契约 · 返回本章目录 · 下一节:用声明式清单交付两个服务单元 · 查看全书目录 · 查看索引中心

19.6 用声明式清单交付两个服务单元

声明式交付的核心不是“YAML 很先进”,而是让目标、作用域、版本、差异与 执行证据可以评审。inventory 是意图,不是已经发生的事实。

19.6.1 inventory、参数模板与主机分组

inventory 同时承载图和参数

Pigsty inventory 的结构大致是:

all:
  children:
    infra:
      hosts: ...
    etcd:
      hosts: ...
    pg-meta:
      hosts: ...
      vars:
        pg_cluster: pg-meta
    pg-test:
      hosts: ...
      vars:
        pg_cluster: pg-test
  vars:
    version: v4.5.0
    pg_version: 18

它表达两类信息:

membership graph
  哪些 host 属于哪些 module/cluster

desired parameters
  version, paths, packages, tuning, users, DBs, services, access

混在一个文件里不表示它们的生命周期相同。host identity 可能多年稳定, password 要轮换,service definition 会迭代,初始化参数只在新 cluster 生效。

identity 参数必须明确

Pigsty 的参数层次包含 global、group/cluster、host/instance 等作用域。对 PostgreSQL member,核心 identity 至少有:

pg_cluster: pg-test
pg_seq: 1
pg_role: primary

官方 Pigsty parameter modelpg_clusterpg_seqpg_role 等视为无默认值的 identity 参数。

没有 identity,不应让自动化猜:

  • 这是哪个 HA group;
  • member 名是什么;
  • bootstrap primary 是谁;
  • service/monitor/backup 如何命名。

参数优先级要可解释

同一参数可能来自:

role default
global vars
module/group vars
cluster vars
host vars
extra vars
generated template
runtime dynamic config

最终值要能回答:

value
source
scope
owner
change context
rendered destination
live observed value

“我在 YAML 里搜不到”不等于它使用 PostgreSQL 默认;可能来自 role default 或 template。

本章 live inventory 没有重复声明每一个 role default。验收从 SQL 读取 checksum、locale、timezone 等结果,避免把“省略”误作“不确定”或 “一定是 PostgreSQL 默认”。

参数模板是起点,不是服务等级

pg_conf: oltp.yml 可提供合理 OLTP 起点,node_tune: oltp 可收敛主机 baseline。它们不包含业务 workload evidence。

模板不能自动知道:

peak TPS and query mix
working set
connection fan-out
storage latency
WAL generation
RPO/RTO
maintenance workload
tenant contention

第 26、27 章才通过压力与瓶颈证据调参。

从 exact release 生成配置

本章没有直接运行当前 dirty checkout,而是:

git archive v4.5.0

解到私有临时目录,并记录 tag commit:

2d5a45f759274048de0c197829228a71d0182e5c

然后:

./configure -c ha/full -s -n -g -v 18 -r default

参数含义:

-c ha/full   使用四节点功能演示模板
-s           跳过 IP 探测/替换
-n           非交互
-g           生成随机密码
-v 18        PostgreSQL major 18
-r default   默认上游仓库区域

ha/full 官方定位主要是演示与测试,不是生产拓扑模板;它把 infra、单 etcd、 MinIO 和 pg-meta 放在第一节点,pg-test 放在后面三节点。这正是本章 将其标为 sandbox 的原因。

generator output 必须 review

wizard 给出初稿后,逐项 review:

source release
target addresses and SSH user
OS/architecture
module membership
PostgreSQL major/locale/checksum
storage paths
cluster/member identity
service/offline placement
repo/mirror/proxy
secrets
safeguards
backup target
monitoring retention
firewall/access

configure 成功只表示生成文件,不表示目标可部署。

live inventory 是 secret-bearing artifact

随机密码比模板默认密码安全,但文件仍含 secret。处理:

mode 0600
private temporary/secret-managed path
never paste full file into issue/chat/log
never commit
rotate if exposed
production uses secret authority and lifecycle

本次初始化过程中,首次生成的凭据曾出现在工具输出边界;因此立即重新生成, 正式 live inventory 使用另一组未打印的值。这个事件提醒我们:

redaction 不是最后一步;command output、debug、diff 和 CI log 都是泄露面。

只导出 allowlist projection

inventory_projection.py 读取 live inventory,只保留:

Pigsty/PostgreSQL/locale non-secret globals
host set and group membership
cluster/member role/offline flags
source mode and a withheld source-fingerprint marker
safe projection checksum in the capture manifest
redacted secret-field count
secret_values_exported=0

它不使用“把已知 password 字段替换为星号后整份输出”的 denylist 模式。 denylist 容易漏掉新字段;allowlist 默认拒绝未知内容。

执行:

export PG36_CH19_INVENTORY=/absolute/private/pg36.yml
export PG36_EVIDENCE_DIR=/absolute/evidence/ch19
static/labs/ch19/task.sh project

投影不是可用于部署的 inventory,故意不可逆。

sanitized example 只表示形状

inventory.example.yml 使用 sentinel secret,帮助理解结构。它不应原样部署。

审阅 example 时也要防止:

真实 address/owner accidentally copied
有效 token/password
private key
internal repository credential
production backup endpoint

inventory 是 code,但不等于把一切都放 Git

适合版本化:

schema and groups
non-secret desired parameters
service definitions
role/database declarations without secret values
policy IDs
change history

需要受控 secret store:

password
private key
recovery secret
API token
CA signing key
KMS credential

需要外部事实系统:

asset/failure-domain inventory
IPAM/DNS authority
owner/on-call
certificate issuance
package artifact promotion

声明式不是单文件崇拜,而是让 authority 可追踪。

19.6.2 生产服务与隔离验证服务

先澄清本节标题的边界

生产设计应该区分:

application service unit
platform/control/validation unit

本章用 pg-testpg-meta 演练这种分工,但两者都位于 disposable sandbox:

production_data_permitted=false
production_traffic_permitted=false

所以 pg-test 是 production-shaped teaching service,不是生产服务; pg-meta 也不是合格的生产 control plane。

服务单元一:pg-test

声明:

pg-test:
  hosts:
    10.10.10.11:
      pg_seq: 1
      pg_role: primary
    10.10.10.12:
      pg_seq: 2
      pg_role: replica
    10.10.10.13:
      pg_seq: 3
      pg_role: replica
      pg_offline_query: true
  vars:
    pg_cluster: pg-test

目的:

three-member topology
primary/replica service rehearsal
offline placement rehearsal
chapter 20 fault target
chapter 21 recovery target
chapter 22 endpoint target

当前 bootstrap role:

.11 primary
.12 replica
.13 replica/offline

future failover 后 runtime role 可能变化;inventory declaration 与 observed role 的解释要跟着 chapter 20 contract 更新。

服务单元二:pg-meta

声明:

pg-meta:
  hosts:
    10.10.10.10:
      pg_seq: 1
      pg_role: primary
  vars:
    pg_cluster: pg-meta

同一 node 还承载:

Pigsty infra
single etcd member
MinIO
monitoring/logging
admin source

它可用于控制与验证,但这个共置形成大 blast radius。不要把“组件齐全”解释为 “组件高可用”。

两个 service unit 必须有不同 system identifier

部署时如果错误 clone 同一 cluster,再改名字,表面可能出现两个 group。 因此验收要求:

system_id(pg-meta) != system_id(pg-test)

同时:

system_id(pg-test-1)
= system_id(pg-test-2)
= system_id(pg-test-3)

这是 declaration 和 physical lineage 的交叉检查。

control data 与 application data 不要无意混合

真实平台需要决定:

Pigsty metadata DB 是否与业务 cluster 分离
monitoring outage 是否影响 DB availability
backup repository failure 是否影响 primary
DCS failure 是否影响 existing traffic/new failover
control credentials 是否能访问 business data
control-plane maintenance blast radius

本沙箱共置是资源选择,不是推荐生产答案。

offline 服务不等于隔离环境

.13 是 replica + offline query 标记。它仍:

接收同一 WAL
占同一 laptop CPU/storage
依赖同一 DCS
可能被 direct access
可能因慢查询产生 recovery conflict

要实现更强隔离,可能需要:

dedicated host/failure domain
cgroup/resource limits
separate service/HBA/role
query timeout
replication/freshness SLO
dedicated cluster or analytical system

production topology 要替换六个例外

从本章沙箱走向生产,不是删除 sandbox 字样。至少解决:

independent failure domains
etcd quorum
backup target/DR independence
qualified storage
secret authority/rotation
measured resource capacity

并完成后续章节 gate。

service unit review 表

问题 pg-meta pg-test
authority control/validation teaching application service
members 1 3
current primary .10 .11
replicas 0 .12, .13
offline placement no .13
system ID own shared within three members
production SLO none none
later fault target limited chapter 20

这张表应进入 design review,而不是从 Ansible recap 猜。

19.6.3 幂等部署、差异检查与失败重跑

幂等的正确含义

理想的 idempotent task:

run desired convergence once -> target state
run again without input/drift -> no material change

但整套部署包含:

package repositories
generated secrets/certificates
database initialization
backup creation
monitoring registration
external APIs
service restarts
time-dependent facts

所以不能用“Ansible 是幂等的”替代每个 action 的语义。

Pigsty 官方 Playbooks 说明大多数 playbook 可重复运行,同时指出清理参数和 *-rm.yml 等有重要 caveat。

一次完整部署的受控顺序

本章实际流程:

1. resolve/extract exact release
2. generate and review private inventory
3. direct SSH ping four identities
4. copy exact release + private inventory to admin node
5. bootstrap admin prerequisites
6. run ./deploy.yml -i pg36.yml
7. retain private log and return code
8. capture only safe recap
9. read-only L2 evidence capture
10. positive + negative validation and review

Pigsty deploy.yml 是 core chain 的 one-pass deployment。它完成很多工作, 不意味着每个业务 policy 自动满足。

recap 是执行证据,不是服务验收

本次安全 recap:

target ok changed unreachable failed
10.10.10.10 326 248 0 0
10.10.10.11 174 134 0 0
10.10.10.12 159 120 0 0
10.10.10.13 159 120 0 0
localhost 6 4 0 0

它证明 playbook 没报告 failed/unreachable。它不证明:

correct target identity
correct locale/checksum
one leader
replication healthy
service routing
production capacity
backup restore
HA behavior

这些另行验收。

changed=0 也不是唯一幂等标准

一些 task 合理地每次:

refresh facts
check service
render timestamped artifact
probe endpoint
rotate ephemeral state

也有 task 误报 changed。应关注:

unplanned service restart
config checksum drift
package version change
database reinitialization
role/permission mutation
secret rotation
endpoint outage

二次执行前先查 playbook/tag 的语义,不以追求漂亮 recap 为目标。

失败重跑前先分类

失败类型:

类型 例子 下一步
transient repo timeout、短暂 DNS 保存证据后 bounded retry
declaration wrong group/var 修 inventory,review diff
prerequisite sudo/clock/disk 修前提并重新 preflight
partial init primary created, replica failed 查 cluster state,按 role runbook
incompatible package/extension/OS 停止,修矩阵
destructive drift wrong target/data exists 停止并升级决策
secret exposure log printed credential 先 rotate/contain

不要在不知道已执行到哪里时直接:

rm -rf PGDATA
pgsql-rm
wipe/reconfigure all

失败现场是证据。

限制 scope

Pigsty playbook 支持 Ansible -l 与 tags。例:

./pgsql.yml -i private.yml -l pg-test
./pgsql.yml -i private.yml -l 10.10.10.13
./pgsql.yml -i private.yml -l pg-test -t pg_service

使用前必须确认:

inventory exact path
limit resolves to expected hosts
tag dependencies
check/diff support and limitations
serial/batch behavior
current member role
change authority

-l pg-test 是作用域控制,不是安全沙箱。

diff 不能泄密

安全 diff 分层:

secret-free projected inventory diff
rendered config hash/semantic diff
live pg_settings diff excluding secrets
package/version diff
service membership diff
policy exception diff

不要把 live inventory git diff、Ansible -vvv、template variables 或 .pgpass 直接上传。

automatic reboot 被刻意拒绝

bootstrap 观察到 meta 节点已安装 kernel 与当前 running kernel 有差异。这 可能要求 reboot 才完成 host baseline,但本章没有自动重启:

  • reboot 会改变运行状态;
  • control/infra/DB 共置;
  • 尚未建立第 20 章 HA 行为证据;
  • maintenance authority 未授予;
  • 本章 deployment 成功不要求偷偷消除 warning。

正确做法是登记 exception/change,计划可观察的 reboot,而不是为了让 preflight 变绿直接执行。

normal lab 没有 deploy action

task.sh 只提供:

project
capture
verify
review
all
reset:cluster

其中 all 只读。没有 deploy 是有意设计:

source-controlled 验收脚本不应在读者以为“检查环境”时顺便收敛 package、 重启服务或重建数据库。

部署需要独立变更窗口和 runbook。

removal playbook 是 destructive action

Pigsty pgsql-rm.yml 用于移除 cluster/instance。官方文档说明 production 应显式启用 pg_safeguard,并对 override 格外谨慎。

本章只提供多重 guard 的 reset-cluster.sh,没有运行它。它要求:

exact sandbox target
exact reset token
no production data assertion
clients drained assertion
exact release/inventory
prior machine-ID allowlist
fresh passing evidence
interactive second ACK

这是演示 destructive contract,不是授权。

交付完成定义

声明式交付完成需同时满足:

source pinned
inventory reviewed and secret-safe
target identity proven
playbook rc/recap retained
live host facts pass
PostgreSQL init contract pass
Patroni/SQL/inventory topology agree
endpoint identity observable
exceptions accepted by owners
rollback/reset boundary explicit
later gates remain named

本章环境满足 sandbox L2,带六个 exception;production gate 仍 pending。


上一节:拓扑、命名与故障域 · 返回本章目录 · 下一节:实战:L2 部署验收 · 查看全书目录 · 查看索引中心

19.7 实战:L2 部署验收

这次实战不是模拟输出。本书在四台本地 Ubuntu VM 上用 exact Pigsty v4.5.0 部署 PostgreSQL 18,并执行了正式的只读验收。读者可以复跑同一 合同,但不能把硬件与 secret 路径照抄。

19.7.1 核对版本、拓扑、资源、端点和安全入口

风险分级

正常动作:

project / capture / verify / review / all
risk = L0 read-only against deployed service
local writes = selected evidence directory only

它们不会:

deploy packages
render remote config
restart/fail over service
run DDL/DML
take/restore backup
remove cluster
export secret values

reset:cluster 是独立 destructive action,不属于正常实验。

正式目标

target               pg36-l2-vagrant
platform             local Vagrant Linux sandbox
Pigsty               exact v4.5.0 tag
PostgreSQL           18.6 observed
hosts                4
PostgreSQL clusters  2
members              4
address hostname after convergence service unit observed role
10.10.10.10 pg-meta-1 pg-meta primary
10.10.10.11 pg-test-1 pg-test primary
10.10.10.12 pg-test-2 pg-test streaming replica
10.10.10.13 pg-test-3 pg-test streaming replica/offline

先读实验合同

入口:

先确认:

你控制的是 disposable local sandbox
没有生产数据/流量
inventory 是 private mode-0600 文件
四个地址可以 direct SSH
当前只执行 read-only acceptance

生产环境不能因命令“只读”就跳过访问授权;主机和数据库 fact 本身也可能是 内部信息。

准备私有输入与 evidence 路径

export PG36_CH19_INVENTORY=/absolute/private/path/pg36.yml
export PG36_EVIDENCE_DIR=/absolute/private/path/evidence/ch19-run
export PG36_SSH_USER=vagrant

要求:

inventory file mode = 0600
evidence path outside source control
inventory contains no production credential
SSH user has intended read/sudo ability on this sandbox

本章不提供正式 live inventory。公开的 inventory.example.yml 只有结构和 sentinel。

先做 secret-free projection

static/labs/ch19/task.sh project

预期:

status=projection-ok
secrets=redacted

检查:

python3 -m json.tool \
  "$PG36_EVIDENCE_DIR/inventory-projection.json"

只检查字段,不把输出粘到公共日志。关键值:

status=secret-free-projection
source.mode_octal=0600
source.secret_values_exported=0
version=v4.5.0
pg_version=18
pg_locale=C.UTF-8
host_count=4

direct SSH 避免 alias/forwarding 冒充目标

采集器固定:

ssh -F /dev/null \
    -o BatchMode=yes \
    -o UserKnownHostsFile=/dev/null \
    -o StrictHostKeyChecking=no \
    vagrant@10.10.10.10 ...

禁用本机 config 是本实验对已知 alias 陷阱的防御。StrictHostKeyChecking=no 只适用于这个 disposable、隔离 sandbox;生产必须维护可信 host key,不应 复制这个选择。

正式 evidence 保存 machine ID 的 SHA-256,不保存原值:

four 64-hex hashes
all distinct
address/hostname exact match

hash 只是避免直接扩散标识,不是强匿名化;仍应把 evidence 当内部资料。

host capture 内容

每台生成:

hosts/<address>.json

包含:

OS/kernel/architecture/virtualization
CPU/model/NUMA count
memory/swap/THP/overcommit
root and data backing filesystem/free bytes
timezone/NTP
addresses/DNS/firewall service state/listening ports
selected systemd services
selected package versions

采集器最初尝试跟随 /pg symlink,目标是 PostgreSQL-owned mode-0700 目录,非特权用户得到 PermissionError。修正后采集 /data backing filesystem,不跨越 PGDATA 权限边界。好的 audit 不应为了“读事实”扩大 权限或放松数据目录。

host acceptance 与真实例外

四台均:

Linux / Ubuntu 24.04.x / aarch64
Etc/UTC + NTP synchronized
swap = 0
THP = never
root free >= 8 GiB
required services active

资源实际值:

pg-meta-1   2 vCPU, about 3.8 GiB RAM
pg-test-*   1 vCPU, about 1.9 GiB RAM each

原设计推荐 2 vCPU/2 GiB。没有把阈值悄悄改成“全部合格”,而是:

hard disposable-sandbox acceptance floor = 1 vCPU / 1.8 GiB
recommended teaching floor                = 2 vCPU / 2 GiB
EX19-LAB-RESOURCE-FLOOR                   = accepted exception

这允许验证部署关系,禁止容量推断。

PostgreSQL capture 内容

采集器通过 direct SSH,在每个 node:

sudo -n -iu postgres \
  psql -X -qAt --dbname=postgres --set=ON_ERROR_STOP=1

输入固定 postgresql-facts.sql,不拼接 live secret, 不读取业务 row。

生成:

postgres/<address>.json

验收:

PG 18
checksums on
block 8192
WAL segment 16777216
UTF8 / builtin / C.UTF-8
Etc/UTC
SCRAM verifier setting
SSL on
SQL recovery role
system identifier relation

Patroni 与 endpoint capture

patronictl list --format=json 生成:

patroni/pg-meta.json
patroni/pg-test.json

Patroni 表中的 healthy state 并不全叫 running

leader    state=running
replica   state=streaming

REST 根端点对 replica 可能返回非 2xx,同时带有效 Patroni JSON。采集器会 保存 HTTP status,并在 body 符合身份协议时标记 reachable,而不是把所有 HTTPError 都误判为网络失败。

endpoint 检查:

5432 direct postgres
5433 primary service
5434 replica service
5436 primary direct service
5438 offline service
8008 Patroni REST

只证明 TCP/identity 可观察,不登录业务 service,也不证明路由语义。

执行完整正常实验

static/labs/ch19/task.sh all

正式输出:

status=captured
target=pg36-l2-vagrant
hosts=4
secrets=redacted
production_approval=false

status=ok
target=pg36-l2-vagrant
deployment=pigsty-v4.5.0-postgresql-18
hosts=4-distinct
topology=pg-meta-1-primary+pg-test-1-primary-2-replicas
counterexamples=9-rejected
sandbox_l2=accepted-with-exceptions
production_ch19_gate=pending
mutation=none

任何缺行都不应靠人工补成通过。

19.7.2 从 SQL、主机与 Pigsty 三侧验证同一事实

实际是四个证据面

本节标题说“三侧”,但严谨验收还包括 declaration:

inventory  declared intent
host       machine and service substrate
SQL        PostgreSQL internal fact
Pigsty     Patroni/service implementation observation

四面回答同一问题:

问题 inventory host SQL Pigsty/endpoint
target 是谁 address/group hostname/machine hash cluster name/system ID member/host
版本 pg_version package server version Patroni REST server version
当前 role bootstrap pg_role process/service pg_is_in_recovery Leader/Replica
replica healthy member declared processes/ports recovery identity streaming
locale/checksum desired vars/defaults OS context database/settings n/a
service exists definitions listeners n/a TCP/REST

某一面冲突就停止,不做多数表决。

declaration 不能替代 observation

inventory 写:

pg_version: 18

可能出现:

package install failed
old server still running
wrong inventory targeted
host variable override
partial rerun

所以 SQL 必须返回 major 18。

同理,pg_checksum: true 或 role default 只表达意图,SHOW data_checksums 才表达运行 cluster 事实。

process/service active 不能替代 SQL

systemctl is-active patroni 返回 active,仍可能:

PostgreSQL startup failed and Patroni loops
member in catchup
wrong data directory
wrong cluster
timeline divergence
port occupied by another process

因此 service state 与 SQL/Patroni identity 同时要求。

SQL recovery role 不能替代 global coordination

两台各自执行:

SELECT pg_is_in_recovery();

如果都返回 false,SQL 只能说明两台都不是 standby;不能告诉你哪一台应当 获得流量,也不能自动 fence。Patroni membership/leader、DCS 和 service health 共同揭露冲突。

反例:

declare-two-live-leaders -> E_TOPOLOGY

validator 要求每个 cluster 恰好一个 leader,并与每台 SQL recovery 状态 一致。

system identifier 连接 SQL 与 topology

假设:

三个 Patroni member 名和 address 都对
但 pg-test-3 是错误初始化的新 data directory

role/status 可能短暂看似正常。system ID 检查会拒绝:

members of one cluster have different system identifiers

另一个反例是 pg-metapg-test 共用同一 system ID,也会拒绝。

endpoint open 不等于正确 service

本章所有声明端口 reachable。这是必要条件,不是充分条件。

第 22 章还要验证:

5433 write reaches current primary
5434 is read-only and uses intended replicas
5438 selects offline members
PgBouncer pooling mode/session state
TLS/auth/database/role
drain/reconnect/failover behavior

把端口扫描写成“service verification passed”会过度声明。

source checksum 防止验收脚本漂移

capture-manifest.json 保存 capture 时每个 lab source file 的 SHA-256。 review.py 再对当前 source 计算。

这样拒绝:

先 capture
后改 requirements/validator
用旧 evidence 宣称新规则通过

修改 lab source 后必须重新 capture。

positive test 与 negative test 是两类证据

正向:

当前环境满足规则

反向:

规则会拒绝我们关心的已知坏状态

九个反例:

case expected code
sandbox 声称 production SLO E_PRODUCTION_CLAIM
两地址复用 machine identity E_HOST_IDENTITY
混用 OS E_HOST_UNIFORMITY
clock 未同步 E_CLOCK
memory 低于 hard floor E_RESOURCE_FLOOR
checksum off E_PG_INIT
两 leader E_TOPOLOGY
inventory mode 0644 E_SECRET_FILE_MODE
evidence 导出 secret value E_SECRET_EXPORT

negative-cases.json 只修改内存中的 evidence 副本,不破坏 live cluster。

单独复核已捕获 evidence

export PG36_EVIDENCE_DIR=/absolute/existing/evidence/ch19-run

static/labs/ch19/task.sh verify
static/labs/ch19/task.sh review

verify 重新运行 policy;review 检查:

source checksums
positive report identity/counts/decision
negative code set
four distinct identities
resource exception exact hosts
endpoint evidence
sanitized deployment account
production boundary
reset_executed=false

人工 review 仍不可省

自动 validator 不知道:

  • owner placeholder 是否已变成真实责任;
  • laptop 是否处于受控物理环境;
  • business classification 是否正确;
  • exception 接受者是否有权;
  • package supply chain 是否可信;
  • future production SLO 是否合理;
  • 当前证据是否用于允许的目的。

机器验证 consistency,人类承担 judgment。

19.7.3 产出基线清单、风险例外与 reset:cluster

evidence bundle

一次 all 产出:

inventory-projection.json
capture-manifest.json
hosts/
  10.10.10.10.json
  10.10.10.11.json
  10.10.10.12.json
  10.10.10.13.json
postgres/
  <four member facts>
patroni/
  pg-meta.json
  pg-test.json
endpoints.json
validation-report.json
negative-report.json
review.txt

不包含:

live inventory
password/token/private key
customer rows
full deployment private log
raw machine ID
production approval

evidence directory 应按内部运维资料管理并设置 retention。

sanitized deployment account

deployment-run.json 保存:

exact source tag/commit
generator command
inventory mode and secret export count
bootstrap/Ansible version
deploy command and safe recap
observed acceptance summary
explicit non-claims
reset_executed=false

它方便读书,不替代 fresh capture。host role、package 和 endpoint 会漂移。

六个风险例外

exception 事实 阻止的生产结论
shared hypervisor 全部 VM 共用 laptop 四个独立 failure domains
single etcd DCS 单节点 control-plane HA
single backup target MinIO/control 共置 DR independence
virtual storage 未做 IOPS/latency/durability qualification capacity/durability
inventory secrets 临时 0600 inventory production secret lifecycle
resource floor 三节点低于推荐 2C/2G sizing/concurrency

exception 必须有:

ID
scope
reason
impact
owner/acceptor
expiry/review
remediation
claim blocked

本章 JSON 记录前四类核心字段;真实生产审批系统还要补责任与到期。

验收决策

正式结果:

{
  "sandbox_l2": "accepted-with-exceptions",
  "production_ch19_gate": "pending",
  "next_gate": "ch20-ha"
}

不是:

production-ready
HA passed
RPO/RTO achieved
backup verified
capacity approved
security compliant

reset:cluster 为什么存在

可重复实验必须说明如何回到起点。但删除 cluster:

停止服务
删除 PostgreSQL data
可能删除 backup state
改变 DCS/service/monitor registration
不可由正常 validation 自动恢复

所以 reset 是 destructive exercise,不是 cleanup convenience。

本章提供 reset-cluster.sh,但正式运行没有执行:

deployment-run.boundary.reset_executed=false

reset 多重 guard

需要显式设置:

export PG36_RESET_TARGET='pg36-l2-vagrant/pg-meta+pg-test'
export PG36_CONFIRM_RESET='RESET_CH19_L2_SANDBOX'
export PG36_NO_PRODUCTION_DATA=yes
export PG36_CLIENTS_DRAINED=yes
export PG36_PIGSTY_HOME=/absolute/exact/pigsty-v4.5.0
export PG36_CH19_INVENTORY=/absolute/private/pg36.yml
export PG36_MACHINE_ID_ALLOWLIST=/absolute/private/machine-ids.json
export PG36_RESET_EVIDENCE_DIR=/absolute/new/pre-reset-evidence

然后才可能:

static/labs/ch19/task.sh reset:cluster

脚本还会:

  1. 检查 exact v4.5.0 marker;
  2. 检查 inventory mode 0600;
  3. 要求新 evidence path;
  4. 先执行 fresh read-only all
  5. 将四台当前 machine hash 与预先 review 的 allowlist 比较;
  6. 要求 /dev/tty 再输入 exact token;
  7. 先移除 pg-test,后移除 pg-meta

任何 guard 缺失,exit 77,什么都不删除。

machine allowlist 不能由 reset 当场自我批准

machine-identity-allowlist.example.json 只有 sentinel。真实 allowlist 应:

从一次已接受 evidence 提取
由 operator review address/hostname/asset
存放 source control 外
限制权限
在 VM rebuild 后重新批准

若 reset 先读取当前机器、再自动把当前值当 allowlist,identity guard 就没有 外部 authority。

不要在 production override safeguard

Pigsty removal playbook 支持 pg_safeguard。production inventory 应显式 启用保护。override 是 emergency/destructive authority,不应复制到普通 runbook、CI 或 alias。

本书脚本的 guard 也不能让 production reset 变安全:

target classification、data ownership、recovery、change approval 与现场 判断仍然优先。发现任何可能是 production,立即停止。

本章结束时保留环境

为了第 20–22 章:

do not reset
do not fail over yet
do not restore
do not benchmark to saturation

保留 accepted baseline,下一章才能比较 fault 前后。

handoff 包含:

exact source/deployment account
fresh accepted evidence
six exception IDs
known bootstrap role
system-ID relation
no reset performed
pending kernel reboot observation
production gate pending

读者自检

完成本章后,应能回答:

  1. 为什么 deployment rc=0 不等于 production ready?
  2. 哪些初始化事实必须从 SQL 读取?
  3. 为什么四个 IP 需要 machine identity?
  4. 为什么 one leader + two replica 还不能给出 HA RTO?
  5. 为什么 endpoint open 不证明 routing?
  6. 如何在不泄露 inventory 的情况下 review topology?
  7. 六个 exception 各阻止什么结论?
  8. 为什么 reset 不属于 all

若任何答案只能是“因为 Pigsty 默认这样”,还没有完成环境基线。


上一节:用声明式清单交付两个服务单元 · 返回本章目录 · 下一章:狡兔三窟:高可用拓扑与容灾目标 · 查看全书目录 · 查看索引中心