读:为什么你的告警永远在喊狼来了
目录
原文:Stop Guessing, Start Seeing: A Five-Layer Framework for Monitoring Distributed Systems
问题:监控很全,但出了事还是后知后觉
凌晨三点手机响了,CPU 超 80%。爬起来一看,业务完全正常,虚惊一场。第二天、第三天还是这样,后来干脆把告警通知静音了。然后某天客户投诉"我的报表生不出来",你才发现这条告警被淹没在几十条 CPU、内存、磁盘的噪音里,根本没人注意。
很多团队的监控就是这样运作的:CPU 超 80% 报一次,内存超 75% 报一次,磁盘超 60% 再报一次,告警一天响几十回,值班的人逐渐麻木。直到真出了事,还得翻遍六七块仪表盘才找到原因。
问题出在监控没有层次。CPU 高不高和客户能不能用报表之间隔着好几层因果关系,告警系统不知道这层关系,只能对所有"看起来不对"的数值大喊大叫。
Prashant Pathak 在 DZone 上发了一篇文章,提出把监控按业务交易、服务健康、容器行为、数据服务性能、容量规划五个层次组织起来,告警只对准最顶层(客户痛感),排查从上往下钻。
核心原则:告警对准客户痛感,排查向下钻取
如果你的告警系统在喊"CPU 80%"但没有客户受影响,这条告警就是噪音。反过来,客户已经在报障了但你的告警一声没吭,那就是漏报。
要做到这一点,监控需要分成明确的层级,层与层之间有清晰的上下级关系:
Layer 1: 业务交易 ← 客户真正在经历什么 Layer 2: 服务健康 ← 哪个服务出了问题 Layer 3: 容器行为 ← 单个容器的资源状况 Layer 4: 数据服务性能 ← 数据库和缓存的瓶颈 Layer 5: 容量规划 ← 还能撑多久
告警设在 Layer 1。Layer 1 响了之后,到 Layer 2 找是哪个服务的问题,再到 Layer 3 看是不是个别容器在拖后腿,到 Layer 4 查数据库或缓存有没有瓶颈。每一层回答一个具体的问题,排查路径是确定的,不用猜。
两个排查工具:RED 和 USE
分层搭好之后,每一层具体看什么指标?原文推荐了两个方法论。
**RED 方法**用在 Layer 2(服务健康),由 Tom Wilkie 提出,三个字母刚好拼成 RED:
- Rate(请求量):流量正常吗?突然涨了还是突然跌了?
- Errors(错误率):这个服务返回了多少错误?
- Duration(延迟):响应变慢了吗?
这三个指标能快速判断一个服务的整体状态。请求量跌了说明上游可能在出问题,错误率升了说明这个服务本身有问题,延迟高了说明可能在排队等资源。不过这三个指标经常联动,比如下游变慢会导致本服务延迟升高、错误率跟着涨、请求量反而跌(因为超时放弃),所以不能单独看某一个指标下结论,要三个放在一起对照。
**USE 方法**用在 Layer 3 和 Layer 4(容器行为和数据服务性能),由 Brendan Gregg 提出:
- Utilization(利用率):资源被用了多少?比如 CPU 75%。
- Saturation(饱和度):有没有排队的、处理不过来的?比如线程池里 90% 的线程都在忙,新请求进来只能排队。
- Errors(错误):有没有硬件级或系统级的错误计数在增长?
这里有个容易忽略的区别:利用率和饱和度是两回事。CPU 75% 可能完全没问题,但线程池 90% 饱和意味着再多几个请求就处理不过来了。饱和度比利用率更适合当早期预警信号,因为它衡量的是"还有没有余力",而利用率只告诉你"已经用了多少"。
一个排查案例:从业务异常追到数据库连接池
原文给了一个实际的排查案例,跟着走一遍就知道分层怎么用。
Layer 1 报警:报表生成这个业务流程的错误率超过了 2%。 → 客户在受到影响,问题确认。 Layer 2 定位:报表服务(report-service)的错误率升高,其他服务正常。 → 问题出在报表服务。 Layer 3 钻取:五个报表服务容器里,有两个的线程池饱和度超过 90%。 → 不是所有容器都有问题,只有两个在扛不住。 Layer 4 根因:报表服务用的数据库连接池已经占到了 95%。 → 真正的瓶颈在数据库连接。
原因追溯:上周发版引入了一条新查询,这条查询占用数据库连接的时间比预期长。平时流量不大时连接池够用,高峰期连接就不够分了,两个容器先扛不住开始报错,错误传导到客户那里就变成了"报表生不出来"。
修复手段:加大连接池、优化查询的连接占用时长、在 Layer 4 加一条饱和度告警。整个排查不到 20 分钟,因为每一层该看什么、怎么往下追都有套路。
高基数标签的陷阱
如果你的监控工具用的是时间序列数据库,有一个常见的坑:把用户 ID、请求 URL 这些高基数值(也就是有无数种不同取值的字段)塞进指标标签里。比如给每个请求打上 user_id 标签,看起来很方便,能按用户查问题,但时间序列数据库的存储方式是每种标签组合生成一条独立的时间序列。三个服务各五十万用户,就是一百五十万条线,内存和查询性能扛不住。
正确做法是:指标标签只用取值有限的字段(服务名、环境、状态码、业务类型),需要按具体用户或请求钻取的时候,去查日志系统。指标负责聚合和告警,日志负责下钻和取证,两者分工明确。
怎么落地:不用一步到位
原文建议了一个渐进式的落地方案:
- 先做 Layer 2(服务健康):给每个服务挂上 RED 指标,搞清楚哪个服务什么时候不健康。大多数团队已经有一些基础设施指标,把它们按 RED 三个维度整理成统一的仪表盘就能用。虽然 Layer 1 最重要,但 Layer 2 上手成本最低,先用 RED 把现有指标整理好,再做 Layer 1 的业务流程埋点会更容易。
- 再做 Layer 1(业务交易):定义出你的核心业务流程(比如"用户下单""报表生成""文件上传"),给每个流程挂上错误率和延迟指标,把触发值班通知的条件从"CPU 超 80%"改成"业务流程错误率超阈值"。业务错误率在正常情况下很稳定(接近 0%),不会像 CPU 那样上下波动,所以用业务指标当触发条件,噪音会降很多。
- 向下铺开 Layer 3(容器行为):加上容器级别的 USE 指标,关注线程池饱和度、连接池占用这些比 CPU 利用率更有预警价值的信号。
- 继续向下到 Layer 4(数据服务性能):监控数据库连接池、查询延迟、缓存命中率、GC 停顿时间。GC 停顿是个容易被忽略的指标,Java 应用出现不明原因的延迟毛刺时,先查 GC 日志。
- 最后做 Layer 5(容量规划):把 Layer 1 的业务增长趋势和 Layer 3-4 的资源消耗趋势关联起来,算出"按当前增长速度,连接池还够用多少天"。容量规划让扩容从"火烧眉毛才加机器"变成"提前排期"。
不用等五层全做完才开始用。Layer 2 就能让你知道哪个服务不健康,Layer 1 就能让告警从"CPU 高了"变成"客户受影响了",做一层有一层的效果。
理想和现实的差距
原文说"告警只设在 Layer 1,其他层用来排查",但实际落地中很难做到这么纯粹。每一层的指标都可能触发告警,CPU 告警、线程池告警、连接池告警、业务错误率告警同时响是常态。
有些监控工具支持根据 CMDB 关系做告警压缩,如果高层告警是底层告警引起的,只通知底层那一条。但告警压缩本身很难做好,依赖关系建模不准、压缩规则覆盖不全都会导致该报的没报或者不该压缩的被压缩了。所以现实中更常见的做法是:各层都设告警,通过优先级和通知渠道区分轻重,Layer 1 的业务告警走电话,底层的资源告警走消息。分层的主要价值不是决定"在哪层告警",而是给排查提供一条确定的路径。