2917 字
约 9 分钟
15
S14-A13 · 监控的两个用户:告警与排查共用一套指标

"为了排查,我们把指标加到了一千多个;告警还是天天误报。"这句抱怨里藏着两个用户在争同一份数据:告警系统要的是判据,排查的人要的是上下文。前者的诉求是低基数、高确定性、可自动判定;后者的诉求是高基数、可任意切分、能一路下钻到单个租户。把两者压进同一套指标、同一个聚合层级、同一个保留窗口,机制必然偏向一方,另一方被静默牺牲。

要回答的不是"该采什么指标",而是:共用一套机制时,被牺牲的是哪一个,以及它为什么不会在任何评审里被发现。


1. 两个用户,两套判据

告警的判据是可执行性:收到之后要做什么动作。答不出动作的告警不是"噪音大",而是从一开始就没有判据。排查的判据是能否缩小范围:看一眼数据能不能把嫌疑面砍掉一半。两条判据都不落在"指标数量"上,加指标对二者都不直接加分。

比较项 告警用户 排查用户
判据 收到后做什么(可执行性) 能否缩小范围(可分性)
时间尺度 分钟级窗口,持续 N 个周期 事件前后数小时到数天
对维度的态度 越少越好,每个标签都要有值域边界 越多越好,缺一个维度就断链
精度要求 高确定性,误报的代价是警报疲劳 高保真,容忍查询慢
需要的形态 预聚合值、跨实例合并 原始分桶、原始标签
失败形态 误报与漏报,立刻可见 排查时"维度不够",只能猜

关键的不对称在这里:**告警失败立刻可见(有人被叫醒),排查失败永远不可见(人只是多花两小时,然后再没人提)。**这是后面所有错配的根因,也是它难以被发现的原因。

判据是可以写进配置的,写不进去就说明还没想清楚:

// 有效的告警:判据 + 动作 + 边界,缺一项就不该存在
─── - alert: 支付失败率异常
───   expr: svc:http_5xx_ratio:5m{service="pay"} > 0.02
───   for: 3m                       // 持续时间是判据的一部分,不是保险
───   annotations:
───     action: 先看 downstream 依赖面板,再决定回滚或摘流
───     boundary: 大促期间基线另设,沿用日常阈值必然误报

2. 维度诉求相反:低基数与高基数

一条时间序列的身份是「指标名 + 标签集」。标签是有限资源,两个用户的要价完全相反:

// 告警要的形态:标签域小、取值可枚举、每个组合都有稳定语义
http_server_requests_seconds_count{service="order", status="5xx", uri="/api/pay"}

// 排查要的形态:能按 tenant_id / upstream_host 切开
http_server_requests_seconds_count{service="order", uri="/api/pay", tenant_id="T_8821"}

// ─── 把两种诉求放进同一个指标,就是基数爆炸的入口
标签 对告警的价值 对排查的价值 基数风险
service / status 高,判据靠它 中,只能定位到服务 低
uri(已归一) 高 中,定位到接口 低
uri(含路径参数、UUID) 负,判据随流量抖动 高 中–高
tenant_id / user_id 无,判据不关心 极高 极高
trace_id / request_id 无 极高 不应进指标,进日志或追踪

结论很直接:**user_id、request_id 属于排查,不属于告警。**为了排查把它们写进指标,告警侧的序列数就不是"多加几个指标",而是各标签值域相乘:

// 序列数的估算:不是加法,是乘法
基础指标      http_server_requests_seconds   × 1
+ status      {2xx,4xx,5xx}                  × 3
+ uri 归一    {约 200 个接口}                 × 200        → 千级
+ tenant_id   {约 5000 个租户}                × 5000       → 百万级
// ─── 千级时告警评估是秒级;百万级时单次评估可能超过评估间隔本身

3. 共用一套指标的三个后果

后果 触发条件 表面现象 谁被牺牲
误报 聚合被细分后每个切片样本量不足 长尾切片偶发抖动即触发 告警可信度
漏报 为压基数做降维,异常并进"其他" 异常淹在 uri="other" 里 排查能力
排查断链 写入时就丢掉了标签,事后无法恢复 "只能看总量,看不出是哪个租户" 排查能力

基数被拖垮的机制并不神秘:序列数等于各标签值域之积。uri 归一不彻底(典型量级:路径参数、UUID、时间戳进了标签),再叠一个 tenant_id,单个服务的活跃序列数从千级跳到百万级是常见的量级变化。后果不是"存储变贵",而是告警规则的评估周期被拉长、查询超时、部分序列被静默丢弃——最后一点最危险:数据不再完整,而没人知道缺的是哪一段。

写入侧降维配置(字段名按所用 Prometheus 版本核对)
─── metric_relabel_configs:
───   - source_labels: [__name__, uri]
───     regex: 'http_server_requests_seconds_count;.*/user/[0-9]+.*'
───     action: drop          // 丢弃 = 判断力永久消失,事后补不回来
───   - source_labels: [tenant_id]
───     action: labeldrop     // 降基数与保排查,只能二选一

