CodeRoad Map 程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。
由 程序员柏风 独立维护 · 关于作者
学习 更多
监控与告警
这一课你将能做到
区分 基础设施告警 、Symptom(症状)告警 与 Cause(原因)告警 ,并解释为什么判题接口的 5xx 告警比单机 CPU 告警更适合叫值班
为 POST /api/judge/submissions 设计一条可执行的 Symptom 告警,含 PromQL 条件、持续时间、严重级别、runbook 链接
识别告警风暴的三种来源,给出治理动作,能解释「告警噪音 = 没有告警」
为什么要学
可观测性解决「有没有信号」;监控与告警 解决「什么时候该把人从床上叫起来」。信号再全,规则设计不好——瞬时抖动就叫、告警没有上下文、半夜几十条 Page 轰炸——团队会习惯性关闭通知,等于没有监控。
判题场景尤其敏感:比赛窗口高峰期,接口挂五分钟和单机 CPU 抖动五分钟对用户的含义完全不同。面试里「symptom 和 cause 的区别」「告警风暴怎么治」是高频题,本课结合 PromQL 和排障流程给答案。
先建立图像
类型 示例(判题系统) 响应方式 Symptom POST /api/judge/submissions 5xx 率 > 1% 持续 5 分钟Page(立即叫醒) Cause 判题 worker 消费延迟增大、MySQL 连接池接近上限 Ticket(工作时间处理) Capacity 磁盘剩余 < 15%、Kafka topic 分区扩容预警 Ticket/计划处理
好的告警链路:
SLI 越界(症状)──► Page on-call
│
├─ Grafana 面板:确认影响面、时间线、最近发布
├─ Runbook 第一步:优先判断是否已自动降级
└─ 日志/Trace:定位具体 submission 与代码路径
差的告警链路:
单机 CPU 突升到 85% ──► 半夜 Page ──► 判题接口正常 ──► 告警被静音
│
└── 没有上下文、没有持续时间、没有恢复步骤
与 reliability-essentials 联动:告警文案应提示「是否已触发降级或熔断」。
跟着做
步骤 1:为判题接口写一条 Symptom 告警
先写意图,再给可运行的 PromQL:
# alert: JudgeSubmissionHighErrorRate
# 判题接口 5xx 率超过 1% 持续 5 分钟,这是用户可见的症状,直接 page
expr: |
(
sum(rate(http_server_requests_seconds_count{
uri="/api/judge/submissions",
status=~"5.."
}[5m]))
/
sum(rate(http_server_requests_seconds_count{
uri="/api/judge/submissions"
}[5m]))
) > 0.01
for: 5m
labels:
severity: page
annotations:
summary: "判题提交接口 5xx 率过高"
runbook: "https://wiki.internal/runbooks/judge-5xx"
impact: "用户提交后无法得到结果或结果错误"
first_check: "Grafana: http_server_requests RED 面板"
应得到:有 for(持续时间)、有 severity、有 runbook 链接、有 impact 说明——不是「expr 满足就 Ding 一声」。
步骤 2:把两条 Cause 告警降级为 ticket
1. 单机 CPU 持续 > 70%(非关键节点)
→ 原因:资源使用增加,用户可能完全无感知
→ 响应:工作时间 ticket,关注趋势而非瞬间
2. Kafka consumer lag 轻微增加但未超阈值
→ 原因:可能正在消费积压,用户仍能看到提交进入排队
→ 响应:ticket,先观察是否需要扩容 worker
应得到:Page 的标准是「用户此刻是否受伤」 ,不是「这台机器不舒服」。
步骤 3:为每条 page 告警补 runbook 四要素 JudgeSubmissionHighErrorRate 的 runbook:
1. 影响面:判题接口不可用,用户无法提交代码;关联 lesson/比赛需确认
2. 首要检查:
- Grafana RED 面板:确认 rate、errors、duration 突变时间
- /actuator/health:DB/Kafka 健康指示器状态
- 最近发布/回滚记录
3. 止血开关:
- 启用降级:返回静态提示页(如「判题队列繁忙,请稍后重试」)
- 扩容:增加判题 worker 实例(如果 lag 是瓶颈)
- 回滚:如果突变与某次发布时间吻合
4. 升级路径:
- 5 分钟内未止血 → 叫 DBA 检查 MySQL 慢查询
- 涉及大面积比赛中断 → 启动重大故障流程
应得到:收到告警的人,能在 30 秒内知道「哪里看」「先做什么」「什么时候升级」。
步骤 4:为「观测自身」加告警 # Prometheus 自身 scrape 失败
expr: up{job="coderoadmap-backend"} == 0
for: 2m
labels:
severity: page
annotations:
summary: "backend scrape 目标不可达"
runbook: "https://wiki.internal/runbooks/scrape-failure"
步骤 5:把 Spring Health 与告警分工写清
/actuator/health 供 K8s 探针 (摘流/重启决策)和 值班看板
用户向告警 仍以请求成功率为准,原因:health 红了但流量本身成功(非关键依赖挂了),或 health UP 但接口已经在报错(假 UP)
步骤 6:模拟一次告警风暴并治理 场景:新部署引入了一个 NPE,导致 5xx 率飙升 30%。现有告警如下:
A. JudgeSubmissionHighErrorRate(page) ← 该醒
B. KafkaProducerFailure(page) ← 根因是 judge 失败,不是 Kafka
C. JudgeWorkerQueueLag(page) ← 根因是 judge 失败,不是 lag
D. MySQLConnectionPoolHigh(page) ← 可能是连锁反应
E. SinglePodRestart(page) ← 无关,Pod 是正常滚动更新
根源收敛 :B、C、D 降级为 cause/ticket,不要跟主告警一起 page
依赖标注 :B 的 runbook 写「先查上游 Judge 是否已报错,不要优先查 Kafka」
静默 :比赛期间已知的短时压测峰值提前设静默窗口
聚合 :同类 Pod 重启告警用聚合规则合并为一条
讲清楚要点
用户痛优先于机器忙,所以 Symptom 告警应高于纯资源告警 ——除非资源已直接表现为超时或失败。
瞬时抖动常见,所以告警要有持续时间(for)与聚合窗口 ——5 分钟内偶尔一次 502 不该 page。
没有上下文的叫醒等于噪音,所以每条 page 必须有 runbook ,且 runbook 至少含影响面、首要检查、止血开关、升级路径。
人的注意力有限,所以告警要少而准 ——宁可先漏一次边缘 case,也不要每天噪音淹没真正的故障。
错误预算是决策工具,burn rate 多窗口告警比单阈值更贴 SRE 实践 ——见 obs-slo 。
降级可能让错误率「看起来变了」,所以告警要理解业务降级语义 ——比如降级返回缓存数据不是 5xx,但用户得到的是旧结果,应有降级触发事件。
K8s 探针失败会摘流,readiness 与告警、流量要一起设计 ——见 kubernetes-essentials 。
你可能会懵的地方
Cause 告警是不是没用?
有用,但多数不应半夜 page。Cause 告警的价值在于:排障时提供线索、Symptom 出现前做容量计划、帮助定位根因。设成 ticket 级别,工作时间处理即可。
为何「磁盘 85%」有时要升到 page?
如果该磁盘写满会导致服务全面不可用(如日志目录写满触发 OOM),可以定义为「即将发生的 Symptom」。但仍建议:带预测曲线 + 工作时间处理窗口 + 与真正的 Symptom 联动(接口延迟/错误率同步上涨时才是真 page)。
告警和面板什么关系?
告警负责「叫醒」;面板负责「确认」和「关联」。值班流程应该是:告警 30 秒内打开 Grafana 确认影响面,然后按 runbook 查日志/链路。不要把 50 个面板阈值都变成 page——面板是给眼睛看的,告警是给 on-call 手机看的。
和 prometheus-essentials 重复吗?
那门课教规则文件怎么写、Alertmanager 怎么分组路由;本课教 选什么告、如何分级、如何写 runbook ——前者是「语法」,后者是「语义」。
错法对照 错法 问题 正解 CPU 一高就叫 用户可能完全无感知,吵醒人对排障无帮助 Symptom 优先;资源告警分级为 ticket 无 for 的瞬时阈值 服务重启、网络抖动导致误报 持续窗口 + 合理聚合(如 rate 5m) 告警无 runbook on-call 收到后不知道先看哪里 每条 page 附影响面/检查项/止血/升级 上百条 page,团队全静音 噪音淹没真实故障 削减、合并、降级 severity、设静默窗口 只告应用,不告 scrape 失败 监控自身挂了,应用故障也发现不了 增加 up == 0 / scrape duration 告警
卡点急救
告警规则改了但不生效 :先查 Prometheus 的 Status → Rules 是否加载了新规则;再确认 Alertmanager 的 route 匹配条件和标签是否一致;最后看 Alertmanager 的日志确认通知有没有被抑制或静默。
同一条告警反复触发/恢复(flapping) :通常是阈值太紧或窗口太短。调整方向:增大 for 持续时间;把绝对阈值改成 rate 或 burn rate;增加 min 样本数防止数据稀疏时抖动。
告警发了但收不到通知 :确认 Alertmanager 配置里的 receiver(钉钉/企微/邮件)连通性;确认路由 group_by 没有把所有告警吞掉;确认没有静默规则(silence)覆盖了该告警。
面试常问
Symptom 告警和 Cause 告警的区别?哪个优先?
背这个版本:Symptom 告警反映用户可见的问题,如判题接口 5xx 率上升——用户此刻在受伤,必须 page。Cause 告警反映根因线索,如 MySQL 连接池满——它可能导致症状,也可能不会;即使会,响应时间通常可以容忍。所以 Symptom 优先 page,Cause 处理为 ticket 或作为排障线索。核心原则:告警的对象是用户体验,不是机器状态。
告警风暴怎么治理?
三来源:同源故障的多点告警(级联反应)、无效的低级告警(CPU/内存抖动)、配置错误(阈值太紧、窗口太短)。治理:按依赖关系把级联告警收敛为一条主 Symptom 告警;清理低级告警到 ticket 或删除;加持续时间 for 和最小样本数;设静默窗口应对已知的压测/发布;用 Alertmanager 的 group 和抑制规则减少重复通知。
一条好的告警需要哪些要素?
四要素:明确的条件(PromQL 且可复现)、持续时间(for 防抖动)、严重级别(page/ticket/info)、上下文(runbook 含影响面、首要检查、止血开关、升级路径)。
自测三问
为什么「判题接口 5xx > 1%」通常比「某 Pod CPU 高」更适合 page?
一条 page 告警的 runbook 至少包含哪四要素?
假设判题服务崩溃导致 Kafka lag 上升、MySQL 连接池满、worker 重启,现有三条 page 告警,怎么治理?
小结一句话 监控求覆盖,告警求精准——Symptom 优先、带持续时间与 runbook,吵人的规则等于没有规则。
延伸