如何实现有效监控并避免 alert fatigue(告警疲劳)?
明确结论
更换通知渠道无法彻底解决 alert fatigue。邮件没人看,改用 Teams 或短信,一段时间后也可能失效。要减少告警疲劳,应控制需要立即人工处理的告警数量。每条告警都应满足以下条件:
- 已经出现或即将出现真实的用户影响;
- 有明确的负责人和处理动作;
- 紧急程度与通知方式相符。
监控信号可以很多,告警则要谨慎设置。对于没有明确处置动作的异常,可以保留为 metric、log 或 dashboard 信息,不要持续呼叫值班人员。
区分监控信号和告警
sensor 变红不一定需要立即通知。可以按用途把信号分成四层:
| 层级 | 用途 | 处理方式 |
|---|---|---|
| 观测数据 | 保存连接数、文件大小、SQL 记录等原始状态 | metric、log、trace |
| 可视化 | 帮助排查趋势和关联问题 | dashboard |
| 待办事项 | 需要修复,但可以等到工作时间 | ticket |
| 紧急告警 | 正在影响用户,必须立即介入 | paging、电话或高优先级消息 |
如果某个文件偶尔晚到一分钟,系统能自动恢复,也没有影响用户,就不必直接创建高优先级 ticket。可以把这种情况显示在 dashboard 中,等到持续超时后再触发告警。
判断一条信号是否应该触发 paging,可以先问:
值班人员现在收到这条告警后,具体应该做什么?
如果答案只是“观察一下”“可能有问题”或“文档要求配置 sensor”,通常还不适合触发 paging。
优先监控用户影响
连接断开、进程重启、队列积压和文件年龄异常都可能引发故障,却未必会造成服务不可用。告警应优先关注用户能感知的结果,例如:
- 请求错误率持续升高;
- 请求延迟超过服务目标;
- 核心业务流程无法完成;
- 数据没有在规定时间内生成;
- 可用性预算正在快速消耗。
底层指标更适合用于诊断。结果类告警触发后,可以根据连接数、日志和队列长度等数据定位原因。
对于批处理系统,可以监控“最近一次成功时间”,避免每次短暂失败都发送通知。在线服务可以围绕 SLI/SLO 设置告警,关注成功率、延迟和可用性。
给瞬时异常留出确认时间
短暂抖动很容易产生噪声。告警规则应设置持续时间、窗口聚合或恢复阈值,避免单个采样点直接触发通知。
下面是一个 Prometheus 规则示例。示例中的阈值仅用于说明规则结构,实际数值应根据业务 SLO、流量规模和历史数据确定:
groups:
- name: checkout-health
rules:
- alert: CheckoutHighErrorRate
expr: |
(
sum(rate(http_requests_total{
service="checkout",
status=~"5.."
}[5m]))
/
clamp_min(
sum(rate(http_requests_total{
service="checkout"
}[5m])),
1
)
) > 0.05
for: 10m
labels:
severity: page
service: checkout
annotations:
summary: "checkout 5xx error rate is above 5%"
description: "The condition has remained true for 10 minutes."
runbook_url: "https://internal.example/runbooks/checkout-high-error-rate"
for: 10m 表示条件必须持续成立十分钟。这样可以过滤短暂尖峰,也会推迟真正故障的发现时间,所以不能给所有规则统一设置相同的持续时间。安全事故、数据损坏等高风险事件可能需要缩短确认时间,甚至立即告警。
对于经常在临界值附近波动的指标,可以使用滞回策略,分别设置触发阈值和恢复阈值。例如,队列长度超过 10,000 时触发告警,降到 7,000 以下时才恢复。如果监控系统不直接支持滞回,可以通过 recording rule 或状态处理逻辑实现。
对通知进行分组、去重和抑制
同一故障常常会引发几十条下游告警。可以保留一条能代表根因或用户影响的告警,并抑制由它引发的次要告警。
以下 Alertmanager 配置展示了分组、重复通知间隔和告警抑制的基本做法:
route:
receiver: default
group_by:
- alertname
- service
- cluster
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- receiver: oncall
matchers:
- severity="page"
repeat_interval: 30m
- receiver: ticket-system
matchers:
- severity="ticket"
inhibit_rules:
- source_matchers:
- severity="page"
target_matchers:
- severity="warning"
equal:
- service
- cluster
receivers:
- name: default
- name: oncall
webhook_configs:
- url: "https://alerts.example/oncall"
- name: ticket-system
webhook_configs:
- url: "https://tickets.example/webhook"
这段配置会合并同一服务和集群的相关告警。相同范围内已经存在高优先级告警时,warning 会受到抑制。只有 severity="page" 的告警会进入值班渠道,可以延期处理的问题则进入 ticket system。尚未恢复的告警会按设定间隔重复发送,避免每次采样都通知。
matchers 等字段的语法可能随 Alertmanager 版本变化。投入使用前,应使用对应版本的配置检查工具进行验证。
让紧急告警可以直接处理
一条合格的告警至少应说明:
- 受影响的服务、客户或环境;
- 当前现象和触发条件;
- 告警开始时间;
- dashboard、log 查询或 trace 链接;
- runbook;
- 负责人或 owning team;
- 确认恢复的方法。
runbook 不必写成完整的运维手册,但要说明值班人员最先需要处理的事项,包括如何验证影响、在哪里查看上下文、可以采取哪些安全的缓解措施,以及何时需要升级给其他团队。
如果值班人员只收到“sensor 42 is red”,还要临时查找对应的系统、客户和日志入口,即使告警判断正确,也很难及时处理。
定期清理无效规则
告警规则也需要维护。规则创建后永久保留,时间一长通常会变成噪声。可以定期审查以下数据:
- 告警触发次数和持续时间;
- 是否有人确认或采取行动;
- 是否经常自动恢复;
- 是否总与另一条告警同时出现;
- 是否长期处于告警状态;
- 对应服务和负责人是否仍然存在。
告警应有明确的治理规则。例如,paging 告警连续多次触发,却从未需要人工操作,就应降级、调整阈值或删除。长期无人负责且没有 runbook 的告警,也不应继续占用紧急渠道。
新告警上线时,可以先将它发送到 dashboard 或测试接收器。经过一段具有代表性的业务周期后,再判断是否需要升级为 paging。观察期应覆盖正常高峰和常见维护窗口,具体时长由系统的业务周期决定。
用维护窗口和依赖关系控制已知噪声
在计划发布、数据库维护或客户现场断网期间,部分异常属于预期行为。可以通过 silence、maintenance window 或部署事件自动抑制相关通知,同时保留审计记录。
抑制范围应尽量精确,明确服务、环境、集群和结束时间。范围过大的 silence 可能掩盖同时发生的真实故障。维护结束后,规则应自动恢复,避免永久静默。
还需要维护基本的依赖关系。例如,数据库不可用已经触发高优先级告警时,由此引发的 API、批处理和报表失败可以分组或抑制。如果无法可靠判断依赖关系,可以合并展示这些信号,不要直接丢弃。
需要避免的做法
不断增加通知渠道通常无法提高关注度。邮件、Teams 或专用 monitor 往往不是问题所在,需要调整的是告警的筛选条件。
不要把所有异常都设为最高优先级。所有告警都一样紧急时,真正严重的问题反而难以识别。
自动关闭 ticket 也无法代替告警治理。如果某类 ticket 经常在无人处理时自行恢复,应重新评估触发条件,避免继续批量创建和关闭。
降低告警数量时也要防止过度静默。每次删除、降级或延迟告警,都应确认还有其他信号能够发现相应的用户影响,并通过故障演练或历史事件回放验证规则。
一套可持续的流程可以从定义服务目标开始,根据用户影响设置少量紧急告警,用底层 sensor 辅助诊断,再通过持续时间、分组、去重、抑制和维护窗口控制噪声。之后根据实际处理记录持续调整规则。告警治理属于日常运维,需要长期维护。
备注:内容仅供参考。
