外观
案例:故障 40 分钟才发现
现象
用户投诉后才有人知道服务挂了。事后查监控:
- Grafana 面板上有红色曲线,但没有任何告警。
- 告警群里有 3000 多条消息,全部被静音。
- 值班同事说"昨天告警太多了,就关了通知"。
定位过程
不是监控没数据,是告警体系失效。
第一步:盘点现有告警
bash
curl -s http://prometheus:9090/api/v1/rules | jq '.data.groups[].rules[] | {name, severity: .labels.severity}' | head -20json
{"name":"InstanceDown","severity":"critical"}
{"name":"HighCPU","severity":"warning"}
{"name":"HighCPU5m","severity":"warning"}
{"name":"CPU90","severity":"critical"}
...同一个问题有 3 条规则,阈值分别是 80%、85%、90%。重复告警。
第二步:看告警量
bash
curl -s http://alertmanager:9093/api/v2/alerts | jq 'length'text
18421842 条活跃告警,其中大部分是同一个根因。
bash
curl -s http://alertmanager:9093/api/v2/alerts | jq -r '.[].labels.alertname' | sort | uniq -c | sort -rn | headtext
420 HighCPU
380 HighMemory
312 DiskSpaceLow
284 Http5xxHigh
...第三步:确认关键告警是否在
业务真的挂了,但 Http5xxHigh 的阈值是 5%,而故障期间错误率 100% —— 应该触发。查为什么没通知:
bash
curl -s http://alertmanager:9093/api/v2/status | jq '.config.route'json
{
"receiver": "dingtalk",
"group_wait": "30s",
"group_interval": "5m",
"repeat_interval": "4h"
}group_wait 30s、group_interval 5m 没问题。再看静默:
bash
curl -s http://alertmanager:9093/api/v2/silences | jq '.[] | {matchers, endsAt}'json
{"matchers":[{"name":"alertname","value":"Http5xxHigh"}],"endsAt":"2026-11-30T00:00:00Z"}有人把业务最关键的告警静默了一个月。
处置
第一步:分类,砍掉噪音
把指标分成三层:
| 层级 | 内容 | 通知方式 |
|---|---|---|
| P0 紧急 | 服务不可用、错误率飙升、数据丢失风险 | 电话 + 群 |
| P1 重要 | 容量接近上限、主从延迟、磁盘 85% | 群 |
| P2 提示 | CPU 高、连接数偏高、慢查询增多 | 只看面板,不通知 |
text
P0 必须能在 5 分钟内响应
P1 当天处理
P2 每周复盘时看第二步:从"指标告警"转向"症状告警"
CPU 80% 不一定是故障;用户下单失败一定是故障。
yaml
groups:
- name: slo
rules:
# 关键:用户可见的症状
- alert: ApiErrorRateHigh
expr: |
sum(rate(http_requests_total{job="order-api",status=~"5.."}[5m]))
/ sum(rate(http_requests_total{job="order-api"}[5m])) > 0.05
for: 2m
labels: { severity: critical }
annotations:
summary: "订单 API 5xx 比例 {{ $value | humanizePercentage }}"
runbook: "https://wiki/runbook/order-api-5xx"
- alert: ApiLatencyHigh
expr: |
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket{job="order-api"}[5m])) by (le)
) > 1.5
for: 5m
labels: { severity: warning }
# 依赖
- alert: RedisMasterDown
expr: redis_up{job="redis"} == 0
for: 1m
labels: { severity: critical }
- alert: KafkaConsumerLagHigh
expr: sum by (consumergroup) (kafka_consumergroup_lag) > 100000
for: 10m
labels: { severity: warning }for: 是关键:持续一段时间才触发,避免毛刺。
第三步:告警收敛
yaml
# alertmanager.yml
route:
receiver: default
group_by: ['alertname', 'service']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- matchers: [severity="critical"]
receiver: oncall-phone
group_wait: 10s
repeat_interval: 30m
- matchers: [severity="warning"]
receiver: dingtalk
- matchers: [severity="info"]
receiver: blackhole # 不通知,只留面板
inhibit_rules:
# 服务整体不可用时,抑制它下面的细分告警
- source_matchers: [alertname="ServiceDown"]
target_matchers: [severity=~"warning|info"]
equal: ['service']| 机制 | 作用 |
|---|---|
group_by | 同一服务的多条告警合并成一条 |
group_wait | 等 30 秒,把相关告警攒一起发 |
repeat_interval | 4 小时内不重复打扰 |
inhibit_rules | 根因告警触发时,抑制派生告警 |
第四步:静默必须有期限和审批
bash
# 只允许静默维护窗口,且必须填原因和时长
amtool silence add alertname="Http5xxHigh" \
--duration=2h \
--comment="发布 1.4.3,已知短时 5xx" \
--author="zhangsan"禁止"永久静默"。建议在 CI 里检查:任何 endsAt 超过 24 小时的静默都要告警。
第五步:值班与升级
text
P0 触发 → 5 分钟未确认 → 升级到备份值班 → 15 分钟未确认 → 升级到负责人没人在意的告警等于没有告警。
复盘
| 问题 | 根因 | 改进 |
|---|---|---|
| 40 分钟才发现 | 无告警 | 加症状告警 |
| 3000 条/天 | 重复规则 + 指标告警过多 | 去重 + 分级 |
| 关键告警被静默 | 静默无审批无期限 | 强制时长 + 审计 |
| 面板有红但没人看 | 只建面板不建告警 | 面板只是辅助 |
| 值班同事关通知 | 噪音太多 | 先降噪再要求值守 |
告警的三个反模式
- 全量通知:所有告警都发群 → 很快没人看。
- 只看资源指标:CPU/内存告警一堆,用户可见的故障没告警。
- 无 Runbook:收到告警不知道做什么,只能等人。
每条 P0 告警都必须配一段"看到它先做什么"。
告警质量的自检问题
- 这条告警响了,用户真的受影响吗?
- 收到后我知道第一步做什么吗?
- 过去一个月它响了几次?其中几次是真问题?
第三个问题答案是"很少"或"每次都是真问题",这条告警才有价值。否则合并、降级或删掉。
附:最小可用的告警清单
如果从零开始,先上这六条:
| 告警 | 条件 |
|---|---|
| 服务不可用 | 健康检查连续 3 次失败 |
| 错误率高 | 5xx 比例 > 5% 持续 2 分钟 |
| 延迟高 | P99 > 1.5s 持续 5 分钟 |
| 磁盘将满 | 使用率 > 85% |
| 依赖不可用 | Redis/MySQL/Kafka 连接失败 |
| 消费积压 | lag > 10 万持续 10 分钟 |
这六条能覆盖绝大多数"用户已经受影响"的场景。