HELP jvm_memory_used_bytes Used bytes of a given JVM memory area.
从“看不见”到“看得很清楚”:我们在一次故障排查中重建监控体系的故事

开篇:为什么我想聊这个话题?
我在一家中小型互联网公司做基础工具研发,日常工作中有一项很核心的任务就是维护、优化和扩展内部的各种开发支持工具。我们团队不大,五个人左右,但支撑着整个公司的工程效率、线上环境稳定性等关键环节。
今天想和大家聊聊一个让我印象非常深刻的项目——重构公司的监控告警系统。这次项目的起因并不复杂,却暴露了我们很多技术债务和长期忽视的问题。通过这次重构,不仅解决了当时紧急的业务问题,还让我们后续在线上运维方面变得从容许多。
这篇文章会尽量贴近实际的工作场景,我会以第一人称的角度来讲讲我们是怎么一步步把“监控这事儿”真正做好一点的。希望对你们有所帮助。
背景介绍:我们的服务怎么突然全挂了?
事情得从一个普通的上午说起。那天早上刚坐下没多久,钉钉群里就炸开了锅:
“用户反馈登录失败!”
“后端接口全报错!”
“数据库连接数飙到上限了!!!”
我们几个迅速进入战时状态,开始检查日志,但发现一个问题——根本没有及时有效的数据支撑。虽然之前也不是完全没做监控,但我们用的是开源方案 Prometheus + Grafana,配置很零散,指标也是各自为政,谁想加谁加,报警也设置得不规范。
那次事故持续了大概40多分钟,最后查出来是一个服务在升级过程中导致 Redis 连接池没有释放,最终打爆了中间件资源。但更糟的是:
- 线上没人第一时间知道出了问题
- 告警是半小时以后才触发
- 我们根本不知道哪个节点出问题
这件事之后,老板说了一句话:“你们不是应该早就发现这个问题吗?”
这句话就像一记闷棍敲在头上,让我们意识到:监控这件事,不能再拖了。
我们的目标很朴素:看得见、看得懂、能响应
于是我们开启了监控体系建设的“第二版”。目标很朴素也很实用:
- 看得见当前系统的健康状态
- 出问题能及时告警并定位根源
- 让每个工程师都能理解这些信息,而不只是DevOps
我们重新梳理了已有的监控内容,包括:
- 各个微服务的关键性能指标(QPS、响应时间)
- 数据库连接数、慢查询统计
- Kafka消息堆积情况
- Redis内存、缓存命中率
- JVM 指标(GC、堆栈等)
- HTTP 错误码分布
但这些都不是最难的,难的是把这些东西组织好、整合起来,并且在关键时刻发挥作用。
第一步:技术选型上的“取舍大战”
我们开始讨论新的架构时,最先面临的就是技术选型。我这里特别要强调一下:不要盲目追求高大上,而是要看自己能不能驾驭。
我们对比了以下几种主流方案:
| 技术栈 | 特点 | 优缺点 |
|---|---|---|
| Prometheus + Alertmanager | 云原生友好,开箱即用,社区活跃 | 配置分散,规则复杂,集群化困难 |
| ELK Stack(Elasticsearch + Logstash + Kibana) | 强大的日志分析能力 | 实时性差,部署成本高 |
| Zabbix | 经典老牌,功能齐全 | 对容器支持较弱,学习曲线陡 |
| OpenTelemetry + Tempo / Loki | 新一代可观察性平台,集追踪、日志、指标一体 | 学习门槛较高 |
| Datadog / NewRelic(SaaS方案) | 全托管,可视化强 | 成本不可控,依赖第三方 |
最终我们选择了一个折中的组合:
Prometheus + VictoriaMetrics (TSDB) + Grafana + Alertmanager + Loki + Promtail + Node Exporter
为什么这么选?
- Prometheus 是轻量级拉模型,适合我们的微服务结构
- VictoriaMetrics 是 Prometheus 的高性能替代(兼容PromQL,存储效率高)
- Loki 和 Promtail 是日志收集组件,与现有系统无缝衔接
- 整体可以自建,节省成本,又不失灵活性
这套方案后来验证下来确实不错,特别是在资源有限的情况下,性价比非常高。
架构设计:如何构建“看得到”的能力?
我们的整体架构图如下(简化版本):
+-----------------+
| Service A |---+ +--------------+
+-----------------+ | | |
| | Grafana |
+-----------------+ | +-------+ Dashboards |
| Service B |---+----| +--------------+
+-----------------+ | |
| | +------------------+
+-----------------+ | | | Prometheus Scrape|
| Service C |---+ +----> VM Agent |
+-----------------+ | | +----------------+
| | |
| | |
+----------------------+ | | |
| DB & Middlewares | -+ | |
+----------------------+ | |
| |
+---------v----------+
| VictoriaMetrics |
| (Remote Write Store)|
+---------------------+

