3、告警规则引擎:规则匹配逻辑、阈值设定、聚合与抑制策略
告警规则引擎,说白了就是整个监控系统的「大脑」。你采集了数据,存到了时序数据库,但什么时候该报警?怎么报?报给谁?这些决策逻辑,全由规则引擎来定。
我刚开始做监控系统时,以为告警就是「CPU > 90% 就发短信」。结果呢?半夜三点被电话吵醒,一看是某台测试机跑了个压测脚本。嗯,从那以后我才明白——规则引擎的设计,直接决定了你是被团队当成「守护神」,还是被当成「狼来了」的那个家伙。
3.1 规则匹配逻辑:告警是怎么「命中」的?
规则匹配,就是判断「当前数据是否满足告警条件」。这里面有几个关键点,我一个个说。
3.1.1 标签匹配与维度过滤
你想想看,一个监控系统里可能有几千台机器、几百个服务。如果每条规则都去匹配所有数据,性能肯定扛不住。所以,规则引擎首先要做的是「标签匹配」。
举个例子,Prometheus 的告警规则是这样写的:
groups:
- name: example
rules:
- alert: HighCPUUsage
expr: cpu_usage_percent{env="production", instance=~"web.*"} > 90
for: 5m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} CPU 使用率过高"
这里的 {env="production", instance=~"web.*"} 就是标签匹配。只有生产环境的、以 web 开头的实例,才会触发这条规则。测试环境的机器再高也不会报警。
3.1.2 时间窗口与持续条件
光匹配到还不够,还得看「持续多久」。这就是 for 关键字的作用。
为什么要加这个?因为瞬时抖动太常见了。比如网络偶尔延迟一下,CPU 突然飙高几秒钟,这些都不值得报警。我个人习惯是:
- 关键业务指标:持续 1 分钟就报警
- 基础设施指标:持续 5 分钟再报警
- 日志类告警:出现一次就报警(因为日志本身已经聚合过了)
我曾经犯过一个错:把磁盘告警的 for 设成了 10 分钟。结果磁盘真的满了,但因为是缓慢增长,等到 10 分钟后触发告警时,业务已经挂了 5 分钟。嗯,这个教训让我学会了——持续条件要根据指标的变化速度来定。
3.2 阈值设定:静态阈值 vs 动态阈值
阈值怎么设?这是每个运维人都头疼的问题。设太松,漏报;设太紧,告警疲劳。
3.2.1 静态阈值
最直接的方式,就是写死一个数字。比如:
- CPU > 90%
- 内存 > 85%
- 磁盘 < 10% 剩余
静态阈值的好处是简单、直观。但问题也很明显——不同业务、不同时段,阈值应该不一样。比如凌晨的业务低谷期,CPU 到 80% 可能就有问题;但白天高峰期,90% 可能都算正常。
3.2.2 动态阈值
动态阈值,就是让系统自己「学习」正常范围。常用的方法有:
- 基于百分位数:比如取过去 7 天同一时刻的 P99 值作为基线
- 基于标准差:正常值在均值 ± 3σ 范围内,超出就算异常
- 基于时间序列预测:用 ARIMA、Prophet 等模型预测下一时刻的值
举个例子,用 Prometheus 配合动态阈值:
# 计算过去 7 天同一时刻的 P95 值
- record: job:request_latency:p95_7d
expr: quantile_over_time(0.95,
request_latency_seconds[7d:5m] offset 1w)
# 告警规则:当前值超过基线 2 倍
- alert: LatencyAnomaly
expr: request_latency_seconds >
job:request_latency:p95_7d * 2
动态阈值不是万能的。我遇到过一种情况:某个服务在搞大促,流量是平时的 10 倍,动态阈值自动「学习」成了高值,结果真正的异常反而被淹没了。所以,动态阈值一定要配合「上限」和「下限」来用。
3.3 聚合策略:减少告警风暴
告警风暴,是每个运维人的噩梦。一台交换机挂了,可能触发几百条告警。这时候,聚合策略就派上用场了。
3.3.1 时间聚合
把一段时间内的同类告警合并成一条。比如:
- 5 分钟内,同一台机器的 CPU 告警只发一次
- 10 分钟内,同一个服务的错误率告警只发一次
我个人习惯用「滑动窗口」来做时间聚合。窗口大小一般设为告警评估周期的 2-3 倍。
3.3.2 空间聚合
把同一维度下的多个告警合并。比如:
- 「机房 A 的 10 台机器磁盘满了」→ 合并成一条「机房 A 磁盘告警(10 台)」
- 「服务 B 的 5 个实例延迟高」→ 合并成一条「服务 B 延迟告警(5 个实例)」
空间聚合的关键是「维度选择」。我建议按以下优先级来聚合:
| 优先级 | 聚合维度 | 示例 |
|---|---|---|
| 1 | 故障根因 | 交换机挂了 → 所有依赖它的机器告警合并 |
| 2 | 业务维度 | 同一个服务的所有实例告警合并 |
| 3 | 地理位置 | 同一个机房/区域的告警合并 |
| 4 | 告警类型 | 所有 CPU 告警合并、所有内存告警合并 |
3.4 抑制策略:避免重复告警
抑制,就是「如果某个告警已经触发了,相关的其他告警就先别发了」。这跟聚合有点像,但逻辑不同——聚合是合并,抑制是「闭嘴」。
3.4.1 依赖抑制
最典型的场景:数据库挂了,所有依赖数据库的服务都会报错。这时候,只需要发一条「数据库挂了」的告警,其他服务的告警全部抑制掉。
在 Prometheus 中,可以用 inhibit_rules 来实现:
inhibit_rules:
- source_match:
severity: 'critical'
alertname: 'DatabaseDown'
target_match:
severity: 'warning'
equal: ['service']
这个规则的意思是:如果某个服务有「DatabaseDown」的严重告警,那么该服务的所有 warning 级别告警都先别发了。
3.4.2 重复抑制
同一个告警,在短时间内重复触发怎么办?比如网络抖动,可能 1 分钟内触发 3 次又恢复 3 次。这时候,可以用「重复抑制」:
- 同一个告警在 30 分钟内只发一次
- 告警恢复后,30 分钟内不再触发
我个人习惯把重复抑制的时间设成告警评估周期的 3 倍。比如评估周期是 1 分钟,重复抑制就设 3 分钟。
3.5 知识体系总览
下面这张图,是我对告警规则引擎的整体理解。你可以把它当成一个「决策流程」来看:
这张图把整个流程串起来了。从数据输入开始,经过规则匹配、阈值判断,再到聚合和抑制,最后输出告警通知。每一步都有坑,每一步也都有优化空间。
3.6 实战建议
最后,我总结几条实战中的建议:
- 先有基线,再有阈值。 别拍脑袋设数字,至少看一周的历史数据。
- 聚合和抑制要配合使用。 聚合减少数量,抑制避免重复,两者缺一不可。
- 动态阈值不是银弹。 它适合周期性强的指标,不适合突增突降的业务。
- 规则要分层。 基础设施一层、业务一层、用户体验一层,每层的阈值和聚合策略都不一样。
- 定期复盘告警。 我每季度都会拉一次告警数据,看看哪些是误报、哪些是漏报,然后调整规则。
告警规则引擎的设计,说到底是个「平衡」的艺术。太敏感,团队疲劳;太迟钝,业务受损。找到那个平衡点,需要经验,也需要持续迭代。嗯,这部分内容就到这里,希望对你有所帮助。
公众号:蓝海资料掘金营,微信deep3321