24.1 服务目录与责任模型
PostgreSQL 集群是技术对象,服务是责任对象。
technical object
cluster pg-test
members pg-test-1/2/3
ports 5433/5434/5436/5438
database test
service object
pg36_shop
journey place-order / read-order
owner / dependency / SLO / escalation / evidence一个集群可以承载多个服务;一个服务也可以依赖多个数据库、队列和外部接口。 若资产目录只记录 IP、实例和版本,事故发生时仍不知道谁能决定:
- 是否暂停下单;
- 是否允许读到旧数据;
- 是否执行 failover;
- 是否接受恢复到某个时间点;
- 是否通知受影响用户;
- 是否冻结发布;
- 谁能关闭事件。
本节先把这些决定分配给职责,再讨论服务等级、依赖与健康证明。
24.1.1 服务所有者、数据所有者与平台所有者
owner 不是“出问题时帮忙的人”
owner 是对某类决定最终负责的职能。它可以把任务委托给执行者,但不能把 accountability 变成“大家共同负责”。
本章采用四个稳定职能:
| 职能 | 最终负责什么 | 不应独自决定什么 |
|---|---|---|
| service owner | 用户旅程、SLO、发布优先级、业务降级 | 数据含义、底层恢复细节 |
| data owner | 数据语义、质量、保留目的、隐私分类 | 集群拓扑与数据库执行步骤 |
| platform owner | PostgreSQL/Pigsty、容量、备份恢复、平台执行 | 用户可接受的损失与业务优先级 |
| security/privacy | 访问政策、安全例外、证据披露、隐私升级 | 正常业务功能取舍 |
这四者不是四个超级管理员。理想状态恰恰相反:
service owner does not need database superuser
data owner does not need shell access
platform owner does not invent data meaning
security/privacy does not run every routine migration权限和责任要能分开。第 23 章已经把 login、runtime、owner 与 break-glass 角色拆开;本章把组织责任也做同样的拆分。
service owner
service owner 负责从用户视角定义合同:
journey:
place-order
eligible:
authorized, valid request admitted by the application
good:
success returned and idempotency token reconciles to one committed order
degradation:
stop new writes, preserve read-only status lookup它不能只写“数据库可用”。若应用在 PostgreSQL 正常时返回 500,用户仍然 没有获得服务。
service owner 还要在错误预算耗尽时决定:
- 暂停哪些功能发布;
- 哪些可靠性工作优先;
- 是否启用产品降级;
- 哪些业务风险值得例外。
这些决定必须与 platform owner 共同评审技术可行性,但不能完全下放给 DBA。
data owner
data owner 负责说明“一行数据代表什么”和“错误意味着什么”:
order_id 是否全局唯一
tenant_id 谁能看见
amount 使用什么货币和舍入规则
status 允许哪些状态迁移
committed order 用户是否已经获得不可撤销承诺恢复时,platform owner 可以证明 PostgreSQL 启动、system identifier 正确、 恢复目标已经到达;data owner 仍要判断:
- 目标时间是否符合业务容损;
keep应当存在、discard应当不存在是否足够;- 订单、库存和支付是否需要跨系统核对;
- 哪些差异要停止业务而不是继续恢复。
因此“数据库恢复成功”与“业务恢复可接受”是两个验收签名。
platform owner
platform owner 维护的是可交付数据库服务:
- Pigsty inventory 和渲染产物;
- PostgreSQL 初始化与运行参数;
- Patroni/etcd 角色与控制面;
- HAProxy/PgBouncer 入口;
- pgBackRest/WAL 恢复路径;
- 容量、升级、安全基线;
- 指标、日志、告警与值班执行。
它必须能把平台事实翻译成服务后果。例如:
pg-test-2 replay lag rises
-> affected path: replica reads
-> user consequence depends on read routing and freshness contract
-> primary writes may still be healthy
-> do not page the service owner merely because one cause metric is high平台 owner 也不能用“基础设施都绿”关闭事故。关闭条件应来自用户 SLI、 数据核对与恢复验证。
security/privacy owner
数据库治理产生敏感证据:
- 谁请求、批准和执行;
- 哪个身份获得了什么权限;
- 哪条查询失败;
- 哪个租户或事件受影响;
- 备份和日志保留多久;
- incident evidence 向谁披露。
security/privacy owner 决定访问、例外和披露边界。它不意味着把所有日志永久 保存。收集过多原始 SQL、bind value 或客户行,会让“审计系统”变成新的数据 泄露面。
incident commander 不是第五个永久 owner
incident commander 是事件期间的协调角色:
declares severity
maintains decision log
assigns technical lead
coordinates communication
checks stop conditions
decides next checkpoint
closes or hands off incident它可以由 service owner 职能担任,也可以按事件规模另行指定。技术 lead 负责执行和验证,不应同时独占业务影响判断、通信和所有批准。
使用职能身份,不把人名写死在合同里
服务卡中的:
{
"function": "platform",
"role_id": "database-platform-owner",
"on_call_route": "route://db-platform-oncall"
}描述稳定接口。真实系统再把 role_id 映射到:
- 身份目录 group;
- 当前 primary/secondary on-call;
- 可达的通知渠道;
- 时区与交接;
- 替补和 manager escalation。
只写“找小王”有三个问题:
- 人员变化会让文档瞬间过期;
- 不知道非工作时间找谁;
- 个人名字不能证明权限和当班状态。
反过来,只写一个永远没人测试的 db-oncall@example 也不算 owner。路由必须
通过 notification canary 定期证明可达。
不要把 RACI 做成责任迷宫
RACI 可以帮助列出 Responsible、Accountable、Consulted、Informed,但最常见 的失败是每个格子都填满:
five accountable owners
ten consulted teams
no person authorized to stop对一个决定,应当尽量有一个最终 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 都应回答三类问题:
decision
我能批准或拒绝什么?
evidence
我需要看见哪些事实?
absence
我不在时由哪条已测试路径接替?若答案只有“出了事群里讨论”,责任模型尚未建立。
24.1.2 等级、规格、依赖、值班与升级路径
服务等级是一组义务
gold、silver、bronze 如果没有义务,只是颜色:
Gold PostgreSQL
? on-call coverage
? SLO
? restore frequency
? RTO/RPO
? upgrade deadline
? security review
? capacity headroom
? evidence retention可执行的 tier 应当打包:
| 维度 | 需要固定的内容 |
|---|---|
| availability | 用户 SLI、目标、窗口、预算政策 |
| recovery | 恢复类型、证据新鲜度、演练频率 |
| support | 值班时段、确认目标、升级链 |
| change | 风险级别、批准、窗口、冻结规则 |
| capacity | headroom、预测周期、扩容 lead time |
| security | 认证、加密、轮换、审计与例外 |
| lifecycle | PostgreSQL/Pigsty 版本和升级时限 |
| evidence | 保留期、访问、完整性与删除 |
本章的 sandbox-reference 明确写:
production tier false
on-call illustrative route only
capacity no laptop sizing inference
restore one retained sandbox run
change L2/L3 independent authority
security mechanism evidence, production gaps retained这比给沙箱贴上“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 回答:
dependency fails
-> which journey is affected?
-> fail closed, fail open, degrade, or become unknown?
-> who owns first diagnosis?
-> what is the first safe action?例如 etcd 暂时不可用时,现任 primary 可能继续服务;真正受损的是安全 role change 能力。若把“etcd member down”直接等同于用户 outage,会制造误报; 若完全忽略,又会在需要 failover 时才发现控制面失效。
把共享故障域写进依赖
第 19 章的四台 VM 看起来有四个地址,但共享:
- 一台 laptop;
- 一个 hypervisor;
- 一套供电和网络;
- 同一底层存储;
- 单节点 etcd;
- 单一 MinIO/control host。
因此拓扑图中的三节点不能推出三个独立 failure domain。依赖模型应同时表达:
logical redundancy = 3 PostgreSQL members
physical independence = not established这也是为什么服务卡保留 known production gaps,而不是把它们埋在实验日志里。
值班路径需要输入与输出合同
一条值班 route 至少固定:
input
severity
service id
user impact
start time
alert id
dashboard
runbook
output
acknowledged by whom
incident id
technical lead
next update time
decision log确认时限不是修复时限:
acknowledge 5 minutes
!= resolve 5 minutes
!= RTO 5 minutesack 只证明有人接管。恢复目标必须按故障类别、数据边界和服务合同另行定义。
escalation 不是把同一消息抄送更多人
有效升级改变权力、资源或沟通:
SEV-2
service on-call + platform on-call
bounded user degradation
SEV-1
incident commander
data owner for correctness/recovery decision
security/privacy for breach or evidence handling
executive/customer communication when required升级触发可以来自:
- 影响范围扩大;
- 数据正确性或保密性受威胁;
- 错误预算快速燃烧;
- 操作超过停止线;
- recovery path 不确定;
- 事件超过时间阈值;
- 现有 authority 不足。
不要只按“CPU 超过 90%”升级。CPU 是原因候选,不是业务严重度。
依赖与升级的服务卡片段
本章完整文件见
service-card.json。其中一项依赖:
{
"id": "pgbackrest-repository",
"kind": "recovery-system",
"relationship": "stores base backup and WAL needed for recovery",
"failure_semantics": "a recent backup job does not prove the selected recovery point is usable",
"owner_function": "platform"
}owner_function 负责第一技术响应;最终恢复点是否可接受仍需要 data/service
owner。
服务目录的最小字段
identity
service_id / display_name / environment / catalog_status
purpose
business capability / customer journeys / critical operations
scope
data class / production boundary / database / cluster / entrypoints
ownership
service / data / platform / security
tier
explicit obligations
dependency
relation / failure semantics / owner
health
user contract and component layers
escalation
severity / trigger / role / acknowledgement
governance
SLO policy / SOP catalog / evidence policy / known gaps每次更改服务卡,应检查对应的 SLO、告警 route、SOP 与权限是否一起更新。
24.1.3 实例健康不等于业务服务健康
六层证明
本章使用六层健康链:
1 process
expected daemon exists
2 endpoint
declared client path is reachable and routes correctly
3 authentication
intended identity over intended transport can authenticate
4 transaction
intended operation can commit and its outcome can be reconciled
5 correctness
domain, tenant and authorization invariants hold
6 durability/recovery
acknowledged state survives declared failures and can be restored没有任何一层单独足够。服务健康还必须满足用户合同。
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。
因此:
pg_isready success
-> a PostgreSQL server at this endpoint is accepting connection attempts
not proven
-> intended application can authenticate
-> intended database and role are correct
-> intended transaction succeedsSELECT 1 证明得更多,但仍不够
使用真实入口、真实 TLS 约束、合成身份执行:
SELECT
current_database(),
session_user,
current_user,
pg_is_in_recovery(),
current_setting('server_version');能证明:
- 网络和 PostgreSQL 协议;
- 认证;
- database/role 身份;
- 当前节点 recovery 状态;
- 简单 query path。
它仍然绕过:
- 业务表;
- lock/constraint/RLS;
- write permission;
- commit;
- connection pool session semantics;
- 应用序列化和错误映射。
transaction probe 必须处理未知结果
更接近用户的 probe:
generate unique synthetic token
-> call declared application or database write path
-> commit
-> query token through declared result path
-> classify exactly one of:
committed once
absent
duplicate
unknown连接在 COMMIT 附近断开时,客户端可能不知道事务是否已经提交。直接重试会
造成重复。probe 和 Runbook 都必须使用幂等 token 核对结果。
第 20、22 章的切换演练正是按:
acknowledged
unknown committed
unknown absent
duplicate
unreconciled分类,而不是把所有连接错误都计为“数据库没写入”。
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 “时间延迟”会在空闲时误导
常见查询:
SELECT now() - pg_last_xact_replay_timestamp();在没有新 WAL 的空闲系统中,这个差值会持续增大,即使 replica 已完全追平。 它回答“最后一个已 replay transaction 的时间距现在多久”,不自动等于当前 replication lag。
对写后读新鲜度,应使用:
commit-correlated token
write through declared write path
record commit outcome
poll declared read path
measure visibility bound诊断时再结合:
- 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
可能出现:
Patroni:
one leader + two streaming replicas
but:
HAProxy still routes old leader
PgBouncer holds stale server connections
DNS/VIP is unreachable from application network
client certificate name mismatches
pool user is absent因此健康检查要从用户网络和声明入口执行。直接 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,用户指标正常
user availability healthy
freshness healthy
remaining redundancy degraded处理:
- 不必重复 page 所有业务 owner;
- platform ticket 或受控事件;
- 检查另一 replica、WAL 保留与恢复能力;
- 若 redundancy policy 已触及耐久性停止线,可升级。
情形 B:三台 PostgreSQL 都绿,下单 100% 失败
database component healthy
service availability failing处理:
- page service symptom;
- 检查应用 release、identity、pool entry、schema compatibility;
- 不要因为 PG dashboard 绿色而关闭;
- platform 指标用于定位原因。
情形 C:请求 99.999% 成功,但出现一条跨租户数据
ratio availability excellent
correctness control failed
severity potentially SEV-1处理:
- 冻结相关写入;
- 保留 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 | 窗口内用户结果 | 根因 |
这张表说明为什么需要两套视角:
SLI tells whether users are hurt
component telemetry tells why and what to do服务卡审查清单
-
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 与错误预算 · 查看全书目录 · 查看索引中心