纲举目张:SLO、SOP 与组织治理
24 纲举目张:SLO、SOP 与组织治理
数据库平台最危险的状态,不是“什么都没有”,而是看起来什么都有:
却没人能回答:
本章把这些问题组织成一条可执行的治理链:
治理不是在 PostgreSQL 外面增加一层审批表。它的任务是把“正常”“异常” 和“谁有权改变什么”变成可计算、可否证、可追责的服务合同。
本章目标
完成本章后,你应当能够:
- 区分数据库实例、集群、入口、应用和用户服务;
- 为服务、数据、平台与安全隐私分别指定 accountable owner;
- 用义务而不是“金银铜”标签定义服务等级;
- 画出依赖、失效语义、值班与升级路径;
- 解释为什么
postgres_up = 1不是业务可用性; - 把 SLI 写成
good events / eligible events; - 区分 SLI、SLO、SLA、错误预算和控制目标;
- 为可用性、延迟、新鲜度选择靠近用户的测量点;
- 把正确性和恢复就绪从可平均的错误预算中分离;
- 计算事件预算、等价时间预算和 burn rate;
- 设计滚动窗口、低流量策略与不可追溯篡改的排除项;
- 用错误预算真实约束发布节奏,而不是装饰仪表盘;
- 区分 SOP、故障 Runbook、恢复演练和单次 Change Plan;
- 把变更拆成申请、评审、执行、验证、回退或前滚与关单;
- 为 L2/L3 高风险动作设计独立批准、精确目标和停止线;
- 解释双人控制、延迟确认与 break-glass 的不同作用;
- 为每个 SLI 固定数据源、查询、维度与缺失语义;
- 区分页型症状、诊断原因和容量工单;
- 使用 multiwindow、multi-burn-rate 作为可调起点;
- 让每个 page 绑定用户影响、所有者、Runbook 和首个安全动作;
- 监控监控系统自身,而不把“没有数据”解释为健康;
- 为配置、变更、访问、恢复、SLO 与事件建立证据目录;
- 区分普通日志、审计记录、决策证据与合规结论;
- 用哈希、时间、身份、最小化和保留政策建立证据链;
- 自动检查治理不变量,并用反例证明检查真正会失败;
- 对一个环境给出“合同通过、生产待决”的诚实结论。
本章不做什么
本章不会把组织设计冒充 PostgreSQL 参数调优,也不会:
- 承诺一个普适的 99.99%;
- 用书中的角色标识代替真实值班表;
- 把沙箱一次成功切换称为生产 RTO;
- 把备份任务退出码称为恢复证明;
- 把现成组件指标直接拼成业务 SLO;
- 发布真实告警、发送真实 page;
- 以“合规”为名收集密码、密钥或完整客户行;
- 让审批替代技术停止线;
- 让自动化替代业务和风险授权。
pg36_shop 在本章仍是 synthetic teaching service。数值是可计算的政策输入,
不是与真实业务 owner 谈判后的生产承诺。
前置与后续
本章收束第 19–23 章已经获得的事实:
- 第 19 章 部署基线:四机 Pigsty v4.5.0 / PostgreSQL 18 沙箱,以及六项明确例外;
- 第 20 章 高可用:计划切换、write gap、 timeline 和未知写结果;
- 第 21 章 备份体系与恢复演练:命名恢复点、WAL 覆盖、 隔离恢复与应用校验;
- 第 22 章 服务接入:入口、池化、 排队、路由与 planned role change;
- 第 23 章 数据安全: TLS、角色、RLS、轮换、审计缺口与 transaction-pool 状态。
这些章节证明了机制,但机制不会自动成为服务承诺。例如:
下一章 第 25 章 监控体系与可观测诊断 将实现本章输出的 指标和规则。先定语义、后写查询,是两章之间最重要的边界。
一张图看完整合同
任何断点都会制造假治理:
| 断点 | 表面现象 | 真正风险 |
|---|---|---|
| 无服务卡 | 大量组件指标 | 不知道为谁服务、谁决策 |
| 无 SLI 语义 | 有阈值 | 不知道分子、分母和缺失意味着什么 |
| 无预算政策 | 有 SLO 图 | 可靠性结果不影响发布决策 |
| 无动作合同 | 有 page | 值班只能临场猜测 |
| 无停止线 | 有 SOP | 文档只会推动动作,不能阻止事故 |
| 无验证 | 命令成功 | 状态是否正确仍未知 |
| 无证据边界 | 日志很多 | 不能证明目标、授权与结果,还可能泄密 |
五种容易混淆的对象
服务目录项
描述“谁向谁提供什么能力,以及依赖、等级和责任”。它不是机器清单。
SLI
一个实际测量值。例如:
$$ \text{availability SLI}
\frac{\text{good eligible order attempts}} {\text{all eligible order attempts}} $$
SLO
对某个窗口内 SLI 的目标。例如“滚动 28 天内至少 99.9%”。它不是法律赔偿 条款;后者通常属于 SLA。
控制目标
不适合用平均错误率淡化的要求。例如“没有未解释的账实不符”与“90 天内有 一次通过的隔离恢复”。一次数据串租不能因为本月另有一千万次正确读取而变得 可以接受。
错误预算政策
当可靠性好或差时,组织具体改变什么。没有后果的错误预算只是 KPI:
本章采用的目标
本章服务卡定义五个目标:
| ID | 类型 | 目标 |
|---|---|---|
SLO-AVAILABILITY |
ratio SLO | 被接纳的下单尝试得到可核对的成功结果 |
SLO-LATENCY |
ratio SLO | 被接纳的下单尝试在 250 ms 内完成 |
SLO-FRESHNESS |
ratio SLO | 带已知 commit token 的读取在 5 s 内可见 |
CTRL-CORRECTNESS |
control | 无未解释的重复、串租、金额或状态错误 |
CTRL-RESTORE-READINESS |
control | 90 天内有通过的隔离恢复证据 |
它们有意不使用:
这些都是有价值的组件事实,但只能解释服务为什么好或坏,不能单独回答用户 是否获得了正确服务。
可计算的错误预算
可用性目标为 99.9%,滚动窗口为 28 天:
若窗口内有 10,000,000 个 eligible events:
若为了直觉把比例换算成连续时间:
40.32 分钟只是等价解释。request-based SLO 的实际预算仍是事件,不应把一 小时的低流量故障与一小时的流量高峰当成同一件事。
burn rate 定义为:
$$ \text{burn rate}
\frac{\text{observed bad-event ratio}} {1-\text{SLO target}} $$
对 99.9% 目标,14.4 倍 burn 对应 1.44% bad ratio。Google SRE Workbook 给出的 multiwindow 起点是:
| route | 长窗 | 短窗 | burn | 按本章 28 天窗口约消耗预算 |
|---|---|---|---|---|
| page | 1 h | 5 min | 14.4x | 1 h 内 2.14% |
| page | 6 h | 30 min | 6x | 6 h 内 5.36% |
| ticket | 3 d | 6 h | 1x | 3 d 内 10.71% |
这是起点,不是常数。实际规则必须按流量、后果和 notification cost 调整。 原始推导见 Google SRE Workbook 的 Alerting on SLOs。
观察与告警的边界
Pigsty v4.5 提供:
PostgreSQL、PgBouncer、HAProxy、Patroni 和主机事实通过 cls、ins、ip
等身份维度关联。当前实现说明以 Pigsty
Monitoring System 与
PGSQL Monitoring 为准。
但平台不能凭空产生业务语义。第 25 章还需要实现:
这些名称是本书的应用合同,不是 Pigsty 当前内置指标。将来改名可以,改变 eligible/good/missing 语义则必须重新评审 SLO。
四类操作文档
| 文档 | 何时使用 | 核心区别 |
|---|---|---|
| SOP | 可重复的日常动作 | 已知输入、稳定步骤、例行验证 |
| Incident Runbook | 症状已经发生 | 先保安全、边诊断边决策 |
| Recovery Drill | 证明恢复路径 | 隔离、预设验收、保留计时与结果 |
| Change Plan | 一次具体变更 | 精确目标、窗口、版本、批准与回退 |
“切主 SOP”这个名称可能掩盖两种完全不同的动作:
不能因为两者最终都出现“新主库”,就复用同一套前提和停止线。
高风险动作的基本不变量
本章把动作分成 L0–L3:
| 等级 | 典型动作 | 基本要求 |
|---|---|---|
| L0 | 只读观察、验证证据 | 精确目标,不改变远端状态 |
| L1 | 有界、可逆、低影响变更 | 预览、验证、回退 |
| L2 | 权限、模式、池或运行态变更 | 独立批准、停止线、证据 |
| L3 | 删除、恢复、拓扑和大影响动作 | 双人控制、延迟确认、强制门禁 |
L2/L3 使用不同的 requester、approver 与 executor。双人控制并不是两个人 盯着同一条未核对的命令按回车;合格的独立批准人必须检查:
- 目标是否精确;
- 影响半径是否可信;
- 前置事实是否新鲜;
- 动作是否与已批准 artifact hash 一致;
- stop condition 是否机器可判定;
- 回退/前滚是否真的可执行;
- 未知结果是否会被错误重试。
NIST SP 800-53 Rev. 5.1 的变更访问限制包含 dual authorization。它是风险控制的参考,不意味着每个 组织必须机械照搬同一审批流。
正式实验
本章提供一套纯 L0 实验:
正式运行:
重新执行第 19 章只读 gate 后,四台主机与四个 PostgreSQL member 仍通过
accepted-with-exceptions;六项沙箱例外没有被隐藏。
形式化验证故意尝试:
- 宣称 production SLO;
- 删除 platform owner;
- 把 process alive 当服务健康;
- 把目标改成 100%;
- 排除计划维护;
- 把缺失数据当健康;
- 让 exhausted budget 没有后果;
- 给指标加入未约束客户标识;
- 接受没有动作的 page;
- 让 cause/capacity 直接 page;
- 把 backup exit 0 当恢复证明;
- 让一人自批自执行;
- 让 break-glass 跳过目标与证据;
- 允许 evidence 保存 secret;
- 伪造上游 restore run id;
- 把一次沙箱切换称为生产证明。
二十个变体全部被拒绝。实验没有部署告警,也没有联系真实值班人。
本章目录
24.1 服务目录与责任模型
24.2 SLI、SLO 与错误预算
24.3 SOP、Runbook 与变更治理
24.4 观察与告警契约
- 24.4.1 每个 SLI 的数据源、查询、维度和缺失语义
- 24.4.2 告警必须绑定用户影响、首个安全动作和所有者
- 24.4.3 症状告警、原因告警与容量预测分开
- 24.4.4 产出供 ch25 实现的告警规则清单
24.5 证据、审计与合规
24.6 实战:把 pg36_shop 纳入服务治理
权威资料
- Google SRE Workbook: Implementing SLOs、 Alerting on SLOs
- Prometheus: Alerting practices、 Alerting rules
- PostgreSQL 18: Monitoring Database Activity
- Pigsty v4: Monitoring System、 PGSQL Monitoring
- NIST: SP 800-53 Rev. 5.1
这些资料提供方法和组件事实;pg36_shop 的服务语义、阈值和治理政策仍由
本章合同负责。
章末验收
不要用“文档已经发布”验收本章。应当能回答:
- 能否从一次用户动作追到 eligible/good 事件定义?
- 能否指出这个定义在哪个测量点实现?
- 没有数据时,系统是 unknown、failed 还是有独立 fallback?
- 正确性错误是否会被平均比例掩盖?
- 计划维护是否仍反映在用户体验中?
- 错误预算状态是否改变变更权限和速度?
- 每个 page 是否有 owner、runbook、第一安全动作和恢复验证?
- cause metric 是否只用于诊断,而不会重复 page?
- capacity 是否形成有期限的 owned ticket?
- 高风险动作能否被同一身份申请、批准和执行?
- break-glass 是否仍绑定目标、时限、证据和轮换?
- backup success 之外,是否有隔离恢复与应用验收?
- evidence 是否能证明 source、target、time、collector 和 hash?
- evidence 是否明确不保存 secret 与不必要的个人数据?
- 能否运行反例并看到 validator 真实拒绝?
- 是否清楚哪些结论仍然
production pending?
如果这些问题没有答案,再多告警、审批单和仪表盘也只是组织噪声。
上一章:固若金汤:认证、授权与数据安全 · 返回下卷导读 · 下一章:望闻问切:监控体系与可观测诊断 · 查看全书目录 · 查看索引中心
24.1 服务目录与责任模型
PostgreSQL 集群是技术对象,服务是责任对象。
一个集群可以承载多个服务;一个服务也可以依赖多个数据库、队列和外部接口。 若资产目录只记录 IP、实例和版本,事故发生时仍不知道谁能决定:
- 是否暂停下单;
- 是否允许读到旧数据;
- 是否执行 failover;
- 是否接受恢复到某个时间点;
- 是否通知受影响用户;
- 是否冻结发布;
- 谁能关闭事件。
本节先把这些决定分配给职责,再讨论服务等级、依赖与健康证明。
24.1.1 服务所有者、数据所有者与平台所有者
owner 不是“出问题时帮忙的人”
owner 是对某类决定最终负责的职能。它可以把任务委托给执行者,但不能把 accountability 变成“大家共同负责”。
本章采用四个稳定职能:
| 职能 | 最终负责什么 | 不应独自决定什么 |
|---|---|---|
| service owner | 用户旅程、SLO、发布优先级、业务降级 | 数据含义、底层恢复细节 |
| data owner | 数据语义、质量、保留目的、隐私分类 | 集群拓扑与数据库执行步骤 |
| platform owner | PostgreSQL/Pigsty、容量、备份恢复、平台执行 | 用户可接受的损失与业务优先级 |
| security/privacy | 访问政策、安全例外、证据披露、隐私升级 | 正常业务功能取舍 |
这四者不是四个超级管理员。理想状态恰恰相反:
权限和责任要能分开。第 23 章已经把 login、runtime、owner 与 break-glass 角色拆开;本章把组织责任也做同样的拆分。
service owner
service owner 负责从用户视角定义合同:
它不能只写“数据库可用”。若应用在 PostgreSQL 正常时返回 500,用户仍然 没有获得服务。
service owner 还要在错误预算耗尽时决定:
- 暂停哪些功能发布;
- 哪些可靠性工作优先;
- 是否启用产品降级;
- 哪些业务风险值得例外。
这些决定必须与 platform owner 共同评审技术可行性,但不能完全下放给 DBA。
data owner
data owner 负责说明“一行数据代表什么”和“错误意味着什么”:
恢复时,platform owner 可以证明 PostgreSQL 启动、system identifier 正确、 恢复目标已经到达;data owner 仍要判断:
- 目标时间是否符合业务容损;
keep应当存在、discard应当不存在是否足够;- 订单、库存和支付是否需要跨系统核对;
- 哪些差异要停止业务而不是继续恢复。
因此“数据库恢复成功”与“业务恢复可接受”是两个验收签名。
platform owner
platform owner 维护的是可交付数据库服务:
- Pigsty inventory 和渲染产物;
- PostgreSQL 初始化与运行参数;
- Patroni/etcd 角色与控制面;
- HAProxy/PgBouncer 入口;
- pgBackRest/WAL 恢复路径;
- 容量、升级、安全基线;
- 指标、日志、告警与值班执行。
它必须能把平台事实翻译成服务后果。例如:
平台 owner 也不能用“基础设施都绿”关闭事故。关闭条件应来自用户 SLI、 数据核对与恢复验证。
security/privacy owner
数据库治理产生敏感证据:
- 谁请求、批准和执行;
- 哪个身份获得了什么权限;
- 哪条查询失败;
- 哪个租户或事件受影响;
- 备份和日志保留多久;
- incident evidence 向谁披露。
security/privacy owner 决定访问、例外和披露边界。它不意味着把所有日志永久 保存。收集过多原始 SQL、bind value 或客户行,会让“审计系统”变成新的数据 泄露面。
incident commander 不是第五个永久 owner
incident commander 是事件期间的协调角色:
它可以由 service owner 职能担任,也可以按事件规模另行指定。技术 lead 负责执行和验证,不应同时独占业务影响判断、通信和所有批准。
使用职能身份,不把人名写死在合同里
服务卡中的:
描述稳定接口。真实系统再把 role_id 映射到:
- 身份目录 group;
- 当前 primary/secondary on-call;
- 可达的通知渠道;
- 时区与交接;
- 替补和 manager escalation。
只写“找小王”有三个问题:
- 人员变化会让文档瞬间过期;
- 不知道非工作时间找谁;
- 个人名字不能证明权限和当班状态。
反过来,只写一个永远没人测试的 db-oncall@example 也不算 owner。路由必须
通过 notification canary 定期证明可达。
不要把 RACI 做成责任迷宫
RACI 可以帮助列出 Responsible、Accountable、Consulted、Informed,但最常见 的失败是每个格子都填满:
对一个决定,应当尽量有一个最终 accountable function:
| 决定 | A | R | C |
|---|---|---|---|
| 修改用户 SLO | service | reliability analyst | platform/data |
| 执行 planned switchover | platform | qualified operator | service |
| 接受恢复点 | data | platform recovery lead | service/security |
| 暂停下单 | service | application operator | data/platform |
| 批准高权访问 | security/privacy | identity admin | platform/data |
“A” 不是可以跳过技术门禁的权力。service owner 可以要求恢复服务,但不能 要求 platform operator 在 fencing 未知时强制 promote。
所有权的验收问题
每个 owner 都应回答三类问题:
若答案只有“出了事群里讨论”,责任模型尚未建立。
24.1.2 等级、规格、依赖、值班与升级路径
服务等级是一组义务
gold、silver、bronze 如果没有义务,只是颜色:
可执行的 tier 应当打包:
| 维度 | 需要固定的内容 |
|---|---|
| availability | 用户 SLI、目标、窗口、预算政策 |
| recovery | 恢复类型、证据新鲜度、演练频率 |
| support | 值班时段、确认目标、升级链 |
| change | 风险级别、批准、窗口、冻结规则 |
| capacity | headroom、预测周期、扩容 lead time |
| security | 认证、加密、轮换、审计与例外 |
| lifecycle | PostgreSQL/Pigsty 版本和升级时限 |
| evidence | 保留期、访问、完整性与删除 |
本章的 sandbox-reference 明确写:
这比给沙箱贴上“Silver”诚实得多。
规格不是只有 CPU 和内存
数据库 service specification 至少有四组:
用户规格
- 关键旅程;
- 可用性、延迟、正确性、新鲜度;
- 写后读、一致性和降级行为;
- 支持时段和沟通。
数据规格
- 数据分类;
- 主权和保留;
- 容许丢失和恢复粒度;
- tenant/authorization 边界;
- 核对不变量。
技术规格
- PostgreSQL major 与扩展;
- 拓扑、同步策略、入口;
- pool mode 与连接预算;
- 备份、WAL 和恢复设施;
- 监控、日志和依赖。
运营规格
- owner/on-call;
- SOP 和演练;
- 变更冻结;
- 证据与审计;
- 已接受例外和到期日。
只记录 4C16G500G,不能回答任何恢复、安全和服务问题。
依赖必须写 failure semantics
依赖表不能止于“uses PostgreSQL”:
| 依赖 | 关系 | 失败语义 |
|---|---|---|
| application runtime | 发出用户事件、提交事务 | PG 健康时应用仍可拒绝或错误提交 |
| HAProxy/PgBouncer | 承担入口与池化 | 直连主库成功不代表应用入口成功 |
| PostgreSQL | 事务与持久状态 | 连接成功不代表业务事务正确 |
| Patroni/etcd | 角色和切换控制 | 控制面受损可先阻止安全切换 |
| pgBackRest/WAL | 恢复路径 | backup exists 不代表可恢复 |
| Victoria stack | 观察和告警 | telemetry missing 会把状态变成 unknown |
failure semantics 回答:
例如 etcd 暂时不可用时,现任 primary 可能继续服务;真正受损的是安全 role change 能力。若把“etcd member down”直接等同于用户 outage,会制造误报; 若完全忽略,又会在需要 failover 时才发现控制面失效。
把共享故障域写进依赖
第 19 章的四台 VM 看起来有四个地址,但共享:
- 一台 laptop;
- 一个 hypervisor;
- 一套供电和网络;
- 同一底层存储;
- 单节点 etcd;
- 单一 MinIO/control host。
因此拓扑图中的三节点不能推出三个独立 failure domain。依赖模型应同时表达:
这也是为什么服务卡保留 known production gaps,而不是把它们埋在实验日志里。
值班路径需要输入与输出合同
一条值班 route 至少固定:
确认时限不是修复时限:
ack 只证明有人接管。恢复目标必须按故障类别、数据边界和服务合同另行定义。
escalation 不是把同一消息抄送更多人
有效升级改变权力、资源或沟通:
升级触发可以来自:
- 影响范围扩大;
- 数据正确性或保密性受威胁;
- 错误预算快速燃烧;
- 操作超过停止线;
- recovery path 不确定;
- 事件超过时间阈值;
- 现有 authority 不足。
不要只按“CPU 超过 90%”升级。CPU 是原因候选,不是业务严重度。
依赖与升级的服务卡片段
本章完整文件见
service-card.json。其中一项依赖:
owner_function 负责第一技术响应;最终恢复点是否可接受仍需要 data/service
owner。
服务目录的最小字段
每次更改服务卡,应检查对应的 SLO、告警 route、SOP 与权限是否一起更新。
24.1.3 实例健康不等于业务服务健康
六层证明
本章使用六层健康链:
没有任何一层单独足够。服务健康还必须满足用户合同。
process alive 只证明进程事实
systemctl is-active postgresql 或 exporter 的 up 指标能证明采集时进程存在。
它不能证明:
- postmaster 接受连接;
- 监听的是正确地址;
- HBA 允许目标身份;
- server 是正确集群;
- 当前节点可写;
- 磁盘还有空间;
- transaction 能提交;
- 应用 SQL 正确。
进程指标适合组件诊断和自动重启,不应直接成为业务 availability 分子。
pg_isready 也有边界
pg_isready 检查 PostgreSQL server 的连接状态,返回 accepting、rejecting
或 no response。它不需要提供正确用户名、密码或数据库才能获得 server
status;错误参数还可能在服务器日志留下失败连接。官方边界见
pg_isready。
因此:
SELECT 1 证明得更多,但仍不够
使用真实入口、真实 TLS 约束、合成身份执行:
能证明:
- 网络和 PostgreSQL 协议;
- 认证;
- database/role 身份;
- 当前节点 recovery 状态;
- 简单 query path。
它仍然绕过:
- 业务表;
- lock/constraint/RLS;
- write permission;
- commit;
- connection pool session semantics;
- 应用序列化和错误映射。
transaction probe 必须处理未知结果
更接近用户的 probe:
连接在 COMMIT 附近断开时,客户端可能不知道事务是否已经提交。直接重试会
造成重复。probe 和 Runbook 都必须使用幂等 token 核对结果。
第 20、22 章的切换演练正是按:
分类,而不是把所有连接错误都计为“数据库没写入”。
correctness 不能由事务成功代替
下面都可能 commit 成功:
- 订单写到了错误 tenant;
- 金额舍入错;
- 同一个 idempotency token 生成两张订单;
- RLS context 泄漏;
- 状态从
cancelled跳回paid; - 应用返回失败但后台已提交。
所以 good availability event 必须包含结果核对,correctness control 还要独立 运行领域 reconciliation。
PostgreSQL constraint、unique index、foreign key、RLS 与 transaction isolation 是防线,不是所有业务正确性的完整证明。
replica “时间延迟”会在空闲时误导
常见查询:
在没有新 WAL 的空闲系统中,这个差值会持续增大,即使 replica 已完全追平。 它回答“最后一个已 replay transaction 的时间距现在多久”,不自动等于当前 replication lag。
对写后读新鲜度,应使用:
诊断时再结合:
- primary 与 replica WAL LSN distance;
pg_stat_replication;pg_stat_wal_receiver;- replay pause/conflict;
- route 选择;
- 客户端时钟。
PostgreSQL 18 的统计视图目录见 Monitoring Database Activity。
cluster healthy 也不等于 entrypoint healthy
可能出现:
因此健康检查要从用户网络和声明入口执行。直接 SSH 到 primary 后 psql 成功
只能作为绕过入口的诊断事实。
Pigsty 提供组件视角,不替应用发明 good event
Pigsty 能关联:
| 层 | 典型事实 |
|---|---|
| host | CPU、memory、disk、network、kernel |
| PostgreSQL | activity、transactions、locks、WAL、I/O |
| PgBouncer | clients、servers、wait、pool |
| HAProxy | frontend/backend/session/health |
| Patroni/etcd | role、timeline、control state |
| pgBackRest | backup/check/log |
| observability | scrape、rule evaluation、notification |
cls、ins、ip 让这些事实能关联到同一个 Pigsty 对象。应用仍需要提供:
- operation class;
- eligible/good outcome;
- user latency;
- commit token;
- domain reconciliation;
- service/environment。
不要在时序标签里放 order_id、tenant_id、完整 SQL 或 error message。它们会
造成无界 cardinality,也可能泄露数据。
三个判断练习
情形 A:一台 replica down,用户指标正常
处理:
- 不必重复 page 所有业务 owner;
- platform ticket 或受控事件;
- 检查另一 replica、WAL 保留与恢复能力;
- 若 redundancy policy 已触及耐久性停止线,可升级。
情形 B:三台 PostgreSQL 都绿,下单 100% 失败
处理:
- page service symptom;
- 检查应用 release、identity、pool entry、schema compatibility;
- 不要因为 PG dashboard 绿色而关闭;
- platform 指标用于定位原因。
情形 C:请求 99.999% 成功,但出现一条跨租户数据
处理:
- 冻结相关写入;
- 保留 reconciliation boundary;
- security/privacy 与 data owner 加入;
- 不能用充足错误预算继续发布。
健康矩阵
| 观测 | 可以证明 | 不能证明 |
|---|---|---|
| process active | daemon 存在 | endpoint/auth/transaction |
pg_isready |
server 接受连接尝试 | 身份、业务 SQL |
authenticated SELECT 1 |
入口、认证、query | write/commit/correctness |
| write token visible | 某次事务与可见性 | 全量业务正确 |
| reconciliation zero | 已覆盖不变量未见差异 | 未覆盖规则、未来恢复 |
| restore drill | 某份证据下可恢复 | 所有故障、生产 RTO |
| user-event SLI | 窗口内用户结果 | 根因 |
这张表说明为什么需要两套视角:
服务卡审查清单
-
service_id是否稳定且不等于某台机器? - 是否列出关键 user journey 与 operation class?
- service/data/platform/security 是否各有明确 accountability?
- owner 是否映射到可测试 route,而不是个人名字?
- tier 是否列义务,而不是颜色?
- 每个依赖是否写 failure semantics 和 owner?
- logical redundancy 与 physical failure domain 是否分开?
- severity 是否来自用户、数据和恢复后果?
- instance、cluster、endpoint 和 service health 是否分开?
- synthetic probe 是否使用真实入口、身份和业务路径?
- commit 附近的 unknown outcome 是否可核对?
- correctness 和 durability 是否有独立 control?
- known production gaps 是否在服务卡而非某个聊天记录里?
完成这张服务卡后,下一节才能计算 SLO。否则百分比没有分子、分母、责任人或 决策后果。
返回本章目录 · 下一节:SLI、SLO 与错误预算 · 查看全书目录 · 查看索引中心
24.2 SLI、SLO 与错误预算
“数据库必须高可用”不是 SLO。它没有说明:
- 测什么;
- 从哪里测;
- 什么算好;
- 什么进入分母;
- 多长窗口;
- 允许多少失败;
- 没有数据怎么办;
- 未达标后改变什么。
可执行的 SLO 至少是:
本节从用户事件开始,而不是从 PostgreSQL exporter 已经有什么指标开始。
24.2.1 可用性、延迟、正确性与数据新鲜度
先分清五个术语
| 术语 | 含义 | 例子 |
|---|---|---|
| SLI | 实际测量值 | 28 天 good/eligible = 99.93% |
| SLO | SLI 的内部目标 | 28 天至少 99.9% |
| SLA | 对外承诺与后果 | 未达 99.9% 触发服务补偿 |
| error budget | 目标允许的 bad events | 0.1% eligible events |
| control objective | 不适合平均掉的控制 | 无未解释数据差异 |
SLA 可能引用 SLO,但两者不是同义词。本章只设计内部服务治理合同,不起草 法律或商务条款。
ratio SLI 的基本形式
Google SRE Workbook 建议优先把 SLI 写成 good events 与 total/eligible events 的比率,因为它自然落在 0–1,并能与预算相连:
$$ \text{SLI}
\frac{\text{good events}} {\text{eligible events}} $$
关键不是公式,而是 event classification。
可用性:成功必须能核对
本章的下单可用性:
事件分类:
| 情形 | eligible | good | 原因 |
|---|---|---|---|
| 合法请求,提交并返回成功 | 是 | 是 | 完整成功 |
| 合法请求,应用 500 | 是 | 否 | 用户失败 |
| 合法请求,连接在 commit 后断开,尚未核对 | 是 | 否,直到核对 | 不把 unknown 假装成功 |
| 合法请求,返回成功但查不到订单 | 是 | 否 | 错误承诺 |
| 合法请求,创建两张订单 | 是 | 否 | 幂等性失败 |
| 语法不符合已发布 API | 否 | 不适用 | 未被服务接纳 |
| 正确拒绝无权限身份 | 否 | 不适用 | 安全合同正常工作 |
| 应有权限却因配置错误被拒绝 | 是 | 否 | 服务失败,不是“安全排除” |
| 服务接纳后客户端取消 | 是 | 依实际结果 | 不能事后从分母删除 |
HTTP 2xx、SQL 无异常或 transaction commit 任一单独都不够。good event 应当
表达用户获得的承诺。
不要用“成功查询数”混合所有操作
高流量健康查询会淹没低流量关键操作:
所以 operation class 必须按后果拆分:
place-order;read-own-order;admin-report;background-reconcile。
不能为得到好看的总数,把不同用户旅程放入同一个分母。
延迟:问“多少事件够快”,不要只看平均值
本章延迟目标:
相应 SLI:
$$ \text{latency SLI}
\frac{ \left|\left{e \in E: good(e) \land latency(e) \le 250\text{ ms} \right}\right| }{ |E| } $$
其中 是所有 eligible events;快速失败虽然耗时很短,也不进入分子。
快速失败不应自动成为 latency good。若一个请求 5 ms 返回 500,它在用户旅程 上既不可用,也没有完成目标动作。
平均延迟会掩盖尾部:
平均值看似低于 250 ms,但最慢用户等了十秒。比例 SLO 或分位数更能表达尾部。 用于预算时,固定阈值的 good/eligible 比例比“p99 的月平均”更容易严格累计。
histogram 边界必须在采集前确定
若 histogram 没有 0.25 秒 bucket,事后无法从聚合数据精确回答“多少请求
低于 250 ms”。因此 SLO 先于 instrumentation:
这正是本章先写观察契约、下一章才实现指标的原因。
正确性:不要用 99.9% 原谅数据错误
某些正确性可以定义为比例,例如非关键搜索结果质量。但本章的订单不变量:
- 一个 idempotency token 只对应一张订单;
- tenant A 看不到 tenant B;
- 金额和状态迁移合法;
- acknowledged order 不丢失;
使用 control objective:
这不是声称软件永远不会出错。它是规定“发现一条此类错误时,组织不能用剩余 错误预算继续正常发布”。
正确性 control 需要:
- 明确覆盖哪些 invariant;
- 固定 input boundary;
- 记录 job/version/query hash;
- 区分 known exception 与 unexplained mismatch;
- 防止 repair 覆盖原始证据;
- 定期证明 reconciliation 本身仍运行。
数据新鲜度:必须与某次 commit 关联
“replica lag 小于 5 秒”有至少三种含义:
- 接收 WAL 与 primary 的 byte distance;
- replay 记录的 timestamp 距当前 wall clock;
- 用户的一次已提交写入在读取路径上何时可见。
用户关心第三种。前两种用于诊断。
本章 freshness event:
流程:
这样能覆盖:
- WAL 传输和 replay;
- HAProxy route;
- PgBouncer;
- query path;
- cache;
- 应用序列化。
now() - pg_last_xact_replay_timestamp() 在空闲时会增长,不能替代这个 probe。
恢复就绪不是可用性 SLO
服务连续运行 28 天,不代表能从灾难恢复。恢复 control:
backup completed 只能作为输入证据。第 21 章已经证明:
缺任一阶段,不能把 recovery readiness 标成 good。
四类目标的组合
| 目标 | 用户问题 | 主要测量 | 失败后果 |
|---|---|---|---|
| availability | 能否完成动作 | outcome counter + reconciliation | consume budget/page |
| latency | 是否足够快 | edge histogram | consume budget/page |
| freshness | 已提交状态何时可见 | commit-token probe | consume budget/route change |
| correctness | 数据是否可信 | invariant reconciliation | freeze/page |
| restore readiness | 能否恢复 | isolated drill evidence age | block risky change |
一个绿色目标不能抵消另一个红色 control。
24.2.2 测量点、统计窗口与排除条件
测量点越靠近用户,覆盖越完整
一条请求的观测点:
| 测量点 | 能覆盖 | 看不到 |
|---|---|---|
| PostgreSQL | SQL、transaction、lock、I/O | gateway/app/network errors |
| PgBouncer | pool wait、server assignment | app correctness |
| application | user operation、business outcome | client edge/network |
| gateway | user-visible HTTP result | commit correctness unless correlated |
| client/synthetic | end-to-end experience | 所有真实用户分布 |
通常使用:
若应用不能立刻增加指标,可以暂用 synthetic probe,但要记录 coverage gap。不要 因为 PostgreSQL 指标更容易获得,就悄悄把 SLO 测量点向内移动。
事件记录与时序指标各有用途
原始事件适合核对:
时序 counter/histogram 适合在线聚合与告警。不要把每个 order id 放进 metric label;需要 drill-down 时,用受控 event/log store 通过 correlation id 连接。
一个教学用关系表:
生产上不一定用 PostgreSQL 保存所有事件;这里用 SQL 展示语义。
过去 28 天:
good IS NULL 若表示 unknown,预算查询应暂按 bad 或单独阻断,而不能由
count(*) FILTER (WHERE good) 自动消失后仍声称完整。
延迟:
这里要求 availability good 且足够快,避免快速错误通过延迟目标。
滚动窗口与日历报告是两件事
本章采用滚动 28 天:
优点:
- 每天都代表同样长度;
- 没有月初“预算重置”错觉;
- 适合持续 alert/budget decision。
28 天是四周,不是自然月。财务、客户或合规可能要求 calendar month/quarter 报告,应单独生成,不要把两个窗口混成一个数字。
推荐节奏:
Google SRE 的 SLO 实施章节也讨论四周滚动窗口、周汇总和季度报告。这些是实践 起点,不是对所有组织的强制周期。
长窗口防止遗忘,短窗口缩短响应
只有月窗:
- 稳定但反应慢;
- 一次快速事故可能在总体比例中暂时不显眼。
只有 5 分钟窗:
- 反应快;
- 流量低时一个错误就剧烈波动;
- 容易被短暂 blip 打扰。
multiwindow 同时要求长窗和短窗超过同一 burn threshold:
这比简单 error_rate > 1% for 5m 更贴近 SLO 后果。
排除条件应在事件发生前定义
本章默认排除:
- 在 admission 前被正确拒绝的语法错误;
- 正确拒绝的越权身份;
- 服务接纳工作前的客户端取消。
默认不排除:
- 计划维护;
- 发布导致的错误;
- 内部依赖故障;
- operator mistake;
- 容量不足;
- “已知问题”;
- 未核对的 unknown result。
原因很简单:用户并不会因为中断有 change ticket 就得到服务。
若产品真的有公开维护窗口,应在服务合同中定义独立承诺,而不是事故后从事件 表删除数据。
例外必须不可追溯篡改
一个 SLO exception 至少记录:
禁止:
这种做法破坏了 SLO 作为决策工具的价值。
telemetry 缺失不能等于零错误
PromQL 查询经常在 series 消失时返回空向量。若 dashboard 把空值渲染为 0, 会出现:
每个 SLI 必须定义:
- expected series cadence;
- no-traffic 与 missing telemetry 如何区分;
absent()或 freshness check;- 独立 synthetic fallback;
- metamonitoring route。
本章统一原则:
低流量服务不能照搬高流量阈值
若 5 分钟只有两个请求,一个失败就是 50% error rate。可以:
- 运行 end-to-end synthetic probe;
- 延长窗口;
- 合并后果相同的 operation,但不能用无关流量稀释;
- 使用明确评审的 time-based 或 user-minute SLI;
- 对关键 batch 用“最近两次应完成周期”定义 freshness;
- 将零容忍 correctness control 独立出来。
降低告警灵敏度不能变成不观察低流量关键路径。
标签维度既要可诊断,也要有界
建议:
谨慎:
禁止无界:
Pigsty 组件关联使用 cls、ins、ip;应用 SLI 使用 service 维度。不要把
实例 identity 当成服务 identity。
24.2.3 错误预算如何约束变更速度
预算不是允许主动制造错误
目标 99.9% 不表示可以计划使用 0.1% 伤害用户。预算用于在可靠性与变化之间 做有数据的选择:
100% 通常不是合适的 ratio target:
- 它没有 error budget;
- 任一测量噪声都成为违约;
- 团队会隐藏错误或停止变化;
- 它仍不能保证 correctness 和 recovery。
“永不丢数据”这样的要求应拆成 durability/control design,而不是伪装成 100% request ratio。
事件预算
若目标 $T$,eligible event 数 $N$:
本章:
观察到 7,500 bad:
剩余 25%,进入 constrained 边界。
等价时间预算
时间可用性解释:
28 天、99.9%:
不要把它用于篡改 event-based 结果。高峰五分钟可能消耗的用户失败数远大于 低谷四十分钟。
burn rate
$$ \text{burn}
\frac{\text{observed bad ratio}} {1-T} $$
99.9% 的 allowed bad ratio 是 0.001:
| observed bad | burn |
|---|---|
| 0.05% | 0.5x |
| 0.10% | 1x |
| 0.60% | 6x |
| 1.44% | 14.4x |
| 10% | 100x |
1x 持续整个窗口恰好用完预算;14.4x 若持续,会非常快地耗尽。
为什么 fast burn 使用 AND
可用性 page:
长窗证明这不是一个无关紧要的点;短窗证明问题仍在。若使用 OR:
- 长窗已受历史事故影响但当前恢复,仍持续 page;
- 短窗单个噪声也会 page。
具体查询和 for 将在第 25 章实现。
四状态政策
本章 policy:
healthy:剩余大于 50%
- 正常评审发布;
- 仍要投资预防性可靠性;
- 不能因为预算充足跳过变更控制。
watch:25%–50%
- 减少并发 L2/L3;
- 每周分析最大预算消费者;
- 提前安排修复。
constrained:0%–25%
- 暂停非必要高风险功能;
- material change 需 service + platform 共同例外;
- 可靠性修复优先;
- 加强观察窗口。
exhausted:小于或等于 0
- 冻结非紧急高风险变更;
- 打开 incident 或 reliability review;
- 只允许直接恢复可靠性、安全、法律或紧急动作;
- 恢复节奏需要记录共同决定。
policy 必须说明哪些变化仍能进行
“冻结所有变更”可能阻止修复。应按意图和风险判断:
| 变化 | exhausted 时 |
|---|---|
| 新推荐功能 | 通常冻结 |
| 无关 UI 文案 | 可按低风险政策评审 |
| 修复当前错误原因 | 允许,但仍需安全门禁 |
| 安全凭据紧急轮换 | 允许,不能跳过证据 |
| 扩容避免迫近 outage | 允许,需验证与回退 |
| 重新定义 eligible 排除错误 | 禁止 |
| 降低 SLO 让报表变绿 | 先与 stakeholder 重新谈判,不能追溯 |
错误预算影响速度,不取消风险管理。
预算的所有者
不能由开发团队单方面改分母,也不能由 DBA 单方面降低服务目标。
防止四种 gaming
事后改变 eligibility
错误发生后把它归类为“不算用户请求”。
把事故改名 maintenance
变更单不能让用户中断消失。
把测量点移到内部
gateway 错误很多时,改看 SELECT 1。
用大流量稀释关键路径
把失败的下单和成功的健康检查合并。
防线:
- versioned SLO policy;
- event classifier tests;
- before/after counts;
- independent approval;
- immutable exception;
- change log;
- 定期抽样原始结果。
从 SLO 到变更门禁
变更请求要读取当前预算:
门禁输入必须固定时间:
审批后若 fast-burn page 触发,执行器应自动停止,而不是拿旧截图继续。
本章 SLO 文件
完整机器可读政策:
重点不是 JSON 语法,而是同一 objective id 在三份文件中保持一致:
validator 会拒绝 missing objective、100% target、planned-maintenance exclusion、 missing=healthy 和 exhausted-with-no-action。
SLO 评审清单
- journey 与 operation class 是否明确?
- eligible 是否能由代码或查询判定?
- good 是否包含用户真正获得的结果?
- unknown outcome 如何核对?
- 快速失败会不会错误通过 latency?
- correctness 是否被独立 control 保护?
- freshness 是否与 commit token 关联?
- restore readiness 是否来自恢复而非 backup job?
- 测量点是否尽可能靠近用户?
- rolling window 与 calendar report 是否分开?
- exclusion 是否事先定义、不可追溯?
- missing telemetry 是否变成 unknown?
- 低流量是否有 synthetic 或其他明确策略?
- event budget 算术是否有自动测试?
- budget state 是否改变变更行为?
- 是否禁止用改分母、改测量点和混流量 gaming?
- 目标是否由真实 stakeholder 批准,还是仍为教学输入?
当这些问题有答案时,SLO 才能驱动告警和变更。下一节把这些决定放进可执行 操作流程。
上一节:服务目录与责任模型 · 返回本章目录 · 下一节:SOP、Runbook 与变更治理 · 查看全书目录 · 查看索引中心
24.3 SOP、Runbook 与变更治理
一份只有命令的文档,会让操作更快,却不一定更安全:
它没有回答:
- 这是哪种故障或变更?
- 当前事实符合文档前提吗?
- 操作者有权改变哪个目标?
- 影响哪些用户和数据?
- 哪一步必须停?
- 命令超时后是失败、成功还是未知?
- 如何证明结果,而不是只看 exit code?
- 应当 rollback 还是 roll forward?
- 哪些证据可以保留,哪些 secret 不能导出?
可执行文档必须同时是动作合同、停止合同和证据合同。
24.3.1 日常操作、故障处置与恢复演练
四类文档解决不同不确定性
| 文档 | 环境 | 不确定性 | 主要形态 |
|---|---|---|---|
| SOP | 日常、重复 | 输入和结果基本已知 | 线性步骤 |
| Incident Runbook | 已有症状 | 原因和状态未知 | 分支与决策点 |
| Recovery Drill | 受控演练 | 恢复路径能否工作未知 | 假设、计时、验收 |
| Change Plan | 一次变更 | 版本、目标、窗口特定 | 精确 artifact 与批准 |
同一个动作可以出现在不同文档中,但语境不同。
例如 credential rotation:
不能把 routine SOP 原样用于凭据泄露事故,因为 rollback 到旧 secret 在事故中 不是安全选择。
SOP:稳定输入下的可重复流程
一份合格 SOP 至少包含:
SOP 中最重要的句子往往是:
而不是最长的命令。
Runbook:从症状出发,而不是假设根因
告警是:
Runbook 不应开头就执行 failover。合理顺序:
若先看到 replica lag 就 promote,可能:
- 切错根因;
- 扩大 write gap;
- 引入 split-brain 风险;
- 让原本健康的 write path 中断;
- 丢失调查证据。
Runbook 应提供 decision tree:
它不能穷举所有根因,但必须约束高风险捷径。
Recovery Drill:证明“能恢复”
backup SOP 和 restore drill 不同:
演练要在开始前固定 success criteria。若恢复后才决定“只要能连上就算成功”, 结果会向已有事实偏移。
本章的 SOP-BACKUP-RESTORE 要求:
- source cluster 和 repository 明确;
- recovery target 固定;
- destination 无 production listener;
- 验证 query 与预期先冻结;
- WAL 缺失、identity 模糊或 isolation 失败立即停止;
- 时间只记为沙箱 observation,不称 production RTO。
Drill 不应依赖临场英雄主义
有效演练测试:
- 文档是否完整;
- 权限是否可用;
- secret 是否能安全获得;
- 工具版本是否兼容;
- 依赖是否存在;
- 验收是否机器可判;
- 证据是否完整;
- 交接是否能继续。
只让原作者凭记忆完成,证明的是个人能力,不是组织恢复能力。可在后续轮次让 另一位合格 operator 按文档执行,原作者只观察和记录歧义。
Change Plan:把抽象 SOP 绑定到一次动作
SOP 可以写:
Change Plan 必须写:
“按切换 SOP 执行”不能替代这些单次事实。
版本和环境必须受支持
文档应写:
命令、输出字段和行为会随版本改变。升级计划必须同时检查 SOP 与 validator。
日常、故障和演练的证据粒度不同
| 类型 | 关键证据 |
|---|---|
| routine | target、输入版本、执行结果、postcondition |
| incident | timeline、user impact、decision、unknown outcome |
| drill | hypothesis、predefined success、timing、exception |
| change | request/approve/execute identity、artifact hash、closeout |
不能因为 incident 需要更多证据,就把所有 routine 日志永久保存;也不能因 routine 简洁,在安全事故中只留一个 shell exit code。
24.3.2 申请、评审、执行、验证与回退
完整生命周期
本章使用十一阶段:
“批准”不是生命周期的起点,也不是结束。
1. Request:先说明目的,不要从命令开始
请求至少回答:
反例:
合格:
2. Classify:按实际影响,不按团队习惯
本章风险级别:
L0:只读
- 查询统计视图;
- 渲染配置 diff;
- 验证已有 evidence;
- 不改变数据库、pool、topology、credential 或 route。
L0 仍要绑定目标,避免拿错误环境的证据做结论。
L1:有界、可逆、低影响
- 增加 dashboard;
- 发布 disabled alert;
- sandbox 中增加窄权限 NOLOGIN role。
需要 preview、verification 和 rollback。
L2:material state
- credential rotation;
- online schema expansion;
- pool limit;
- role/permission;
- 生产参数 reload;
- 业务路由变化。
需要独立批准、停止线、完整证据。
L3:destructive/topology/recovery
- failover;
- restore/promotion;
- data deletion;
- cluster reset;
- 大范围不可逆迁移。
需要最强 target binding、双人控制、延迟确认或等价门禁。
风险由 blast radius、可逆性、未知结果与数据后果共同决定,不是“SQL 只有 一行所以低风险”。
3. Freeze preconditions:审批的是事实快照
preflight 可以包括:
每个事实要有:
- source;
- captured_at;
- freshness limit;
- expected value;
- stop behavior。
批准后到执行前可能发生变化。执行器必须重新采集关键事实:
4. Preview and test:使用相同 artifact
好的 pipeline:
不要测试一套手写 SQL、执行另一套临时编辑内容。
preview 可能包括:
- Pigsty inventory projection 和 diff;
- SQL parse/plan;
- lock/rewrite analysis;
- restore dry-run facts;
- target list;
- expected state transition;
- synthetic data replay;
- rollback simulation。
preview 不能证明生产一定成功,但能拒绝明显不符合合同的输入。
5. Approve:批准目标、动作、窗口,不是空白授权
批准记录要绑定:
“同意处理”或聊天表情不能作为 L3 授权。
6. Execute:每一步都应可观察
执行器:
- 使用个人 durable identity;
- 不共享 root/DBA 密码;
- 不把 secret 放在 shell 参数;
- 记录 phase start/end;
- 每一阶段检查 stop condition;
- 不并行执行计划外动作;
- 超时后先分类结果。
脚本的 set -e 只能在命令非零时停止。它不能判断:
- command 返回 0 但目标错误;
- client timeout 后 server 已完成;
- failover command 成功但路由未刷新;
ALTER TABLE成功但 application 不兼容;- backup 成功但不可恢复。
7. Observe:动作期间看用户与组件
观察至少两条线:
只看执行命令输出,会错过用户影响;只看 SLI,又无法及时识别安全停止线。
8. Verify:状态正确,而不是命令完成
postcondition 应与 request success 一一对应。
planned switchover:
schema release:
credential rotation:
9. Rollback or roll forward:不是所有状态都能倒回
rollback 适合:
- application feature flag;
- additive configuration;
- reversible grant;
- 在 semantic cutover 前保留旧结构。
roll forward 更适合:
- timeline 已推进的 failover;
- 已泄露 credential;
- 已有新版本写入的数据格式;
- 外部系统已消费的事件;
- commit outcome unknown。
PostgreSQL DDL 的边界
许多 PostgreSQL DDL 可以在 transaction 中回滚,但不能据此声称发布总可逆:
CREATE DATABASE等命令不能在 transaction block 内运行;CREATE INDEX CONCURRENTLY不能在 transaction block 内运行,失败还可能 留下INVALIDindex;- data backfill 已被新应用读取后,SQL rollback 不能撤回外部影响;
- table rewrite/WAL/replica lag 已经发生;
- application 与 schema 的兼容窗口可能关闭。
具体行为见 PostgreSQL CREATE INDEX 和第 11 章的安全模式变更。
回退计划要说明“在哪个决策点之前可回退”,而不是只写 ROLLBACK。
10. Retain evidence:保存证明,不保存秘密
每次变化至少形成 manifest:
不要保存:
- cleartext password;
- SCRAM verifier;
- private key;
- 完整 credential URI;
- 无边界 bind values;
- 不必要的客户行。
11. Close and review:关闭是一个结论
关单条件:
- service postcondition 通过;
- component postcondition 通过;
- unknown outcomes 为零或有明确 owner/deadline;
- 临时权限、route、pool/config 已恢复或正式纳管;
- evidence 完整;
- follow-up 有 owner 与 due date;
- budget/incident 状态更新。
“维护窗口结束”不能自动关闭失败的变更。
一个 change record
本章要求字段:
真实系统还要验证三个 identity 不同且当时有效。
24.3.3 高风险动作的双人或延迟确认
双人控制解决什么
高风险动作容易同时发生三类错误:
独立批准人应使用独立证据检查意图与目标;执行器负责按批准 artifact 操作。 两人同时复制同一个错误命令,不构成有效独立控制。
NIST SP 800-53 Rev. 5.1 在 access restrictions for change 中描述 dual authorization:两名合格个人批准 和实施选定高风险变化,并对变化负责。组织还可轮换职责,降低串通风险。
本章将它转化为:
独立不只意味着用户名不同
无效:
- 同一人使用两个账号;
- 两人共享一个 password;
- approver 没有理解 PostgreSQL/Pigsty 风险;
- approver 只看 requester 的截图;
- 执行 artifact 在批准后被修改;
- 两人都受同一个未验证假设影响。
有效独立性包括:
- durable workforce identity;
- 不同职责;
- 足够专业能力;
- 能访问 source-of-truth;
- artifact hash;
- 能拒绝;
- 拒绝不会被绕过;
- 事后记录。
延迟确认解决冲动和误目标
destructive action 可要求 cooldown:
延迟让:
- 执行者有时间发现环境错误;
- 依赖 team 有时间反馈;
- 自动备份/导出有时间完成;
- 用户影响窗口再次确认。
它不适用于所有 incident。真正紧急时可以缩短 normal wait,但不能跳过 target、 authority、stop line 和 evidence。
confirmation token 必须绑定语义
差:
较好:
脚本还应从运行时重新解析 target identity,而不是完全信任环境变量字符串。
break-glass 不是无规则
break-glass 允许:
- 跳过正常排期;
- 获取时限高权身份;
- 执行恢复服务所需的最小动作。
它不允许:
- 不记录目标;
- 无止境保留高权;
- 关闭审计;
- 将 credential 发进聊天;
- 跳过事后 review;
- 使用后不轮换;
- 将未知结果当成功。
本章合同:
emergency change 仍要选择最小动作
事故中的压力会放大 scope:
Runbook 应给出干预阶梯:
每一级需要新的证据和 authority。不能因为已经进入 SEV-1,就自动获得所有 destructive action 的授权。
自动化能做什么
适合机器执行:
- 解析 inventory;
- 验证 mode/owner;
- 比较 target identity;
- 采集 leader/timeline/LSN;
- 计算 lock、WAL、budget;
- 检查 artifact hash;
- 拒绝非空 evidence directory;
- 执行 bounded probe;
- 核对 postcondition;
- 生成 manifest;
- 扫描 secret material。
仍需组织决定:
- 用户损失是否可接受;
- recovery point 是否符合业务;
- 是否暂停业务;
- 谁拥有 change authority;
- 合规/隐私边界;
- 何时允许例外。
自动化可以证明 guard 成立,不能发明授权。
guard 不是一串容易伪造的环境变量
高风险 runner 应结合:
只检查 NONPRODUCTION=true 不够;操作者可以错误设置。第 19–23 章的实验
runner 还会核对 host、cluster、topology 和 fixture identity。
超时与 unknown outcome
高风险 API/SQL 超时后:
例子:
COMMITtimeout:查 idempotency token;- switchover timeout:查 Patroni leader/timeline;
- revoke timeout:从新 session 测认证与 role attributes;
- DDL timeout:查 catalog/lock/index validity;
- backup timeout:查 repository manifest,不凭 client exit 猜。
重试本身是一项 change,需要可幂等证明。
高风险 review 的三次停止机会
批准人不能提前放弃后两次停止。机器 guard 也不能因为 approval 存在而忽略 live drift。
本章的 SOP 与变更政策
四个 SOP 覆盖:
每份都有 precondition、stop、verification、rollback/roll-forward 和 evidence。 validator 会拒绝:
- backup exit 0 作为全部恢复验证;
- L2/L3 同人自批自执行;
- break-glass 跳过 target/evidence;
- 沙箱运行被标为 production proof。
文档演练清单
- 文档类型是否正确,还是把 incident 写成线性 SOP?
- scope 和版本是否明确?
- trigger 与 authority 是否明确?
- exact target 能否由 live evidence 验证?
- precondition 是否带 freshness?
- 每个高风险 phase 前是否有 stop condition?
- 命令 timeout 后是否先核对 durable state?
- success 是否由 postcondition 定义?
- rollback 与 roll-forward 的决策点是否明确?
- external side effect 是否考虑?
- requester/approver/executor 是否真正独立?
- break-glass 是否有时限、审查和轮换?
- evidence 是否 secret-free?
- 另一个合格 operator 能否只凭文档完成?
- 文档、脚本、目标版本变化后是否重新演练?
下一节会把 SLO 与这些 Runbook 连接起来:一个告警若不能把值班人带到安全的 第一动作,就不应成为 page。
上一节:SLI、SLO 与错误预算 · 返回本章目录 · 下一节:观察与告警契约 · 查看全书目录 · 查看索引中心
24.4 观察与告警契约
监控系统不会自动知道“什么对用户重要”。它只会忠实地计算你交给它的数值。
因此告警规则之前要有 observation contract:
本节产出第 25 章的实现合同,而不提前声称指标和 page 已经部署。
24.4.1 每个 SLI 的数据源、查询、维度和缺失语义
一个 SLI 需要哪些字段
最小 observation contract:
| 字段 | 问题 |
|---|---|
objective_id |
它实现哪个 SLO/control? |
source |
谁产生原始事实? |
metric_type |
counter、histogram、gauge 还是 event? |
good_selector |
哪些事件进入分子? |
total_selector |
哪些事件进入分母? |
dimensions |
按哪些有界维度分解? |
query_template |
如何从窗口计算? |
missing_semantics |
series 消失代表什么? |
fallback |
主 observation path 失败后看什么? |
metric_status |
已存在、待实现还是 deprecated? |
少一个字段就可能改变结论。
本章的五个来源
| objective | source | type | 状态 |
|---|---|---|---|
| availability | pg36_shop_request_outcomes_total |
counter | 应用待实现 |
| latency | pg36_shop_request_duration_seconds |
histogram | 应用待实现 |
| freshness | pg36_shop_commit_visibility_probes_total |
counter | synthetic probe 待实现 |
| correctness | pg36_shop_reconciliation_mismatches |
gauge | 核对任务待实现 |
| restore readiness | pg36_shop_restore_evidence_age_seconds |
gauge | 证据导出器待实现 |
这些是本书定义的应用 metric contract,不是 Pigsty 内置指标。metric_status
故意保留“to implement”,防止目录文档冒充运行事实。
availability query
原始 counter:
bad ratio 模板:
unknown 在核对前不进入 good,所以会消耗预算。后续事件若核对为 committed
once,可通过事件管道作有审计的最终分类;不能在 dashboard 手工改值。
clamp_min 只防除零,不解决 telemetry missing。no traffic、counter absent 和
ingestion broken 要由独立 freshness/metamonitoring 判断。
latency query
histogram 需要 le="0.25" bucket:
这里示意把快速失败留在 denominator 而不进入 good bucket。实际 instrumentation 也可以统一用 outcome counter 与 duration histogram 通过 recording rule 对齐, 但必须有自动测试证明:
否则两个 SLO 使用不同分母,会产生无法解释的预算。
freshness query
每个 probe:
bad ratio:
probe absence:
不能用 pg_last_xact_replay_timestamp() 填补,因为它没有对应当前 commit。
correctness query
需要配套:
last_success_timestamp;- reconciliation input boundary;
- rule/query version;
- invariant;
- result hash。
只导出 0 而不监控 job freshness,会在核对任务停摆后永远显示“零差异”。
missing semantics:
restore evidence age
阈值 90 天:
导出器必须只接受验证通过且完整性 hash 正确的 restore manifest。不能因为目录中 有一个新文件,就把 evidence age 归零。
PostgreSQL statistics 用于解释原因
PostgreSQL 18 提供:
pg_stat_activity:backend、状态、query/transaction 时间;pg_stat_database:transaction、block、tuple 与 session 统计;pg_stat_replication:sender、state、LSN 与同步状态;pg_stat_wal_receiver:standby receiver;pg_stat_archiver:归档成功/失败;pg_stat_io:按 backend/object/context 的 I/O;pg_stat_ssl:连接 TLS;- lock 与 progress views。
完整目录见 PostgreSQL Monitoring Database Activity。
这些视图回答:
不直接回答:
访问统计应使用 pg_monitor 等受控预定义角色或更窄授权,而不是让 exporter
成为 superuser。预定义角色边界见
Predefined Roles。
Pigsty v4.5 的实现层
当前 Pigsty 监控栈:
| 组件 | 职责 |
|---|---|
| VictoriaMetrics | 时序 ingestion、storage、query |
| VictoriaLogs | 结构化日志 |
| VMAlert | 规则评估 |
| Alertmanager | 聚合、抑制、路由、通知 |
| Grafana | dashboard 与调查入口 |
| exporter/agent | 暴露数据库、主机和组件事实 |
Pigsty v4 已从旧的 Prometheus/Loki 存储迁到 VictoriaMetrics/VictoriaLogs; VMAlert 仍使用 PromQL-compatible 规则。不要复制旧文档后声称 v4 仍以 Prometheus server 存储时序。当前架构见 Monitoring System。
PostgreSQL、PgBouncer、host、load balancer 尽量通过:
关联,详见 PGSQL Monitoring。
维度:服务与组件分别建模
应用 SLI:
组件 telemetry:
连接方式:
不要强行把 service 填成 ins,也不要把所有 database metric 复制一份
customer label。
cardinality 是可靠性和隐私问题
禁止 label:
customer_id;tenant_id;order_id;- raw SQL;
- error message;
- stack trace。
原因:
- series 数无界增长;
- query 和 rule 变慢;
- monitoring storage 自身失稳;
- 用户数据进入广泛可见系统;
- label 变化让 aggregation 不可靠。
使用:
- bounded error class;
- normalized SQL fingerprint;
- exemplar/correlation token;
- 受控日志查明具体事件。
missing-data truth table
| traffic/probe | SLI series | 结论 |
|---|---|---|
| 有预期流量 | 存在 | 计算 SLI |
| 有预期流量 | 缺失 | observation failure |
| 无真实流量 | synthetic 存在 | limited synthetic evidence |
| 无真实流量 | synthetic 缺失 | unknown |
| exporter 存在 | app SLI 缺失 | component observable, service unknown |
| app SLI 存在 | Alertmanager canary 失败 | health known, notification path broken |
“没有错误 series”与“error counter value is zero”不是一回事。
24.4.2 告警必须绑定用户影响、首个安全动作和所有者
page 的门槛
Prometheus 官方 alerting practices 总结为:
- 保持简单;
- 对症状告警;
- 用良好 console 定位原因;
- 避免无事可做的 page。
本章把 page 定义为:
缺一项就应考虑 dashboard、ticket 或删除。
一个 alert contract
每个 accepted alert 至少包含:
这比:
多出来的内容,正是值班可行动性的来源。
user impact 要具体
差:
好:
差:
好:
first safe action 不是最终修复
fast-burn availability page:
它没有假设根因。之后 Runbook 才检查应用、入口、pool、PostgreSQL、锁和拓扑。
correctness page:
不能先运行“修复 SQL”,否则会覆盖事故证据。
metamonitoring page:
监控断了时先恢复观察,不要凭空重启数据库。
owner 必须有 route
owner_function=service 仍不够。规则要带:
route 要用 canary 验证:
不能只看 Alertmanager 进程 up。
for 防短暂抖动,但会增加检测延迟
Prometheus/VMAlert 风格规则可以使用:
条件必须持续 2 分钟才 firing。它适合滤除短 blip,但不是越大越好:
correctness mismatch 可以 for: 0m,因为单个已确认 mismatch 的后果不同。
当前 Prometheus 规则语义还支持 keep_firing_for,用于条件短暂消失后继续
firing 一段时间;使用前要确认 VMAlert 当前版本的兼容和行为,并通过 rule
test 验证。官方字段见
Alerting rules。
silence 不是关闭问题
silence 必须有:
- incident/change id;
- owner;
- reason;
- exact matcher;
- start/end;
- replacement observation;
- review。
禁止:
计划维护也应尽量使用 route/inhibition 和用户 SLO 的明确政策,而不是让所有 信号消失。
verification 决定何时恢复
fast-burn 恢复:
只看到 alert resolved 可能是:
- series 消失;
- label 改变;
- rule reload 失败;
- traffic 归零;
- silence;
- query error。
Runbook 必须检查 missing semantics 和用户结果。
alert 要有生命周期
规则不是写完永存:
orphaned route 或过期 runbook 应使 CI/治理 review 失败。
24.4.3 症状告警、原因告警与容量预测分开
症状 page
症状直接表示用户或数据后果:
- availability fast burn;
- latency fast burn;
- commit-correlated freshness violation;
- correctness mismatch;
- imminent durability loss;
- observation path loss 导致服务状态不可知。
它回答:
原因 telemetry
原因帮助定位:
- CPU;
- disk latency;
- lock waits;
- pool queue;
- replica WAL distance;
- cache hit ratio;
- autovacuum backlog;
- connection count;
- Patroni member state。
同一用户症状可能有多个原因;同一原因也可能被 redundancy 吸收而没有用户 影响。若每个原因都 page:
值班收到五个 notification,却没有更多信息。
原因应进入:
- linked dashboard;
- diagnostic annotation;
- bounded ticket;
- automatic enrichment。
两个允许越过“用户症状”的例外
完整性/保密性
一条跨租户数据或 confirmed corruption 即使用户尚未报告,也需要立即处理。
迫近耐久性
例如所有可恢复副本/备份路径都失效且继续运行会导致不可恢复数据风险。此时 page 的依据是已定义的 durability control,不是随意的 component threshold。
例外仍要 owner、Runbook、action 和 verification。
容量是有期限的 ticket
容量预测:
通常不是当前 user incident,因此:
- 创建 owned ticket;
- 带 forecast confidence;
- 检查 demand/query mix/retention;
- 给 due date;
- 不在凌晨 page。
若容量已经造成 latency/availability,则症状 SLO 会 page;capacity facts 作为 诊断。
本章候选:
metamonitoring
监控系统也会失败:
- exporter 停止;
- agent 无法发送;
- VictoriaMetrics ingestion/query 失败;
- VMAlert rule evaluation error;
- Alertmanager route 失败;
- receiver 不可达;
- dashboard query 误导。
有效 metamonitoring:
Prometheus alerting practices 也建议用贯穿 PushGateway/Prometheus/ Alertmanager/email 的 blackbox 测试,而不是只盯每个组件进程。Pigsty v4 中要 把这个思想映射到 VictoriaMetrics/VMAlert/Alertmanager。
分类矩阵
| signal | class | route | 例子 |
|---|---|---|---|
| user event bad ratio | symptom | page/ticket by burn | availability |
| data invariant mismatch | integrity | page | duplicate/cross-tenant |
| WAL distance | cause | dashboard | replication diagnosis |
| pool queue | cause | dashboard | latency diagnosis |
| CPU | cause | dashboard/ticket | capacity/diagnosis |
| days-to-full | capacity | ticket | disk/storage |
| notification canary overdue | metamonitoring | page | blind operation |
本章明确拒绝的 page
候选:
被拒绝,因为:
- 一台 instance 可因 offline maintenance 合理下线;
- redundancy 可能仍满足服务;
- 没有声明用户影响;
- 没有 owner;
- 没有 Runbook;
- 没有第一安全动作。
替代:
validator 会故意把它的 decision 改成 accepted,并确认合同拒绝。
24.4.4 产出供 ch25 实现的告警规则清单
七个 accepted candidates
1. PG36ShopAvailabilityFastBurn
对 99.9%:
即两个窗口 bad ratio 都高于 1.44%。
2. PG36ShopAvailabilitySlowBurn
阈值 0.6% bad ratio。
3. PG36ShopAvailabilityBudgetTicket
用于慢性消耗,不打扰夜间值班。
4. PG36ShopCorrectnessMismatch
不使用 burn rate。
5. PG36ShopFreshnessFastBurn
不要把所有 read traffic 永久移到 primary;这是有界安全动作,恢复后再评审 read policy。
6. PG36ShopCapacityHorizon
预测没有足够历史时,创建 evidence-quality ticket,而不是编造 forecast。
7. PG36MonitoringPathBroken
rule skeleton
第 25 章可从下面开始,但必须使用 recording rules 和测试后的真实 label:
这段只是规则语义骨架,不能直接部署,因为:
- recording rules 尚未实现;
- production environment/region labels 尚未固定;
- route 与 receiver 仍是教学标识;
- real owner 尚未批准;
- rule evaluation interval 尚未纳入检测时间;
- test fixture 尚未建立。
第 25 章必须交付什么
Instrumentation
- counters/histograms 的真实采集;
- eligible/good classifier tests;
- commit token probe;
- reconciliation freshness;
- restore evidence exporter;
- bounded labels。
Recording rules
- 5m、30m、1h、6h、3d 等窗口;
- label-preserving aggregation;
- reset 和 counter semantics;
- no-traffic/missing 分支;
- rule unit tests。
Alert rules
- 七个 accepted candidates;
- actionless candidate 保持 rejected;
for和 evaluation delay;- route/inhibition/silence;
- review expiry。
Dashboards
- SLI、target、budget remaining;
- numerator/denominator;
- eligible classification;
- recent changes;
- component cause drill-down;
- missing telemetry state。
Delivery tests
- synthetic rule firing;
- label/annotation assertions;
- Alertmanager grouping;
- notification canary;
- acknowledgement;
- resolved 与 missing 的区分。
交接门槛
在实现规则前逐项确认:
- objective id 与 SLO policy 一致;
- metric status 从“待实现”变更有 evidence;
- good/total selector 经过代码测试;
- histogram 有目标 bucket;
- unknown outcome 不会被算 good;
- missing series 有独立规则;
- labels 有界且不泄露数据;
- Pigsty identity 使用
cls/ins/ip; - component facts 不冒充 user SLI;
- multiwindow 使用 AND;
- page 有 owner、route、Runbook、action 和 verification;
- capacity 进入 ticket;
- cause 进入 dashboard;
- correctness/durability 例外有独立 control;
- notification path 自身被测试;
- real pager 测试获得明确授权;
- sandbox 规则没有被称为 production-approved。
完整输入见:
下一节处理这些指标、变更和操作留下的证据:证据必须足够证明决定,却不能把 秘密和个人数据无边界地复制到治理系统。
上一节:SOP、Runbook 与变更治理 · 返回本章目录 · 下一节:证据、审计与合规 · 查看全书目录 · 查看索引中心
24.5 证据、审计与合规
“日志里应该有”不是证据策略。
一份可用于运营决策的证据至少要回答:
同时还要回答:
数据库 evidence 很容易包含 credential、SQL bind value、tenant data、backup 内容和人员身份。收集越多,不等于治理越好。
24.5.1 配置、变更、访问和恢复证据
日志、审计、证据和合规不是同义词
| 对象 | 主要用途 | 例子 |
|---|---|---|
| operational log | 调试运行行为 | PostgreSQL error/log line |
| audit record | 记录受关注主体动作 | role/DDL/object access |
| evidence | 支撑一个具体结论 | target+hash+result manifest |
| compliance assessment | 将证据映射到外部控制 | control tested/effective/gap |
一个系统可以日志很多但证据不足:
反过来,保存一份结构化的 secret-free manifest 可能比复制整个日志目录更适合 日常 change evidence。
配置证据:声明、渲染、运行事实
Pigsty 管理的 PostgreSQL 配置有三层:
只保存 inventory 不能证明部署生效;只保存 SHOW 不能证明变更来源和评审。
配置 evidence:
需要特别区分:
- desired value;
- current effective value;
- reload-pending;
- restart-pending;
- session override;
- role/database override。
PostgreSQL pg_settings 的 source、sourcefile、pending_restart 等字段有助于
解释运行值,但路径和配置内容仍需按敏感性处理。官方视图见
pg_settings。
变更证据:意图、授权、执行、结果
完整 change evidence:
命令历史不能替代这个链:
- shell history 可编辑;
- shared account 无法归因;
- command line 可能泄露 secret;
- 它不记录独立批准;
- 它不证明 target;
- 它不证明结果。
访问证据:身份链而不是密码
第 23 章区分:
访问 evidence 可以保存:
- durable identity id;
- authentication mechanism/result;
- login/effective role;
- membership option;
- target object/action;
- approval;
- SQLSTATE;
- session id/correlation;
- start/end/revoke;
- policy version。
不能保存:
- cleartext password;
- SCRAM verifier;
- private key;
- session token;
- credential-bearing URI;
- raw PgBouncer userlist;
- 无必要的 SQL parameter。
credential rotation evidence 保存 secret version id,而不是 secret value:
ordinary PostgreSQL logs 的边界
PostgreSQL logging 可以记录:
- connection/disconnection;
- statement duration;
- SQLSTATE 和错误;
- DDL/statement;
- prefix 中的 user/database/application/session;
- lock、checkpoint、autovacuum 等运行事件。
配置见 Error Reporting and Logging。
但普通日志不是自动完整 audit:
- 采样和阈值会省略事件;
- statement 与 bind value 记录受参数影响;
- superuser/host admin 可能改配置;
- owner/role 语义需要额外关联;
- log storage、access、integrity、retention 仍需治理;
- 记录过多参数会泄露数据。
pgAudit 可以提供更结构化的 session/object audit,但仍需安装、preload、配置、 容量和审查。官方项目见 pgaudit/pgaudit。它也不能替代 change request、业务授权和 evidence chain。
恢复证据:从 backup 到 application
恢复 evidence 至少有:
第 21 章正式摘要:
本章只引用其 run id 与 SHA-256,不复制 private raw bundle,也不把单次沙箱 timing 升格为生产 RTO。
SLO 和告警证据
SLO decision 需要:
- policy version;
- numerator/denominator;
- query/recording rule hash;
- evaluation window;
- exclusion records;
- missing-data intervals;
- budget state;
- release/change markers;
- alert firing/resolved;
- route delivery 和 acknowledgement;
- owner decision。
只有 dashboard screenshot 不够:
- 时间范围可能隐藏;
- query 可能后来改变;
- series 可能 missing;
- panel 可手工选择过滤;
- screenshot 不能复算。
可以保留 screenshot 作为沟通附件,但 canonical evidence 应能机器重算。
incident evidence
事件证据:
时间线要区分:
避免事后把“后来知道的根因”写成当时已知事实。
六类 evidence policy
本章定义:
| 类别 | 教学保留 | 分类 |
|---|---|---|
| configuration | 400 d | internal |
| change | 400 d | confidential-operational |
| access | 400 d | confidential-security |
| recovery | 400 d | confidential-operational |
| SLO/alert | 400 d | internal |
| incident | 730 d | confidential-incident |
这些数字只是教学政策,用于验证每类都有正数、访问和删除合同。真实保留期必须 经过法律、监管、隐私、调查和成本评审,不能照抄。
24.5.2 保留、不可抵赖与隐私边界
chain of custody
证据链:
“文件还在”不能证明它没被改。
hash 证明什么
SHA-256 可以证明:
不能单独证明:
- 谁采集;
- source 真实;
- 采集前没有被篡改;
- timestamp 可信;
- manifest 没被一起替换;
- 结论正确。
增强方式:
- signed manifest;
- append-only/object lock store;
- independent timestamp;
- durable collector identity;
- separation of duties;
- multiple source correlation;
- access and deletion audit。
不要把“有 hash”写成绝对不可抵赖。
不可抵赖依赖个人身份
shared postgres 或 root account 最多证明“某个拥有共享 credential 的人”。
要提高 accountability:
仍要考虑:
- 身份被盗;
- host compromise;
- log admin;
- 时钟;
- collusion;
- privacy。
所以工程上更准确的说法是“提高归因与篡改检测能力”,而不是宣称数学意义上的 绝对不可抵赖。
UTC、时钟与顺序
多节点 PostgreSQL/Pigsty 证据需要:
- UTC timestamp;
- NTP status;
- monotonic duration;
- event sequence/correlation;
- clock uncertainty。
wall clock 可调整,耗时应使用 monotonic clock。跨系统排序若只靠毫秒 timestamp 可能出错,应用事件应有 correlation/idempotency token。
最小必要收集
问四次:
例子:
| 原始材料 | 更安全替代 |
|---|---|
| password | secret version id |
| certificate private key | public certificate fingerprint |
| full SQL values | normalized fingerprint + SQLSTATE |
| customer row | count + synthetic marker |
| full connection URI | host/db/user + redacted auth |
| raw system identifier everywhere | relation matches/differs,必要时私密保存 |
evidence store 不是 backup repository
backup 本身包含业务数据,访问和保留应按数据分类;治理 evidence 只需记录:
不要把 backup archive 复制到工单附件。
legal hold 与 scheduled deletion
正常生命周期:
事件/诉讼 hold:
hold 不能变成永久保存所有数据的借口;解除后恢复原 retention decision。
redaction 要保留原始与派生关系
安全流程:
直接覆盖 original 会破坏调查能力;把 original 给所有 reviewer 又扩大泄露面。
日志参数与隐私
PostgreSQL 的 statement/parameter logging 可以帮助诊断,也能记录:
- password reset SQL;
- token;
- email/phone;
- tenant data;
- health/financial data;
- application secret。
第 23 章沙箱观察到普通非错误 statement logging 可能保留完整 bind parameter, 且 pgAudit 未安装/preload。这个 gap 在治理层必须:
- 有 owner;
- 有整改或接受期限;
- 限制日志访问;
- 评审 retention;
- 避免在 SQL 中传 secret;
- 对敏感语句/值设计脱敏;
- 不谎称“审计完整”。
合规是外部控制映射
NIST、ISO、行业或地区监管可以要求:
- change control;
- dual authorization;
- audit generation/review;
- retention;
- least privilege;
- incident evidence;
- recovery testing。
本章合同可以成为控制 evidence,但不能自行宣布:
正式 assessment 还需要:
- applicable scope;
- control owner;
- external requirement version;
- test procedure;
- evidence period/sample;
- deficiency;
- compensating control;
- assessor decision。
本章参考 NIST SP 800-53 Rev. 5.1 的 change/audit 思路,不把它当通用认证印章。
24.5.3 用自动检查减少人工表格
自动化检查不变量
最有价值的自动化不是生成更多空表格,而是拒绝错误状态。
本章 validator 检查:
交叉引用比格式更重要
JSON 能 parse 不代表治理一致:
这类关系很难靠人工逐页检查,适合 CI。
对抗性验证证明 validator 不是摆设
positive fixture 全通过仍可能说明检查从未真正失败。每条关键 policy 要有 negative case:
本章二十个 mutation 全部必须产生至少一个 failure。若一个 mutation 被接受, validator 即失败。
hash binding
正式 evidence 保存:
这样以后修改合同再验证旧 evidence,会因 source hash drift 失败。不能拿新版 规则解释旧运行而不声明差异。
live gate 与历史证据分开
本章正式运行做两件事:
它没有重跑:
- failover;
- restore;
- pool mutation;
- credential/RLS drill。
因此结论:
这是 evidence freshness 的重要边界。
secret scanner
review 扫描 private evidence:
- SCRAM verifier signature;
- PEM private key;
- clear password JSON field;
- credential-bearing PostgreSQL URI。
scanner 不能发现所有 secret,也会有误报/漏报。还需要:
- source schema allowlist;
- credential input 与 output 分离;
- mode
0600; - no shell args;
- redacted projection;
- 人工 review;
- secret manager policy。
mode 与位置
正式 evidence directory:
- 新建且为空;
umask 077;- 文件 group/world 不可读;
- 不进入 repository;
- private inventory 不复制、不 hash;
- repository 只保存 secret-free summary。
本章的 governance-run.json 只保留摘要、run id、计数和 source/upstream hash。
自动化不能决定什么
validator 能发现:
它不能决定:
- 99.9% 是否符合真实用户;
- 250 ms 是否合理;
- 400 天是否符合法律;
- 哪个人有组织授权;
- 一次 data mismatch 的客户影响;
- 是否应该恢复到某个时间点;
- 一个 exception 是否值得接受。
这些需要真实 owner 与 authority。自动化的价值是让他们在同一组可信事实上 决策。
将手工表格转换为规则
手工问题:
更强的机器合同:
不是所有内容都能计数,但能机器判定的部分不应只靠勾选。
CI 分层
把所有检查塞进夜间报表会错过执行门槛。
本章正式结果
公开摘要见
governance-run.json,正式原始 evidence 位于
私密临时目录,不进入仓库。
Evidence review 清单
- 结论要证明什么?
- source、target、collector、time、tool 是否明确?
- desired/rendered/observed 是否分开?
- requester/approver/executor 是否可归因?
- artifact 是否按 hash 绑定?
- command success 与 postcondition 是否分开?
- access evidence 是否不含 credential?
- recovery evidence 是否包含 application verification?
- SLO numerator/denominator/query/exclusion 是否可复算?
- missing intervals 是否保留?
- original 与 redacted copy 是否可追溯?
- retention 是否有目的、访问、hold 和删除?
- hash 是否被错误宣传为绝对 authenticity?
- shared account 是否破坏归因?
- raw SQL/bind/customer rows 是否最小化?
- negative cases 是否真的失败?
- 历史 evidence 的 freshness 是否明确?
- compliance 结论是否由适用的正式评估给出?
下一节把服务卡、SLO、告警、SOP 和 evidence 串成一个完整的 pg36_shop
治理实验。
上一节:观察与告警契约 · 返回本章目录 · 下一节:实战:把 pg36_shop 纳入服务治理 ·
查看全书目录 · 查看索引中心
24.6 实战:把 `pg36_shop` 纳入服务治理
本节把全章压成一条可重放的 L0 证明:
实验不会:
- 创建或修改 PostgreSQL 对象;
- 改 PgBouncer/HAProxy;
- 切换 leader;
- 运行 restore;
- 修改 role/credential/HBA/TLS;
- 部署 VMAlert rule;
- 发送真实 page;
- 删除或重置数据。
all 在本章表示“采集只读 gate 并验证全部合同”,不是“执行所有被引用的
高风险演练”。
实验合同:
24.6.1 发布服务卡、SLO、责任人与升级路径
目标环境
服务卡明确:
因此 validator 会拒绝任何把它改成 production SLO 的 mutation。
文件布局
核心合同八份,其他文件负责解释、验证和保存公开摘要。
服务卡
四个 owner:
| function | role id | accountable |
|---|---|---|
| service | shop-service-owner |
journey、SLO、release、业务决定 |
| data | shop-data-owner |
语义、保留、质量、隐私分类 |
| platform | database-platform-owner |
PG/Pigsty、容量、恢复、执行 |
| security/privacy | security-privacy-duty |
access、例外、披露、隐私 |
这些是 role contracts,没有写个人名字。route://... 也只是教学 route id,
不声称有真实 pager。
依赖图
Mermaid 源文件:
dependency-map.mmd。
每项依赖带 failure semantics。例如:
六层健康
所有层的 sufficient_for_service_health=false,服务卡另有:
validator 会把第一层改成 true,确认“process alive 即服务健康”被拒绝。
SLO policy
slo-policy.json 定义:
| ID | kind | target |
|---|---|---|
SLO-AVAILABILITY |
ratio | 99.9% / rolling 28d |
SLO-LATENCY |
ratio | 99% under 250 ms / rolling 28d |
SLO-FRESHNESS |
ratio | 99% under 5 s / rolling 28d |
CTRL-CORRECTNESS |
control | zero unexplained mismatch |
CTRL-RESTORE-READINESS |
control | passing isolated restore ≤ 90d |
算术验证:
Python validator 使用容差重新计算,而不是信任 JSON 中的答案。
exclusion
三个默认排除:
- admission 前的语法错误;
- 正确拒绝的未授权身份;
- admission 前客户端断开且没有 server outcome。
明确:
一个新例外必须记录 id、时间、operation、independent approver、reason 和 before/after count。
error-budget state
每个状态都有 action。validator 把 exhausted actions 清空,确认“没有后果的 预算”被拒绝。
只做合同 lint
不连接任何 sandbox:
输出:
lint 使用私密临时目录保存报告,并在结束时删除;不产生 repository 文件。
24.6.2 为备份、切换、权限和发布建立 SOP
四份 SOP
| ID | capability | risk | owner / approver |
|---|---|---|---|
SOP-BACKUP-RESTORE |
backup and restore | L3 | platform / data |
SOP-ROLE-CHANGE |
switch/failover | L3 | platform / service |
SOP-ACCESS-ROTATION |
access/credential | L2 | security / platform |
SOP-SCHEMA-RELEASE |
schema release | L2 | service / platform |
owner 与 approver 不同。每份包含:
- trigger;
- exact target;
- blast radius;
- prerequisites;
- procedure phases;
- stop conditions;
- verification;
- rollback/roll-forward;
- evidence;
- review interval。
恢复 SOP
输入:
停止:
- identity/target 模糊;
- destination 可接 production traffic;
- WAL/integrity 失败;
- 验收未事先固定。
通过:
- target marker 存在;
- post-target marker 不存在;
- database identity 正确;
- recovered server 已停止;
- timing 只称 sandbox observation。
validator 会把 verification 替换为:
并拒绝。
role change SOP
先区分:
共同停止线:
- 多写主可能;
- DCS/fencing unknown;
- candidate 不符合 durability policy;
- unknown writes 不能核对。
验证:
rollback 文案刻意写:
timeline 已推进后,不能把“切回”当撤销历史。
access rotation SOP
步骤:
若 suspected disclosure,不能 rollback 到旧 secret,只能再次向前轮换。
schema release SOP
停止:
- lock 超时;
- WAL/lag/disk/budget 越界;
- old app 提前不兼容;
- backfill 不可恢复或不幂等。
上游实验绑定
三份 SOP 引用第 20、21、23 章;第 22 章作为入口/池证据也在本章 upstream manifest 中绑定。
| 章 | run id | SHA-256 | production |
|---|---|---|---|
| ch20 | 475e9b47-bc35-4687-87da-012f1d5ea455 |
602e932b...9d9a5d |
pending |
| ch21 | run_20260729T201040Z_961665aa |
c0b3589d...a212ca |
pending |
| ch22 | 87a63891-d6bf-46b3-bb65-d70a8d7bac3a |
444a6521...e6a2 |
pending |
| ch23 | 64b857a6-8d8f-46e2-9462-3f097a95a69f |
e1548658...cb59 |
pending |
完整 digest 见
governance-run.json。
绑定证明:
不证明:
变更政策
change-policy.json 固定 L0–L3。
L2/L3:
break-glass:
正式 L0 运行
需要第 19 章 private mode-0600 inventory:
runner:
没有 reset action。
当前第 19 章 gate
正式运行观测:
六项例外:
- shared hypervisor;
- single etcd;
- single backup target;
- virtual storage;
- sandbox inventory secret handling;
- lab resource floor。
再次通过 gate 没有消除这些例外。
正式 evidence
private evidence 保存:
- ch19 current capture;
- governance evidence;
- positive report;
- negative report;
- review output。
公开仓库只保存
governance-run.json。
24.6.3 输出观察契约,并拒绝没有动作的告警候选
观察契约
observation-contract.json 为五个目标绑定:
Pigsty component labels:
application labels:
禁止:
component telemetry
四组:
| ID | sources | purpose |
|---|---|---|
PG-COMPONENT-ACTIVITY |
activity/database/io | concurrency/error/I/O diagnosis |
PG-COMPONENT-REPLICATION |
replication/receiver/LSN | replay and WAL risk |
PG-COMPONENT-ARCHIVE |
archiver/pgBackRest | recovery-path risk |
PIGSTY-CORRELATION |
Victoria stack | correlate PG/pool/LB/host/log |
它们不替代 user SLI。
accepted alerts
| alert | class | route |
|---|---|---|
PG36ShopAvailabilityFastBurn |
symptom | page SEV-1 |
PG36ShopAvailabilitySlowBurn |
symptom | page SEV-2 |
PG36ShopAvailabilityBudgetTicket |
symptom | ticket |
PG36ShopCorrectnessMismatch |
integrity | page SEV-1 |
PG36ShopFreshnessFastBurn |
symptom | page SEV-2 |
PG36ShopCapacityHorizon |
capacity | ticket |
PG36MonitoringPathBroken |
metamonitoring | page SEV-2 |
其中:
所有 accepted item 都有:
- user impact;
- owner function;
- route;
- runbook;
- first safe action;
- verification;
- dashboard;
- missing semantics;
- silence policy;
- test id;
- review expiry。
rejected alert
字段:
replacement:
这不是漏填;它是一个特意保留的反例。若有人将 decision 改为 accepted,
validator 失败。
三个 diagnostic-only causes
都不 page。它们进入 dashboard,只有 correlated user/integrity/durability 合同触发后才帮助定位。
metamonitoring
要求:
- application series freshness;
- VMAlert evaluation failures;
- Alertmanager notification canary;
- VictoriaMetrics query/ingestion;
- external blackbox probe。
missing_is_healthy=false。
二十个反例
| ID | mutation | 被哪类不变量拒绝 |
|---|---|---|
| N01 | 宣称 production SLO | scope |
| N02 | 删除 platform owner | ownership |
| N03 | process 即服务健康 | health chain |
| N04 | SLO target = 100% | ratio target |
| N05 | 排除 planned maintenance | exclusion |
| N06 | instance health = user SLI | measurement |
| N07 | missing = healthy | missing semantics |
| N08 | exhausted 无 action | budget policy |
| N09 | availability series 缺失算健康 | observation |
| N10 | 允许 unbounded labels | cardinality/privacy |
| N11 | page 无 first action | actionability |
| N12 | cause 直接 page | alert class |
| N13 | capacity 直接 page | route |
| N14 | 接受 actionless instance page | rejection |
| N15 | backup exit 0 即 restore | recovery evidence |
| N16 | 一人申请批准执行 | authority |
| N17 | break-glass 跳过 target/evidence | emergency boundary |
| N18 | access evidence 允许 secret | privacy |
| N19 | 伪造 ch21 run id | upstream identity |
| N20 | 沙箱切换称 production proof | claim boundary |
正式结果:
review 输出
为什么 production 仍 pending
机器验证通过,不等于生产批准。仍缺:
- 真实 service/data/platform/security owner;
- 真实 on-call schedule 与 notification test;
- 真实用户 traffic 的 eligible/good classifier;
- 生产 failure domains;
- 生产容量和 workload;
- 多次切换/恢复的分布,而非一次观察;
- 真实 RTO/RPO 谈判;
- 法律/隐私 retention authority;
- production secret/audit 路径;
- 第 25 章真实 instrumentation/rules/dashboards;
- 生产等价环境中的重复演练;
- 正式 exception 与 expiry。
所以本章结论:
重验已有 evidence
修改任何受 hash 保护的 policy/script 后,旧 evidence 验证会失败。应:
不能覆盖旧 evidence 让历史“自动符合”新政策。
本章验收
- service card 有 4 owner、6 dependencies、6 health layers;
- 3 个 ratio SLO 和 2 个 control objective 可计算;
- planned maintenance 未被隐藏;
- missing telemetry 不算健康;
- error budget 真实影响 change policy;
- 四类 SOP 有停止线与验证;
- L2/L3 权力分离;
- break-glass 仍需 target/evidence;
- 五个 observation source 有 query/missing/fallback;
- 7 个 accepted alerts 可行动;
- cause、capacity 与 symptom 分开;
- actionless page 明确拒绝;
- 六类 evidence 不允许 secret;
- ch19 live gate 重新只读通过;
- ch20–23 run 按 id/hash 绑定;
- 20 个 adversarial mutations 全拒绝;
- production gap 与 pending gate 保留。
第 25 章的任务不是重新发明这些语义,而是把它们实现为真实的应用 metric、 Pigsty recording/alert rules、dashboard、notification route 和 rule tests。
上一节:证据、审计与合规 · 返回本章目录 · 下一章:望闻问切:监控体系与可观测诊断 · 查看全书目录 · 查看索引中心