这里必须说清被牺牲的是哪一个:如果为了告警压低基数,被牺牲的是排查,代价是"维度不够"——它只体现为排查变慢,不体现为任何故障;如果为了排查保留高基数,被牺牲的是告警,代价是误报与评估延迟——它至少还会被抱怨。两种牺牲里,前一种更常见,也更难被发现,因为没有任何一个时刻会有人为"少了一个维度"负责。

度量 归属 说明
告警可执行率 告警用户 能回答"收到后做什么"的告警占比
误报率 告警用户 按同类事件统计,不按告警条数
排查时维度命中率 排查用户 下钻所需标签存在的比例
活跃序列数增长率 共同的预算 与流量增长脱钩即为异常

4. 一套采集,两套视图

分离两个用户不等于建两套采集。可持续的做法是写入侧一次采集,读侧两套视图:

// 视图 A:告警用。预计算、低基数、序列名稳定
─── recording rule(评估间隔按实际核对):
───   - record: svc:http_5xx_ratio:5m
───     expr: sum(rate(http_server_requests_seconds_count{status="5xx"}[5m]))
───           / sum(rate(http_server_requests_seconds_count[5m]))

// 视图 B:排查用。保留原始分桶与完整标签,保留期短,不参与告警
─── 原始 histogram + 完整标签集,只在下钻时查询
取舍 告警侧要求 排查侧要求 冲突点
聚合层级 预聚合值,跨实例合并 原始分桶与实例级标签 预聚合抹掉分片差异
保留期 长而粗(判据看趋势) 短而细(事件窗口) 同一存储两头不讨好
采样 可接受,判据看统计量 危险,边缘样本可能就是证据 采样率应按排查侧定
收敛 必须去重、抑制、分组 有损,被抑制的那条可能正是根因 收敛规则要能回答被吃掉的是什么

同一错配的镜像形态是用日志做告警、用指标做排查:日志告警的成本随流量线性上涨(写入、索引、扫描),判据也不稳定;指标做排查则维度天生不够。这个分工不是偏好问题,是判据决定的。

把两个视图真正分开,读侧的差别只有几行:

// 告警视图:只查预聚合序列,查询代价与实例数无关
svc:http_5xx_ratio:5m{service="pay"}

// 排查视图:查询时才展开标签,代价高但只在人在场时发生
topk(10, sum by (tenant_id, uri) (
  rate(http_server_requests_seconds_count{service="pay", status="5xx"}[10m])))
// ─── 这条查询不能进告警规则:它慢、结果不稳定、且随时可能超时

这里有一条容易被忽略的约束:**排查视图的保留期可以短,但它的标签必须在写入时就已存在。**事后想补维度,只能重跑历史流量;而历史流量不会重演。

5. 排错:五种常见情形

现象 真实原因 处理方向
告警"每次都能看出问题但没人处理" 判据缺失,只描述现象不指向动作 每条告警补一行"第一步做什么",补不出的下线
同一故障反复告警几十条 无分组抑制,或分组键用了高基数标签 分组键限定在基数可控的标签上
排查时只能看到"总计" 写入侧做过 labeldrop 或预聚合 细分需求下沉到排查视图,不能反向改告警指标
采集序列数突然翻倍 高基数标签进了历史指标 先定位是哪个标签的哪个值域,再决定丢弃或改写
告警比故障晚十几分钟 评估间隔与 for 被高基数拖长 核对单次评估耗时,拆规则或缩窗口

6. 边界:什么时候不值得分离

场景 结论 理由
系统极小,告警只有三条 不分离 三条判据人眼可维护,两套视图的运维成本高于收益
排查靠直连机器、现场看日志 不分离 排查用户不吃指标,维度诉求不冲突
单租户、单实例 不分离 标签域几乎无基数,两个用户诉求重合
峰值与均值差异小、无尖峰 不分离 低基数聚合已足够表达判据

判断这套分离是否值得,只看四个量:告警可执行率、误报率、排查时维度命中率、活跃序列数增长率。前两个衡量告警用户,第三个衡量排查用户,第四个是两者共同的预算线。


自测

  1. 一条告警答不出"收到之后做什么",它缺的是哪一类判据?
  2. user_id 为什么不该出现在告警指标的标签里,代价具体落在哪一步?
  3. 基数爆炸先伤到的是存储成本,还是告警规则的评估?
  4. 一份指标要同时服务两个用户,分离点放在写入侧还是读侧?
  5. 告警收敛规则在什么情况下会吃掉排查需要的那条记录?
  6. 什么规模与什么数据形态下,"两个用户"这个划分本身不成立?
S14-A13 · 监控的两个用户:告警与排查共用一套指标
https://www.clxhxhhr.top/posts/6356/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。