+-----------------+
| Loki 日志采集 |<-----> Promtail
+-----------------+
其中几个关键点说明:
- 每个服务都暴露
/metrics接口(基于 Micrometer 或 Prometheus client) - 使用 VictoriaMetrics Agent 替代 Prometheus 拉取数据
- 所有指标写入 VictoriaMetrics 单机版(VMStorage)
- 使用 Grafana 展示面板(内置PromQL)
- AlertManager 负责报警路由和抑制策略
- Loki 收集所有服务的日志(通过 Promtail),并支持按关键字搜索、关联指标报警
实践细节:从零搭建一套监控系统
1. 微服务埋点(Java为例)
我们使用 Spring Boot 应用,所以直接引入了 Micrometer 来暴露指标:
<!-- pom.xml -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
然后在启动类中启用监控端点:
@SpringBootApplication
@EnableWebMvc
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
配置 application.yml:
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
metrics:
enabled: true
prometheus:
enabled: true
访问 http://localhost:8080/actuator/prometheus 可以看到类似输出:
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{area="heap",} 1.54986968E8
...
有了这个就可以被 Prometheus 抓取了。
2. VictoriaMetrics Agent 配置示例
我们不再使用原始的 Prometheus,改用 VictoriaMetrics 的 agent 来抓取指标:
global:
scrape_interval: 15s
evaluation_interval: 15s
remote_write:
- url: http://victoriametrics:8428/api/v1/write
scrape_configs:
- job_name: 'spring-boot-services'
static_configs:
- targets: ['service-a:8080', 'service-b:8080']
这样所有的指标都会写入 VictoriaMetrics 提供的远程写接口。
3. Grafana 面板配置技巧
Grafana 中我们创建了一个统一的“服务大盘”模板:
- 左边显示 QPS、错误率
- 中间展示 JVM 内存变化趋势
- 右侧是最近5分钟的错误日志摘要(Loki)
举个例子,JVM 内存的查询语句可以这样写:
jvm_memory_used_bytes{job="spring-boot-services"} / (1024*1024)
单位换算成 MB,直观易读。
踩坑经验:监控系统也有“暗礁”
在落地过程中我们踩了不少坑,有些至今想起来还会苦笑一声。
1. 监控指标太多导致性能崩溃?
最初我们试图抓取所有能拿到的指标,结果VictoriaMetrics的CPU飙到了90%以上,甚至影响到线上其他服务。
解决办法:
- 在应用代码中限制暴露出的指标数量(如只保留核心业务相关)
- Prometheus 抓取时加过滤,避免拉取非必要指标:
metric_relabel_configs: - source_labels: [__name__] regex: '(jvm_.*)|(http_server_requests.*)' action: keep
2. 告警轰炸,没人敢睡觉
刚开始搞完告警规则,半夜收到几十条告警,全是“某个服务某一分钟内错误数超10”的通知,但实际上可能是偶发的异常。
解决办法:
告警增加聚合和容忍度:
record: job:http_errors_per_second:rate1m expr: rate(http_requests_total{status=~"5.."}[1m]) by (job) alert: HighHttpErrorRate expr: job:http_errors_per_second:rate1m > 10 for: 2m labels: severity: warning annotations: summary: High error rate on {{ $labels.job }} description: Error rate is above 10 per second (current value: {{ $value }})设置“抑制规则”,比如当某个服务不可达时,暂停其派生的所有告警
3. 一个指标名称引发的“血案”
我们有个服务叫 user-service,它的 JVM 指标里包含了两个实例标签:
{jvm_application_class=..., instance="user-service-74d6fc8dfb-qgklx:8080"}
结果在 Grafana 中显示的时候,每个 Pod 名字都是独立的图,根本看不出总体趋势。
解决办法:
在查询时忽略 Pod 名字:
max by (job) (rate(http_requests_total[1m]))
或者在 Prometheus 抓取时重写 instance 标签:
- source_labels: [__address__, __meta_kubernetes_pod_label_app]
regex: (.+):.*;(.+)
target_label: instance
replacement: "$2"
上线效果:从慌乱到从容
这套系统上线两周后,我们又遇到了一次真实故障:
一个定时任务因为配置错误,每天凌晨1点向 Kafka 推送大量无效数据,导致下游服务处理阻塞。但这次……
不到5分钟我们就收到了告警:“kafka-topic 处理延迟超过阈值”。
我们很快打开 Grafana 定位到具体哪一组消费者出现瓶颈,并在几分钟内回滚配置解决问题。
老板问:“上次这种事是不是要1小时?”
我说:“现在我们还没睡醒就知道了。”
总结一下:这次重构带来的价值
- 响应速度显著提升:大多数线上问题在发生前就被预警,而不是被用户投诉才发现
- 定位效率提高:结合日志和指标可以快速判断根因,不需要翻半天日志
- 团队协作更顺畅:新来的同学也能通过Grafana看清系统运行状况,减少沟通成本
- 技术债有所缓解:标准化的监控入口让我们今后添加新服务更加容易
给读者的一些建议
如果你现在所在的团队还没有完整的监控体系,或者还在“裸跑”,那么以下建议或许对你有用:
- 不要一开始就想着搞一套“完美的”系统,先抓重点,比如核心业务服务、关键中间件
- 学会权衡监控粒度,不是指标越多越好,能表达问题即可
- 告警要有节制,否则就成了噪声,反而掩盖了真正的风险
- 文档和命名规范很重要,尤其是多个服务同时接入时
- 让监控成为“标配”,新服务上线时默认就要包含指标接入逻辑
另外,在如今微服务和容器化盛行的大背景下,OpenTelemetry(OTel) 也是一个值得探索的方向。它将 Metrics、Logs、Traces 三者统一管理,未来可能会逐步替代传统的割裂式监控方式。
不过,再先进的工具也需要“正确地用”,否则也可能变成另一个雷区。
最后说几句心里话
说实话,做监控这件事,短期内很难看出收益,也不像开发新功能那样能立马让用户感受到变化。但它就像房子的地基——平时你不会注意,但一旦塌了,后果严重。
通过这次项目我深深体会到,一个好的监控系统并不是冷冰冰的数据堆砌,而是一种安全感的建立。它让我们在面对未知时少一分焦虑,多一分底气。
所以,下次当你准备动新需求的时候,不妨花点时间想想:“如果这部分服务出问题了,我能第一时间察觉吗?”
别等到真的出问题了,才发现那时候的你说:“早知道就该早点做了。”
如果你也在做类似的改造,欢迎留言交流经验。毕竟,只有经历过的人才知道这其中有多少坑,又有多少收获。

评论 0