从半夜报警到稳如老狗:我的监控系统优化血泪史
凌晨三点,杭州的雨敲着窗,AirPods里放着Lo-fi Hip Hop,我盯着屏幕上第17条重复告警,手里的美式已经凉透。那一刻真想把 Prometheus 配置文件扔进西湖。
作为一个在杭州远程办公三年的独立开发者,自由是真自由——没人管我几点起床、穿不穿拖鞋写代码;孤独也是真孤独——出了问题只能自己扛,连个一起骂产品经理的同事都没有。去年接了个电商 SaaS 项目的运维模块重构,客户要求“系统要稳,告警要准,别动不动半夜call我”。听起来很合理,对吧?结果这成了我今年最痛苦也最有收获的一次技术挑战。
起因:一个被产品经理逼出来的监控需求
事情得从去年双11说起。那会儿我还在帮一家本地跨境电商做后端服务,系统跑在阿里云上,用的是公司默认的 Zabbix + 自定义脚本组合。双11当晚流量暴增,数据库连接池被打爆,但 Zabbix 只告诉我“CPU 飙高”,没任何上下文。我花了40分钟才定位到是某个新上线的促销接口没加缓存,疯狂查库。老板第二天黑着脸说:“你这个监控,跟没装有啥区别?”
更惨的是,测试环境和生产环境配置不一致,导致上线后一堆 false positive 告警。运维小哥(其实就我一个人)每天被钉钉轰炸,最后干脆把通知静音了——直到客户投诉系统卡顿,才发现 Redis 主从同步早就断了三天。
痛定思痛,我决定彻底重构监控体系。目标很明确:精准告警、快速定位、低成本维护。但说起来容易,做起来全是坑。
技术选型:为什么我放弃了 Zabbix?
一开始我考虑继续用 Zabbix,毕竟熟。但它的问题太明显:
- 指标采集靠 agent 或 SNMP,对现代微服务架构支持弱
- 告警规则写死在 UI 里,难以版本化
- 缺乏标签(label)体系,多维查询几乎不可能
于是我转向云原生方案:Prometheus + Grafana + Alertmanager。这套组合拳在阿里、网易内部早已普及,社区生态也成熟。而且 Prometheus 的 pull 模型 + 多维数据模型,特别适合动态扩缩容的容器环境。
但现实很快打脸。
坑一:指标爆炸,存储撑不住
我把所有 Spring Boot 应用都加上了 Micrometer,暴露 /actuator/prometheus 端点。结果 Prometheus 一天抓取的样本数直接飙到 800 万+。一个月下来,磁盘占了 1.2TB,查询慢得像蜗牛。
原因:Micrometer 默认暴露太多无用指标,比如每个 HTTP 请求路径都生成独立的时间序列(http_server_requests_seconds_count{uri="/api/v1/order/{id}"})。而 Prometheus 对高基数(high cardinality)指标极其敏感。
解决方案:
精简指标:在
application.yml中过滤掉不必要的指标:management: metrics: enable: http: false # 先关掉,后面按需开 web: server: requests: autotime: enabled: true tags: application: ${spring.application.name}统一 URI 标签:通过自定义
WebMvcTagsProvider,把动态路径归一化:@Bean public WebMvcTagsProvider customWebMvcTagsProvider() { return (request, response, handler, exception) -> Tags.of( Tag.of("uri", extractStablePath(request.getRequestURI())) ); } private String extractStablePath(String uri) { // 把 /api/order/123 转成 /api/order/{id} return uri.replaceAll("/\\d+", "/{id}"); }启用 TSDB 压缩:升级 Prometheus 到 2.20+,开启
--storage.tsdb.wal-compression,存储占用减少 40%。
坑二:告警风暴 vs 告警沉默
Alertmanager 配置看似简单,但稍不注意就会翻车。
第一次上线,我写了条规则:rate(http_requests_total{status=~"5.."}[5m]) > 0。结果某个第三方支付回调偶尔失败,每分钟触发一次告警。Alertmanager 的 group_wait 设成 30s,导致每 30 秒发一条钉钉消息——整整骚扰了我 3 小时。
后来改成:
- alert: HighHttpErrorRate
expr: |
sum by (job, instance) (
rate(http_requests_total{status=~"5.."}[5m])
) /
sum by (job, instance) (
rate(http_requests_total[5m])
) > 0.05 # 错误率超过5%
for: 10m
labels:
severity: warning
annotations:
summary: "High error rate on {{ $labels.job }}"
但又遇到反向问题:某次数据库主从切换,持续 8 分钟,刚好卡在 for: 10m 之前恢复,告警根本没触发。等我发现时,用户订单已经积压了上千条。
教训:for 时间要根据业务容忍度调整。核心交易链路设成 5m,非核心可放宽到 15m。同时,关键路径必须配 多级告警(warning + critical)。
实战:从“能用”到“好用”的三板斧
光有基础监控不够,得让它真正帮到开发。我总结了三个提升体验的关键实践。
1. 黄金指标全覆盖
Google SRE 提出的四大黄金信号:延迟、流量、错误、饱和度。我在每个服务里都确保这四类指标可查:
| 指标类型 | Prometheus 表达式示例 | 用途 |
|---|---|---|
| 延迟 | histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) |
发现慢接口 |
| 流量 | rate(http_requests_total[5m]) |
监控 QPS 波动 |
| 错误 | rate(http_requests_total{status=~"5.."}[5m]) |
捕获服务异常 |
| 饱和度 | 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) |
CPU 使用率 |
Grafana 里做成统一 Dashboard 模板,新项目一键导入。再也不用每次从零画图。
2. 日志 + 指标联动
有一次线上出现偶发性超时,指标显示延迟 spike,但日志里找不到对应 traceId。原因是应用日志没和监控打通。
解决方案:注入 traceId 到 MDC,并在指标中携带。
// 在拦截器中
String traceId = UUID.randomUUID().toString();
MDC.put("traceId", traceId);
// 同时让 Micrometer 指标带上 traceId(谨慎使用,避免基数爆炸)
Counter.builder("request_processed")
.tag("trace_id", traceId)
.register(meterRegistry)
.increment();
更安全的做法是:只在 error 日志中记录 traceId,并通过 Loki 或 ELK 收集。Grafana 支持 Logs + Metrics 联合查询,点击异常点直接跳转日志,排查效率翻倍。
3. 自动化健康检查
以前每次发布,都要手动 check 一堆 URL 是否 200。现在我用 Prometheus 的 Blackbox Exporter 做主动探测:
- job_name: 'blackbox'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://api.myapp.com/health
- https://admin.myapp.com/login
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: 127.0.0.1:9115 # blackbox exporter 地址
配合 Alertmanager,服务不可达 2 分钟就告警。连 DNS 解析失败都能捕获,比被动监控靠谱多了。
面试题挑战:面试官最爱问的监控陷阱
最近帮朋友内推,发现大厂面试必问监控设计。结合我的踩坑经验,分享几个高频题:
Q:如何避免告警疲劳?
A:关键在 分层 + 聚合。例如:
- 应用层:关注业务错误率(如支付失败率)
- 系统层:关注资源饱和度(CPU、内存、磁盘 IO)
- 依赖层:关注第三方服务可用性(用 Blackbox Exporter)
告警规则按严重程度分级,非核心服务只发 warning,不 call 人。
Q:Prometheus 数据丢失怎么办?
A:短期方案是调大 --storage.tsdb.retention.time(默认15天),长期必须上 远程存储(如 Thanos、Cortex)。我们用 Thanos 实现了多集群指标聚合,历史数据查一年都没问题。
Q:如何监控 Serverless 函数?
A:Lambda 或 FC 这类平台,传统 agent 行不通。得依赖平台提供的指标(如 AWS CloudWatch),再用 exporter 拉到 Prometheus。重点监控 冷启动延迟 和 并发限制。
结语:监控不是成本,是保险
折腾半年,现在的系统终于“稳如老狗”。上周五晚上十点,客户系统突发流量高峰,Grafana Dashboard 自动标红,Alertmanager 推送一条钉钉:“API QPS 突增至 5000,错误率正常”。我扫了一眼,回了个“收到,自动扩容已触发”,然后继续听歌写代码。
远程开发者最大的安全感,不是自由,而是 系统出问题时你知道它会告诉你,且你知道怎么修。监控体系就是这份安全感的基石。
如果你也在杭州搞技术,或者正被监控折磨,欢迎交流。反正我一个人在家,咖啡管够,bug 管修(收费的那种 😏)。
最后送大家一句我贴在显示器边的话:“没有监控的系统,就像闭着眼开车——迟早出事。”
P.S. 本文所有配置和代码都经过生产验证,但别照搬!每个业务场景不同,先小范围试,再全量推。毕竟,谁也不想在周五晚上被自己写的告警吵醒,对吧?

评论 0