第二章 故障发现机制:告警风暴治理、告警收敛策略、基于阈值的检测、基于AI的异常检测
故障发现,是整个自愈体系的第一道关卡。你想想看,如果连故障都没发现,后面的自动化恢复就是空谈。但现实往往很残酷——告警太多,反而淹没了真正的故障。我见过太多团队,每天收到几万条告警,最后干脆把告警群屏蔽了。这哪是监控?这是噪音。
所以这一章,我们来聊聊怎么把告警变少、变准、变快。说白了,就是三个目标:该响的响,不该响的不响,响了就能定位。
2.1 告警风暴:为什么你的手机半夜总在响?
告警风暴,我给它下个定义:短时间内大量重复、无效、或关联性极低的告警同时爆发。这种现象在微服务架构里尤其常见。一个底层服务抖动,上游几十个服务跟着报错,瞬间几千条告警刷屏。
我在项目中遇到过最夸张的一次:某电商大促期间,数据库连接池耗尽,结果引发了2000多个微服务同时报“上游服务不可用”。运维同学手机直接卡死,等重启完再看,故障已经持续了15分钟。
- 告警量在短时间内呈指数级增长
- 大量告警内容重复或高度相似
- 真正的根因告警被淹没在噪音中
- 值班人员产生“告警疲劳”,开始选择性忽略
为什么会这样?根本原因在于:告警之间缺乏关联,每个服务都在独立发声。你想想看,一个故障就像一棵树倒了,但每个树枝都在喊“我倒了”,而不是告诉你“树根断了”。
2.2 告警收敛策略:从千条到一条
告警收敛,不是简单地减少告警数量,而是把相关的告警合并成一条有上下文的信息。我常用的策略有四种,下面逐个说。
2.2.1 基于规则的收敛
这是最基础的做法。比如:同一个IP、同一个告警类型,5分钟内只发一次。或者,当CPU使用率超过90%时,不再重复发送80%的告警。
# 示例:基于规则的告警去重配置(Prometheus + Alertmanager)
groups:
- name: cpu_alerts
rules:
- alert: HighCpuUsage
expr: node_cpu_seconds_total{mode="idle"} < 10
for: 5m
labels:
severity: critical
annotations:
summary: "CPU使用率超过90%"
description: "实例 {{ $labels.instance }} CPU使用率持续超过90%"
# Alertmanager 中的分组配置
route:
group_by: ['alertname', 'instance']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
嗯,这里要注意:repeat_interval 别设太短。我曾经设成10分钟,结果一个告警一天重复144次,值班同学差点把我拉黑。
2.2.2 基于依赖关系的收敛
微服务架构里,服务之间有调用链。如果A调用B,B挂了,A报错。这时候,你应该只发B的告警,A的告警自动抑制掉。
2.2.3 基于时间窗口的收敛
有些告警是瞬时的,比如网络抖动。如果1分钟内恢复了,根本没必要通知人。我习惯的做法是:设置一个“观察窗口”,比如5分钟。告警触发后,先进入“待确认”状态,5分钟后如果还没恢复,再升级为“正式告警”。
2.2.4 基于事件关联的收敛
这是比较高级的做法。把多个告警事件关联起来,生成一个“故障事件”。比如:
| 原始告警 | 关联后 |
|---|---|
| 主机A CPU 100% | 故障事件:主机A宕机 影响范围:服务X、Y、Z 根因:硬件故障 |
| 主机A 内存OOM | |
| 服务X 调用超时 |
说白了,就是把一堆碎片拼成一张完整的图。我见过做得好的团队,一个故障只发一条告警,里面包含了根因、影响范围、甚至建议的恢复步骤。
2.3 基于阈值的检测:简单但有效
阈值检测,是故障发现最基础的手段。但别小看它,用好了能解决80%的问题。我总结了一套“三层阈值”的方法:
- 预警阈值(Warning):比如CPU 70%。这时候系统还能正常工作,但需要关注。通常发到运维群,不用打电话。
- 严重阈值(Critical):比如CPU 90%。系统已经受影响,需要立即处理。这时候要打电话、发短信。
- 灾难阈值(Disaster):比如CPU 95%以上。系统濒临崩溃,需要启动应急预案。
动态阈值的实现,其实不复杂。可以用滑动窗口计算过去7天同一时间段的平均值,然后加上3倍标准差作为阈值。代码示例如下:
# 动态阈值计算示例(Python伪代码)
import numpy as np
def calculate_dynamic_threshold(metric_name, current_time):
# 获取过去7天同一时间段的指标数据
historical_data = get_historical_data(metric_name,
start_time=current_time - 7*24*3600,
end_time=current_time)
# 计算均值和标准差
mean = np.mean(historical_data)
std = np.std(historical_data)
# 阈值 = 均值 + 3倍标准差
warning_threshold = mean + 2 * std
critical_threshold = mean + 3 * std
return warning_threshold, critical_threshold
2.4 基于AI的异常检测:让机器帮你找问题
阈值检测有个硬伤:只能发现已知的异常模式。但现实中的故障,往往是不按套路出牌的。比如,某个接口的响应时间突然从10ms变成50ms,虽然没超过阈值,但已经说明有问题了。
这时候,AI异常检测就派上用场了。我常用的方法有三种:
2.4.1 时序异常检测
说白了,就是让模型学习指标的历史规律,然后判断当前值是否“反常”。常用的算法有:
- 3-Sigma:最简单,适合正态分布的数据
- 移动平均法:适合有周期性规律的数据
- Prophet:Facebook开源的时序预测工具,能处理节假日等特殊日期
- LSTM:深度学习模型,适合复杂的时间序列
我个人习惯先用Prophet做快速验证。它不需要太多调参,而且能自动识别周周期和年周期。比如,某个接口在周一上午10点流量暴涨,Prophet会认为这是正常的,不会误报。
2.4.2 日志异常检测
日志里藏着大量信息。但人工看日志?不现实。我见过一个团队,每天产生10TB日志,靠人根本看不过来。
我的做法是:先用NLP技术把日志转成向量,然后用聚类算法找出“异常模式”。比如,突然出现大量“Connection refused”的日志,而且集中在同一台机器上,那大概率是网络问题。
# 日志异常检测简化流程
1. 日志采集 -> 2. 日志解析(正则提取关键字段)
3. 向量化(Word2Vec / TF-IDF)
4. 聚类(DBSCAN / K-Means)
5. 异常模式识别(与历史模式对比)
6. 生成告警
2.4.3 多维指标关联分析
单个指标异常,不一定代表故障。但多个指标同时异常,那基本可以确定了。比如:CPU高 + 内存高 + 响应时间长 + 错误率上升,这四个指标同时出现,99%是系统出问题了。
我常用的方法是PCA(主成分分析)。把几十个指标降维成几个主成分,然后监控主成分的变化。如果某个主成分突然偏离正常范围,就触发告警。这样做的好处是:能发现多个指标协同变化的异常,而不是孤立地看每个指标。
2.5 知识体系总览
下面这张图,是我对本章知识体系的总结。你可以把它当作一个“故障发现机制”的思维导图:
2.6 实战建议:从零搭建故障发现体系
如果你现在要从头搭建一套故障发现体系,我建议按这个顺序来:
- 先搞定基础监控:CPU、内存、磁盘、网络,这些是必须的。阈值先用固定值,别追求完美。
- 再搞告警收敛:把重复告警合并掉,把依赖关系梳理清楚。这一步能减少80%的噪音。
- 然后上动态阈值:用历史数据算阈值,减少误报和漏报。
- 最后引入AI:先做时序异常检测,再做日志分析。别一上来就上深度学习,容易翻车。
好了,这一章的内容就到这里。记住:故障发现的核心不是“发现所有问题”,而是“在正确的时间发现正确的问题”。下一章,我们会聊聊故障自愈的触发条件——什么时候该自动恢复,什么时候该让人介入。
公众号:蓝海资料掘金营,微信deep3321