跳至内容

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 尚未完成,可以:

  1. 先保存 initial pack;
  2. 15 分钟后补一个 immutable supplement;
  3. 不覆盖 initial;
  4. 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
reload

READ 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 address

top 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:30release eventversion B 完成high
10:03:00user SLIlatency short window 超阈值high
10:04:00PgBouncer metricwait queue 增长medium/high
10:04:20trace samplepool wait 占主要时间sampled
10:05:00PG activityactive/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 response

CPU 是 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 boundary

postmortem:

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 metricL0自动
bounded system-view aggregateL0自动
top queryid without textL0自动,有 timeout
blocker graph/PID detailL0/L1 data-sensitiveincident role
bounded log bodysensitive二级授权
raw SQL/bind exporthigh data risk禁止自动
EXPLAINlow/moderate评估 function/lock
EXPLAIN ANALYZEexecutes statement禁止自动
statistics resetdestructive observation禁止
load/fault injectionmutation隔离演练

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           1000

timeout 后:

-记录 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/version

log 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 authorization

metric 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     0600

reviewer 检查所有文件 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

本节验收

你应当能说明:

  1. 诊断包为什么先有 manifest;
  2. target 为什么要多源确认;
  3. 为什么同时保存 alert、collector 和 database clock;
  4. ratio 为什么必须带 event count;
  5. topology 与 change event 为什么要保存事件时刻版本;
  6. automatic pack 为什么只保存 queryid 聚合;
  7. T0/T1/T2/T3 四层 evidence 权限如何分开;
  8. symptom/correlate/hypothesis/cause 的措辞差异;
  9. 假设为什么必须带预测与反证;
  10. repair 为什么要回到 user/control signal 验证;
  11. EXPLAIN ANALYZE 为什么不能自动运行;
  12. timeout/limit/concurrency 如何让采集 fail-safe;
  13. 0700/0600 与 public allowlist 各解决什么;
  14. secret scan 为什么仍不能替代数据分类;
  15. 采集失败为什么必须是 incomplete/unknown 而不是 healthy。

上一节:Pigsty 可观测体系 · 返回本章目录 · 下一节:实战:实现并演练观察契约 · 查看全书目录 · 查看索引中心

最后更新于