做物联网平台的,迟早会被一个问题折磨:告警太多。
设备一多,告警量就爆炸。运维人员每天收到几千条告警,看到后来直接麻木了,真的出了问题反而被淹没在告警海洋里。
这就是"告警风暴"。我最近参与了一个工业物联网平台的告警系统重构,从设计到落地踩了不少坑,分享一下。
一、告警风暴是怎么产生的
先看一个真实的场景。
某制造企业有300台设备,每台设备有5个传感器。每个传感器设了上下限阈值,超阈值就告警。
粗算一下:
- 300台 × 5个传感器 = 1500个监控点
- 每个监控点每天平均触发2次告警 = 3000条/天
- 如果某台设备出了问题,可能导致关联传感器同时告警 → 一次故障可能产生几十条告警
3000条/天,平均每分钟2条。运维人员根本看不过来。
更糟糕的是"告警雪崩":一个核心交换机挂了,下面50台设备同时离线,瞬间产生几百条告警。运维人员的手机被轰炸,真正的根因反而被淹没了。
二、告警系统的架构设计
一个完整的告警系统,包含以下几个核心模块:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 数据采集层 │───▶│ 规则引擎层 │───▶│ 告警处理层 │
│ │ │ │ │ │
│ 传感器数据 │ │ 阈值规则 │ │ 告警收敛 │
│ 设备状态 │ │ 复合规则 │ │ 告警关联 │
│ 日志数据 │ │ AI模型 │ │ 告警升级 │
└──────────────┘ └──────────────┘ └──────┬───────┘
│
┌──────▼───────┐
│ 通知分发层 │
│ │
│ 短信/电话 │
│ 邮件/IM │
│ 工单系统 │
└──────────────┘
2.1 规则引擎层
规则引擎是告警的"大脑",负责判断什么情况下需要告警。
class AlarmRule:
"""告警规则基类"""
def __init__(self, name, severity, condition):
self.name = name
self.severity = severity # critical, warning, info
self.condition = condition
self.enabled = True
def evaluate(self, context):
"""评估规则是否触发"""
if not self.enabled:
return None
if self.condition.check(context):
return AlarmEvent(
rule_name=self.name,
severity=self.severity,
timestamp=datetime.now(),
context=context
)
return None
# 简单阈值规则
class ThresholdRule(AlarmRule):
def __init__(self, metric, operator, threshold, **kwargs):
super().__init__(**kwargs)
self.metric = metric
self.operator = operator
self.threshold = threshold
def evaluate(self, context):
value = context.get_metric(self.metric)
triggered = False
if self.operator == '>':
triggered = value > self.threshold
elif self.operator == '<':
triggered = value < self.threshold
elif self.operator == '==':
triggered = value == self.threshold
if triggered:
return AlarmEvent(
rule_name=self.name,
severity=self.severity,
metric=self.metric,
value=value,
threshold=self.threshold,
timestamp=datetime.now(),
device_id=context.device_id
)
return None
# 复合规则:多个条件同时满足
class CompositeRule(AlarmRule):
def __init__(self, rules, logic='AND', **kwargs):
super().__init__(**kwargs)
self.rules = rules
self.logic = logic
def evaluate(self, context):
results = [rule.evaluate(context) for rule in self.rules]
if self.logic == 'AND':
if all(r is not None for r in results):
return AlarmEvent(
rule_name=self.name,
severity=self.severity,
sub_events=results,
timestamp=datetime.now()
)
elif self.logic == 'OR':
triggered = [r for r in results if r is not None]
if triggered:
return AlarmEvent(
rule_name=self.name,
severity=self.severity,
sub_events=triggered,
timestamp=datetime.now()
)
return None
# 趋势规则:基于变化趋势判断
class TrendRule(AlarmRule):
def __init__(self, metric, window_size, trend_type, **kwargs):
super().__init__(**kwargs)
self.metric = metric
self.window_size = window_size
self.trend_type = trend_type # 'rising', 'falling', 'volatile'
def evaluate(self, context):
history = context.get_metric_history(self.metric, self.window_size)
if len(history) < self.window_size:
return None
if self.trend_type == 'rising':
# 连续上升
if all(history[i] < history[i+1] for i in range(len(history)-1)):
return AlarmEvent(
rule_name=self.name,
severity=self.severity,
trend='rising',
values=history,
timestamp=datetime.now()
)
return None
2.2 告警处理层(核心:告警收敛)
这是解决告警风暴的关键。主要做三件事:
1. 告警去重
同一条告警在短时间内重复触发,只保留一条:
class AlarmDeduplicator:
def __init__(self, dedup_window=300): # 5分钟去重窗口
self.dedup_window = dedup_window
self.last_alarm = {} # key -> timestamp
def deduplicate(self, alarm):
key = f"{alarm.device_id}:{alarm.rule_name}"
now = time.time()
if key in self.last_alarm:
if now - self.last_alarm[key] < self.dedup_window:
return None # 重复告警,丢弃
self.last_alarm[key] = now
return alarm
2. 告警聚合
关联的告警合并成一条,避免告警雪崩:
class AlarmAggregator:
def __init__(self, aggregation_window=60):
self.aggregation_window = aggregation_window
self.pending_alarms = []
def aggregate(self, alarms):
"""按设备+时间窗口聚合告警"""
groups = defaultdict(list)
for alarm in alarms:
# 同一设备、相近时间的告警归为一组
group_key = f"{alarm.device_id}:{int(alarm.timestamp / self.aggregation_window)}"
groups[group_key].append(alarm)
aggregated = []
for key, group in groups.items():
if len(group) == 1:
aggregated.append(group[0])
else:
# 合并成一条聚合告警
parent = AlarmEvent(
rule_name='aggregated_alarm',
severity=max(g.severity for g in group),
device_id=group[0].device_id,
sub_events=group,
count=len(group),
timestamp=group[0].timestamp
)
aggregated.append(parent)
return aggregated
3. 告警抑制
当根因告警触发时,抑制由它引起的下游告警:
class AlarmSuppressor:
def __init__(self):
# 定义告警之间的依赖关系
self.dependencies = {
'network_switch_down': [
'device_offline', # 交换机挂了 → 抑制下游设备离线告警
'connection_timeout'
],
'power_failure': [
'device_offline',
'sensor_no_data',
'communication_error'
]
}
def suppress(self, alarms):
"""抑制被根因告警覆盖的下游告警"""
active_suppressions = set()
# 先找出根因告警
for alarm in alarms:
if alarm.rule_name in self.dependencies:
active_suppressions.update(self.dependencies[alarm.rule_name])
# 过滤掉被抑制的告警
return [a for a in alarms if a.rule_name not in active_suppressions]
2.3 通知分发层
不同类型的告警,通知不同的对象,用不同的方式:
class NotificationDispatcher:
def __init__(self):
self.routes = {
'critical': [SMSNotifier(), PhoneNotifier(), IMNotifier()],
'warning': [IMNotifier(), EmailNotifier()],
'info': [EmailNotifier()]
}
self.escalation_rules = {
'critical': {
'timeout': 300, # 5分钟未处理
'escalate_to': 'manager',
'notify': SMSNotifier()
}
}
def dispatch(self, alarm):
notifiers = self.routes.get(alarm.severity, [])
for notifier in notifiers:
notifier.send(alarm)
# 记录告警,用于升级判断
self.alarm_tracker.track(alarm)
def check_escalation(self):
"""检查是否有需要升级的告警"""
for alarm in self.alarm_tracker.get_unhandled():
rule = self.escalation_rules.get(alarm.severity)
if rule and time.time() - alarm.timestamp > rule['timeout']:
rule['notify'].send(
f"告警升级:{alarm.rule_name} 已 {rule['timeout']}s 未处理"
)
三、重构后的效果
经过三个多月的优化,效果还是比较明显的:
| 指标 | 重构前 | 重构后 |
|---|---|---|
| 日均告警量 | 3000+ | 200~300 |
| 有效告警占比 | ~15% | ~85% |
| 平均响应时间 | 45分钟 | 8分钟 |
| 告警风暴次数 | 每周2~3次 | 基本消除 |
关键改进点:
- 去重:消除了60%的重复告警
- 聚合:把关联告警合并,减少了25%的告警量
- 抑制:根因定位更准确,下游告警不再干扰
- 分级通知:重要告警能第一时间触达对的人
四、几点经验
- 告警不是越多越好。一条精准的告警,胜过一百条模糊的告警。宁可少报,不要滥报。
- 规则要持续优化。上线后要根据实际运行情况,不断调整阈值和规则。可以用历史数据做回测,找到最优阈值。
- 告警要有上下文。告警消息里要包含足够的上下文信息——当前值、阈值、历史趋势、影响范围,让运维人员一看就知道严重程度和处理方向。
- 要有告警升级机制。如果告警发出去没人处理,要能自动升级。不然告警系统就形同虚设。
这套告警系统底层用的是JVS物联网平台的规则引擎做的,省去了自己写规则匹配和事件处理的底层代码。规则引擎负责告警规则的解析和执行,上层自己去重、聚合、抑制的逻辑是我们自己写的。整体来说,规则引擎这部分如果自己做,至少要两三个月,用现成的省了不少事。
#物联网告警 #告警系统 #规则引擎 #告警收敛 #JVS物联网平台