25.6 从告警到诊断包
告警只告诉你一个合同条件成立。它不是事故报告,也不是根因分析。
值班最先丢失的往往不是 metric,而是上下文:
onset 前有哪些变更?
当时谁是 primary?
哪些入口受影响?
长窗和短窗的 event count 是多少?
锁图在三分钟后消失前是什么样?
统计是否刚 reset?
采集链有没有延迟?
日志保留窗口还在吗?
值班执行的第一条查询是否改变了状态?诊断包的任务,是在不增加事故的前提下,自动保存一个有界、脱敏、带身份与 时间语义的起点:
alert
-> manifest
-> symptom windows
-> topology and changes
-> PostgreSQL aggregates
-> platform/host correlation
-> hypotheses and falsifiers它不能承诺:
captured everything
proved root cause
safe to mutate
compliance achieved机器可读合同:
diagnostic-pack-contract.json。
25.6.1 自动保存时间窗、拓扑、变更与关键查询
诊断包从 manifest 开始
任何包先记录:
{
"run_id": "...",
"captured_at": "...Z",
"trigger_alert": "...",
"service": "pg36_shop",
"environment": "l2-sandbox",
"targets": ["pg-test-1"],
"tool_versions": {},
"source_hashes": {},
"risk": "L0",
"mutation": "none"
}没有 manifest 的截图无法回答:
-哪次事件; -哪个环境; -哪一版 query; -谁采集; -何时采集; -是否做了 mutation; -是否后来被改。
target 要通过多源确认
本章正式采集没有相信工作站 SSH alias,而是:
ProxyJump via pg-meta-1
-> actual sandbox network 10.10.10.11
-> hostname pg-test-1
-> Patroni scope pg-test, name pg-test-1
-> PostgreSQL cluster_name pg-test
-> pg_is_in_recovery false
-> two pg_stat_replication rows如果其中冲突:
stop
classify identity mismatch
do not continue to mutation连接成功不是身份验证。
时间窗要覆盖 onset 前后
本章合同默认:
before alert 30m
after alert 15m
clock UTC这不是 universal window。选择应考虑:
-最长 burn window; -scrape/evaluation/group delay; -release duration; -query/log retention; -incident severity; -存储成本。
至少保存:
alert starts_at
rule evaluation time
collector time
database clock
change event time如果 after window 尚未完成,可以:
- 先保存 initial pack;
- 15 分钟后补一个 immutable supplement;
- 不覆盖 initial;
- manifest 建立 parent/child。
保存 event count,不只保存 ratio
同样 10% bad ratio:
1 bad / 10 total
10,000 bad / 100,000 total动作和置信度不同。SLO section 保存:
-good/bad/eligible count 或 rate; -long/short window; -threshold; -burn; -low-traffic flag; -missing/freshness; -independent probe; -objective version。
不要只截图红线。
topology 是事件时刻拓扑
保存:
service entrypoints
HAProxy/PgBouncer path
cluster/member role
Patroni timeline
replication state
declared dependencies
read/write routing不自动保存:
-client address; -password; -connection URI; -inventory secret。
IP 是否允许进入私密包取决于政策;本章公开摘要只保留教学网络身份。
变更时间线
收集:
application release
schema migration
PostgreSQL parameter/config
pool size/mode
HAProxy route
failover/switchover
node restart
backup/restore
monitoring rule/dashboard
credential/security policy
capacity/retention每个 event:
event id
actor role
target
requested/approved/executed times
result
rollback/roll-forward
evidence link不要直接收集个人聊天全文作为唯一 change log。
PostgreSQL section
本章自动保存:
activity aggregate by client state/wait type
lock aggregate
replication identity/state/gap without client address
pg_stat_wal
pg_stat_checkpointer
pg_stat_archiver
nonzero pg_stat_io aggregate
database aggregate
maintenance aggregate
pg_stat_statements aggregate/reset, no query text为什么 activity 只自动聚合:
-完整 query text 敏感; -PID 列表可能很大; -一次 current sample 很快过时; -自动证据的目标是安全起点。
若 incident 需要 blocker graph,runbook 再用受限角色采集二级包。
SQL query controls
采集连接先执行:
SET statement_timeout = '5s';
SET lock_timeout = '500ms';
SET default_transaction_read_only = on;
BEGIN READ ONLY;并禁止:
EXPLAIN ANALYZE
pg_stat_reset*
pg_stat_statements_reset
VACUUM / ANALYZE
CHECKPOINT
cancel/terminate
DDL/DML
load generation
failover
restore
reloadREAD ONLY 不是绝对安全证明:
-SELECT 仍可很贵; -function 可能具有副作用,具体取决于实现与权限; -系统视图查询可拿锁; -external extension 可能访问资源。
因此同时使用 allowlisted query、timeout 和 least privilege。
queryid-level top list
自动保存:
dbid
userid or role class
queryid
toplevel
calls
total/mean execution
rows
blocks/temp/WAL
stats_since/minmax_stats_since限制:
top 50
no query text
no bind
no client addresstop 50 不是完整 workload。manifest 要写:
sort key
limit
window/reset
source member平台 section
保存:
-exporter/target freshness; -VictoriaMetrics health/series; -VMAlert groups/rules/error/missed iteration; -Alertmanager failure counter; -node resource trend; -日志 query count,不含 body; -component versions。
本章公开摘要包括:
VMAlert 17 groups
50 alert rules
698 recording rules
0 rule errors它是采集时刻 baseline,不能事后覆盖。
文件布局
建议:
incident-<id>/
├── manifest.json
├── symptom.json
├── topology.json
├── changes.json
├── postgresql.json
├── platform.json
├── hypotheses.json
├── validation.json
└── hashes.json正文和 body 类材料如确需保存,放在更严格的 evidence tier,不与常规诊断包 混合。
分区大小上限
本章每 section:
manifest 64 KiB
symptom 256 KiB
topology 256 KiB
changes 256 KiB
postgresql 1 MiB
platform 1 MiB
hypotheses 256 KiB超限时:
-不应无限截断而不标记;
-记录 truncated=true;
-保存 total count;
-使用 top-N;
-提供 restricted source link;
-按需发起二级采集。
初始包与后续包
T0 automatic
cheap, bounded, no text
T1 operator
focused blocker/query/log under incident authority
T2 mutation evidence
before/after, approval, command, verification
T3 postmortem
decisions, cause, counterfactual, actions不要让 T0 自动化拥有 T2 权限。
source hash 与可重放
保存:
-collector version; -query template hash; -rule/config hash; -upstream governance run id; -output hash; -capture parameters。
这样以后能回答:
same data, same query?
same rule version?
same target?hash 证明 byte identity,不证明内容真实或政策正确;仍需身份和 source trust。
25.6.2 区分首发症状、伴随现象与根因证据
先写 timeline,不先写故事
事实表:
| 时间 | 来源 | 事实 | 置信度 |
|---|---|---|---|
| 10:01:30 | release event | version B 完成 | high |
| 10:03:00 | user SLI | latency short window 超阈值 | high |
| 10:04:00 | PgBouncer metric | wait queue 增长 | medium/high |
| 10:04:20 | trace sample | pool wait 占主要时间 | sampled |
| 10:05:00 | PG activity | active/wait 未显著变化 | point sample |
先不要写:
version B caused database slowdown事实支持的初步说法:
latency symptom began after version B;
pool wait correlated;
current PostgreSQL execution evidence did not show the same growth.四种状态标签
symptom
contract violation directly observed
correlate
same window changed, causal role unknown
hypothesis
proposed mechanism with predicted evidence
cause
mechanism corroborated, alternatives tested, repair verified诊断包中的每条观察带 role,避免相关线自动升级成 root cause。
首发症状不是第一个 dashboard spike
“first observed” 受:
-scrape interval; -evaluation interval; -threshold; -日志延迟; -时钟; -采样; -dashboard refresh
影响。可以说:
first reliable observation available to this system不要说“绝对第一个事件”,除非证据支持。
假设要写预测与反证
模板:
hypothesis: pool configuration reduced reusable server connections
mechanism: requests queue before PostgreSQL
predicts:
- PgBouncer wait rises
- edge latency rises
- PostgreSQL active executions do not rise equally
- host CPU/I/O remains stable
falsified_by:
- no pool wait
- server execution time explains latency
- rollback does not change symptom
next_safe_query:
- inspect pool wait and config event
mutation_required: false这比“看起来像连接池”更可执行。
反例一:CPU 同时升高
availability fast burn
CPU high可能机制:
-应用 retry storm 让 CPU 高; -slow query 让 CPU 高并导致错误; -batch 与 outage 巧合; -monitoring query 自身; -另一 process。
需要:
queryid active work
wait state
request/transaction rate
process CPU
release/batch event
repair responseCPU 是 correlate,不是自动 cause。
反例二:replica gap 与 stale read
freshness probe bad
replica WAL gap high强候选,但仍检查:
-probe 确实走 replica; -token commit 已确认; -clock; -route; -cache; -transaction snapshot; -replica state/timeline。
如果切 primary path 后新 token 立即满足,而 gap 回落后 replica path 恢复, 机制更强。
反例三:归档失败计数
failed_count=21时间线:
last failure 18:57
last success 22:27
current capture 22:48结论:
historical failures occurred
archive later succeeded
no active failure is established by this counter alone这是 falsification:反驳“非零 counter = 当前故障”。
反例四:锁已经消失
用户 latency 曾受 lock 影响,但初始包晚到:
current pg_locks no blocker
deadlock/lock-wait log in window
application timeout in window不能因为 current view 空就否认历史;也不能因为日志有等待就证明当前仍阻塞。 timeline 必须保留各自时间语义。
修复验证要回到症状
如果动作是 cancel blocker:
blocker disappears只是组件验证。还要:
-user burn short/long window; -event outcome; -unknown writes reconciliation; -correctness; -pool/entry; -副作用; -recurrence。
动作可能同时让 query 消失和用户失败增加。
recovery 不是瞬时绿
fast rule 的两个窗口恢复速度不同:
5m clears first
1h remains elevated关闭事件政策可要求:
-短窗恢复; -长窗下降趋势/低于阈值; -independent probe; -correctness check; -no new unknown outcome; -repair stable for observation period。
不要等所有长窗完全归零,也不要一个样本就关。
counterfactual
root cause review 要问:
如果没有这次变更,症状是否仍会发生?
如果只改变这个机制,症状是否恢复?
为什么监控提前没有发现?
哪个 guardrail 能阻止复发?不能真实重放生产时,可以:
-staging reproduction; -synthetic fixture; -plan/config diff; -canary; -independent domain evidence。
明确证据强度。
诊断包不是 postmortem
诊断包:
raw-ish bounded facts
initial hypotheses
capture boundarypostmortem:
impact
timeline
cause/contributing factors
detection/response
decisions
counterfactual
actions/owners/dates不要在自动包里预填 root_cause=database。
25.6.3 证据采集本身的负载和权限边界
observer effect
采集会:
-建立连接;
-拿 AccessShareLock;
-读取 shared stats;
-执行 sort/aggregate;
-查询 storage;
-扫描日志;
-占网络与磁盘;
-出现在 pg_stat_activity/pg_stat_statements;
-影响被观察系统。
目标是有界,不是声称零影响。
风险分级
| 采集 | 风险 | 默认 |
|---|---|---|
| health/version/small metric | L0 | 自动 |
| bounded system-view aggregate | L0 | 自动 |
| top queryid without text | L0 | 自动,有 timeout |
| blocker graph/PID detail | L0/L1 data-sensitive | incident role |
| bounded log body | sensitive | 二级授权 |
| raw SQL/bind export | high data risk | 禁止自动 |
EXPLAIN | low/moderate | 评估 function/lock |
EXPLAIN ANALYZE | executes statement | 禁止自动 |
| statistics reset | destructive observation | 禁止 |
| load/fault injection | mutation | 隔离演练 |
EXPLAIN ANALYZE 会执行
对 DML:
EXPLAIN ANALYZE DELETE ...真的执行 DELETE,除非在可回滚事务中且没有外部副作用;即便 rollback,也会:
-运行 trigger/function; -拿锁; -产生 WAL/side effect; -消耗资源; -影响 sequence/external service 等。
诊断自动化不得把它当只读。
function 与 extension
SELECT 可以调用:
-volatile function; -security definer; -external API; -advisory lock; -file/extension; -sleep。
allowlist system catalog/view query,不接受 incident 参数拼任意 SQL。
query timeout
本章:
statement_timeout 5s
lock_timeout 500ms
parallel capture max 4
top query 50
log rows 1000timeout 后:
-记录 section incomplete; -保存 error class; -不在 tight loop 重试; -不自动提高 timeout; -交给 operator 决定替代 source。
并发限制
对 100 个 database 同时执行 catalog query,单条很轻也会形成 load spike。
策略:
priority:
affected service/database first
concurrency:
fixed small pool
jitter:
avoid synchronized collectors
deadline:
stop when incident value decays
cache:
reuse inventory/versionlog query cost
无界:
all clusters
30 days
regex on full message
no limit会拖慢 VictoriaLogs,也可能返回大量敏感正文。
有界:
exact cls/ins/database
45m window
severity/SQLSTATE structured filter
limit 1000
count/group first
body only under secondary authorizationmetric query cost
危险模式:
-regex 匹配所有 metric;
-高基数 query label 长窗口;
-subquery 小 step;
-topk 前未聚合;
-cross join;
-dashboard 多 panel 同时 refresh;
-incident automation 重复相同查询。
记录:
-expression hash; -start/end/step; -series count; -duration; -result size; -timeout/error。
权限
数据库:
machine exporter
dedicated monitoring role, stable views
automatic diagnostic
aggregate-only allowlist
interactive incident
time-bounded pg_read_all_stats or narrower
mutation operator
separate role/approval监控平台:
metric read
log metadata read
log body read
rule edit
silence create
receiver secret
admin不应是一个万能账号。
evidence 文件权限
本章 private bundle:
directory 0700
files 0600reviewer 检查所有文件 mode。公共仓库只放 allowlist summary:
-版本; -计数; -pass/fail; -declared gaps; -run id; -production gate。
evidence secret scan
自动扫描:
-SCRAM verifier;
-private key;
-credential-bearing URI;
-authorization header;
-clear password JSON;
-query/raw_sql/sql_text 字段。
扫描通过不证明绝对无秘密:
-未知格式; -encoded value; -业务 PII; -hash 可重识别; -file name。
仍要 data classification 与人工 review。
retention
本章 private evidence 教学默认 30 天;生产需独立政策。决定:
-事件/合规需求; -敏感程度; -调查周期; -删除; -legal hold; -backup; -访问 audit; -hash/manifest。
不要因为是“监控证据”永久保存。
完整性与可更新性
原始 initial pack 不覆盖。修正错误时:
new supplement
references original run_id/hash
states correction reason
preserves original公开摘要可以更新展示,但应保留 underlying immutable run reference。
failure-safe behavior
若 capture 失败:
do not report empty as healthy
mark section incomplete
record error without secret
continue independent low-cost sections
page metamonitoring if critical path
do not mutate database to make capture pass本章 validator 对 application SLI absence 就是:
declared expected gap而不是失败或绿色。
自动化权限不能随事故升级
事故 severity 变高,不代表 collector 自动获得:
-superuser; -SSH root; -raw log body; -query text export; -failover; -terminate; -restore。
需要新 authority 的动作必须停下来进入 SOP/change plan。
诊断入口输出
给第 31 章的输入:
manifest identity/time/version
user symptom and windows
entrypoint/path
PostgreSQL state/counters/reset
top queryid aggregates
topology/change timeline
host/platform correlation
known gaps
hypotheses and falsifiers
permissions/risk第 31 章再按症状分支:
slow query
lock/deadlock
connection/pool
replication/freshness
disk/capacity
vacuum/freeze本节验收
你应当能说明:
- 诊断包为什么先有 manifest;
- target 为什么要多源确认;
- 为什么同时保存 alert、collector 和 database clock;
- ratio 为什么必须带 event count;
- topology 与 change event 为什么要保存事件时刻版本;
- automatic pack 为什么只保存 queryid 聚合;
- T0/T1/T2/T3 四层 evidence 权限如何分开;
- symptom/correlate/hypothesis/cause 的措辞差异;
- 假设为什么必须带预测与反证;
- repair 为什么要回到 user/control signal 验证;
EXPLAIN ANALYZE为什么不能自动运行;- timeout/limit/concurrency 如何让采集 fail-safe;
0700/0600与 public allowlist 各解决什么;- secret scan 为什么仍不能替代数据分类;
- 采集失败为什么必须是 incomplete/unknown 而不是 healthy。
上一节:Pigsty 可观测体系 · 返回本章目录 · 下一节:实战:实现并演练观察契约 · 查看全书目录 · 查看索引中心