1. 为什么监控这件事,值得从环境搭建就开始较真
做过线上服务的人大概都有过这种体验:半夜被电话叫醒,说接口大面积超时,登服务器一看CPU飙到90%以上,但具体是哪个接口、哪个SQL、哪个下游依赖拖垮的,完全靠猜。日志翻半天,指标全靠top和free,这种“盲人摸象”式的排查,在单体应用时代还能勉强应付,到了SpringBoot微服务遍地走的今天,基本等于裸奔。
SpringBoot应用天生适合做指标暴露,因为它有Actuator这套现成的端点体系,而Prometheus(普罗米修斯)又是云原生监控事实上的标准,Grafana负责把采集到的时序数据画成人能看懂的图。这三者组合起来,就是一套成本极低、落地极快的监控方案。但我在带团队和帮朋友排查环境问题时发现,真正卡住大家的往往不是代码怎么写,而是环境准备阶段的一堆细节:版本对不上、端点暴露不出来、Prometheus抓不到数据、Grafana连不上数据源,每一步都能让人折腾大半天。
这篇内容就是冲着这些“环境坑”来的。我会把SpringBoot集成Prometheus加Grafana这套组合的环境准备过程,从版本选型、依赖引入、配置暴露、服务部署到数据源打通,一步步拆开讲清楚,并且把每一步背后的“为什么”说明白。不管你是刚接触监控的新手,还是已经用过一段时间但总在环境上翻车的开发者,都能从这里找到可以直接抄作业的配置和排查思路。整套环境准备完成后,你就能看到一个能实时反映应用健康状态的监控面板,后面再往上加业务指标、告警规则,都是水到渠成的事。
2. 组件选型与版本匹配:别让版本号成为第一个拦路虎
2.1 三个组件的角色分工,先搞清楚谁管什么
在动手之前,有必要把这三个东西的职责边界理清楚,否则很容易出现“配置写错地方”的低级问题。SpringBoot应用在这里扮演的是被监控对象加指标生产者的角色,它通过Actuator和Micrometer把JVM、HTTP请求、数据库连接池等运行时数据转换成Prometheus能识别的格式,暴露在一个HTTP端点上。Prometheus是指标采集与存储中心,它按照配置的时间间隔主动去拉取(pull)SpringBoot暴露的端点,把数据存进自己的时序数据库。Grafana则是可视化层,它不生产数据也不存储数据,只负责从Prometheus查询数据并渲染成图表。
理解了这个分工,很多配置就不会放错位置。比如“采集频率”这个参数,它属于Prometheus的配置,不是SpringBoot的;而“暴露哪些指标”属于SpringBoot的配置,Prometheus管不着。我见过有人把抓取间隔配在application.yml里,然后纳闷为什么没生效,这就是角色没分清导致的。
2.2 版本兼容性:一张表说清楚怎么选
版本匹配是环境准备阶段最容易埋雷的地方。SpringBoot 2.x和3.x在Actuator端点的默认暴露策略、Micrometer的版本、以及Prometheus客户端库的依赖上都有差异。下面这张表是我在实际项目中验证过的组合,可以直接参考:
| 组件 | 推荐版本 | 选择理由 |
|---|---|---|
| SpringBoot | 2.7.x 或 3.2.x | 2.7是2.x最后稳定版,3.2对Java 17支持成熟 |
| Micrometer | 随SpringBoot BOM | 不要手动指定版本,交给BOM管理最稳 |
| Prometheus | 2.45 LTS以上 | LTS版本长期维护,避免用太新的实验特性 |
| Grafana | 10.x | 10.x对Prometheus数据源支持完善,界面成熟 |
| JDK | 8/11(配2.7)或17(配3.2) | 版本对应关系别搞反 |
这里有个经验:Micrometer的版本千万不要手动在pom里写死。SpringBoot的依赖管理(BOM)已经帮你选好了和当前版本兼容的Micrometer,手动指定极容易造成版本冲突,表现为应用启动时报NoSuchMethodError或者指标端点返回空。我早期就干过这事,为了“用上新特性”强行升级Micrometer,结果整个Actuator端点全挂,排查了半天才发现是版本打架。
2.3 部署形态的选择:全本地还是容器化
环境准备还有一个决策点:这三个组件是全部装在本机,还是用容器跑。两种方式我都用过,各有适用场景。全本地安装适合你想深入理解每个组件的配置文件结构和启动流程,出问题容易定位;容器化部署(比如用容器编排工具)适合快速搭一套可复现的环境,尤其是团队协作时,一份编排文件就能让所有人环境一致。
我的建议是:第一次搭建时用本地安装,把Prometheus的配置文件、Grafana的启动参数都亲手过一遍,这样后面遇到抓取失败、数据源连不上时,你脑子里有清晰的链路。等环境跑通了,再考虑用容器化方式固化下来。下面章节的实操我会以本地安装为主线,容器化的关键差异会单独点出来。
3. SpringBoot侧的改造:让应用主动“开口说话”
3.1 引入依赖:两个依赖缺一不可
SpringBoot应用要暴露Prometheus格式的指标,需要两个依赖:spring-boot-starter-actuator和micrometer-registry-prometheus。前者提供端点框架,后者提供Prometheus的指标格式转换。很多人只加了actuator,结果访问/actuator/prometheus返回404,就是因为缺了registry这个依赖。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> <scope>runtime</scope> </dependency>注意micrometer-registry-prometheus的scope设为runtime就够了,编译期不需要它。版本同样不要写,交给SpringBoot的BOM管理。加完依赖后启动应用,如果日志里出现PrometheusMeterRegistry相关的初始化信息,说明依赖生效了。
3.2 暴露端点:默认只开了一个口子
SpringBoot Actuator出于安全考虑,默认只暴露/actuator/health和/actuator/info两个端点。Prometheus需要的/actuator/prometheus端点默认是关闭的,必须手动打开。配置写在application.yml里:
management: endpoints: web: exposure: include: health,info,prometheus endpoint: prometheus: enabled: true metrics: tags: application: ${spring.application.name}这里有几个细节值得说。exposure.include里必须显式加上prometheus,用*通配虽然省事但会暴露所有端点,生产环境不建议。metrics.tags.application这行是给所有指标打上一个应用名的标签,后面在Grafana里做多应用区分时非常有用,不加的话所有应用的指标混在一起,根本分不清谁是谁。
配置完成后重启应用,访问http://localhost:8080/actuator/prometheus,应该能看到一大段以# HELP和# TYPE开头的文本,里面包含jvm_memory_used_bytes、http_server_requests_seconds_count这类指标。如果返回404,检查依赖是否加全;如果返回403,检查是否引入了Spring Security且没放行该端点。
3.3 自定义业务指标:环境通了再考虑的事
环境准备阶段其实不需要急着加自定义指标,但有必要知道入口在哪。Micrometer提供了MeterRegistry,注入后就能创建Counter、Gauge、Timer等指标。比如统计某个业务方法的调用次数:
@Component public class OrderMetrics { private final Counter orderCounter; public OrderMetrics(MeterRegistry registry) { this.orderCounter = Counter.builder("business_order_total") .description("订单创建总数") .tag("type", "create") .register(registry); } public void recordOrder() { orderCounter.increment(); } }这段代码在环境准备阶段可以先不写,但要知道自定义指标最终也会出现在同一个/actuator/prometheus端点里,Prometheus采集方式完全一样。先把默认指标跑通,再逐步加业务指标,节奏更稳。
提示:如果应用配置了
server.servlet.context-path,Actuator端点的路径会变成/your-context/actuator/prometheus,Prometheus的抓取配置里也要相应调整,这个坑我在一个老项目里踩过,抓取目标一直显示down,查了半小时才发现是context-path没算进去。
4. Prometheus部署与抓取配置:让数据真正流起来
4.1 安装与目录结构:别小看目录规划
Prometheus的安装本身很简单,下载对应平台的压缩包解压即可。但目录结构值得提前规划,因为后面要放配置文件、数据文件、告警规则文件,乱放的话维护起来很痛苦。我习惯的目录结构是这样的:
prometheus/ ├── prometheus # 可执行文件 ├── prometheus.yml # 主配置文件 ├── data/ # 时序数据存储目录 └── rules/ # 告警规则目录(后续用)启动时通过参数指定配置文件和存储路径:
./prometheus \ --config.file=prometheus.yml \ --storage.tsdb.path=data/ \ --web.listen-address=:9090--storage.tsdb.path不指定的话默认是当前目录下的data,但显式指定更清晰。--web.listen-address默认就是9090,写出来是为了提醒自己端口别和别的服务冲突。启动后访问http://localhost:9090,能看到Prometheus自带的Web界面,说明服务起来了。
4.2 抓取配置:一个job对应一类目标
Prometheus的核心配置就是scrape_configs,它定义了“去哪里抓、多久抓一次”。针对SpringBoot应用,最基础的配置长这样:
global: scrape_interval: 15s evaluation_interval: 15s scrape_configs: - job_name: 'springboot-app' metrics_path: '/actuator/prometheus' scrape_interval: 10s static_configs: - targets: ['localhost:8080'] labels: env: 'dev' app: 'demo-service'逐项解释一下。global.scrape_interval是全局默认抓取间隔,这里设15秒;具体job里又设了10秒,会覆盖全局值,因为业务应用的关键指标希望采得密一点。metrics_path必须和SpringBoot暴露的路径一致,默认就是/actuator/prometheus。targets里写应用的IP和端口,多个实例就写多个。labels是给这批目标打的标签,env区分环境,app区分应用,后面在Grafana里做筛选全靠这些标签。
配置改完后不用重启Prometheus,它支持热加载。发送一个SIGHUP信号或者调用/-/reload端点(需要启动时加--web.enable-lifecycle)即可。我一般用curl -X POST http://localhost:9090/-/reload,改完配置刷一下就行,比重启快得多。
4.3 验证抓取状态:Targets页面是第一个检查点
配置生效后,打开Prometheus界面的Status -> Targets,应该能看到springboot-app这个job下的目标状态是UP。如果是DOWN,把鼠标悬停在错误信息上,常见原因有几种:连接被拒绝说明应用没启动或端口不对;返回404说明metrics_path配错了;返回403说明端点被安全框架拦截了。这个页面是排查抓取问题的第一站,比看日志快。
状态是UP之后,可以去Graph页面输入一个指标名,比如jvm_memory_used_bytes,点Execute,如果能看到曲线或数值,说明数据已经进到Prometheus里了。这一步验证通过,整个采集链路就算打通了。
注意:如果SpringBoot应用和Prometheus不在同一台机器,
targets里要写应用所在机器的IP,并且确保防火墙放行了应用端口。我遇到过本地Prometheus抓远程应用一直down,最后发现是云服务器安全组没开端口,这种网络层的问题最容易被忽略。
5. Grafana接入与第一个监控面板
5.1 安装启动与初始登录
Grafana的安装同样简单,下载解压后直接运行bin/grafana-server(Linux/Mac)或bin\grafana-server.exe(Windows)。默认监听3000端口,启动后浏览器访问http://localhost:3000,初始用户名和密码都是admin,首次登录会强制要求改密码。这个改密码的步骤别跳过,不然后面每次登录都提示。
Grafana的配置文件在conf/defaults.ini,一般不需要改。如果3000端口被占用,可以在配置文件里改http_port,或者启动时用--config指定自定义配置文件。数据默认存在data/目录下,包括你后面建的面板、数据源配置,所以这个目录要记得备份,换机器时直接拷过去就能恢复。
5.2 添加Prometheus数据源:URL填对是成功的一半
登录后第一步是添加数据源。左侧菜单Connections -> Data sources -> Add data source,选择Prometheus。最关键的一项配置是Connection里的URL,填Prometheus的地址,本地就是http://localhost:9090。如果Prometheus和Grafana不在同一台机器,填Prometheus所在机器的IP和端口。
这里有个容易踩的坑:如果Grafana是用容器跑的,而Prometheus在宿主机上,URL里的localhost指向的是容器内部,不是宿主机。这种情况下要填宿主机的实际IP,或者用容器网络里的服务名。我见过有人卡在这里,数据源测试一直失败,其实就是localhost的指向问题。
URL填完后点Save & test,如果显示Data source is working,说明连通了。如果报错,检查Prometheus是否在运行、端口是否可达、有没有配反向代理导致路径变化。
5.3 导入现成面板:站在别人的肩膀上
Grafana社区有大量现成的SpringBoot监控面板,最经典的是ID为4701的JVM (Micrometer)面板,专门适配Micrometer暴露的指标。导入方式:左侧菜单Dashboards -> Import,在Import via grafana.com输入框里填4701,点Load,然后选择刚添加的Prometheus数据源,点Import即可。
导入后应该能看到JVM内存、CPU使用率、HTTP请求速率、GC次数等图表。如果某些图表显示No data,通常是两个原因:一是指标名对不上,不同Micrometer版本暴露的指标名可能有细微差异;二是数据源选错了。可以先在Prometheus的Graph页面确认指标存在,再回Grafana检查面板的查询语句。
5.4 自己动手建一个面板:从零理解查询逻辑
导入现成面板能快速看到效果,但要想真正会用,还是得自己建一个。点Dashboards -> New dashboard -> Add visualization,选择Prometheus数据源,在查询框里输入rate(http_server_requests_seconds_count[1m]),这是计算每分钟的HTTP请求速率。右上角选时间范围,比如最近15分钟,图表就会画出来。
这个查询语句值得解释一下:http_server_requests_seconds_count是一个Counter类型的指标,只增不减,直接画出来是一条一直上升的线,没有意义。rate(...[1m])计算的是过去1分钟内的每秒平均增长速率,这才是我们关心的“QPS”。理解Counter和rate的配合,是看懂大部分Prometheus查询的基础。
建好后点保存,给面板起个名字,比如“应用请求速率”。一个面板可以加多个查询,比如再加一条rate(http_server_requests_seconds_count{status="500"}[1m])来看错误请求速率,两条线放一起对比,异常时一眼就能看出来。
6. 环境准备阶段的高频问题与排查链路
6.1 端点返回404或403:从依赖到安全配置逐层查
/actuator/prometheus访问不到是最常见的问题。排查顺序应该是:先确认micrometer-registry-prometheus依赖加了没有,这是404的头号原因;再加了依赖还404,检查management.endpoints.web.exposure.include里有没有prometheus;如果返回403,检查项目里有没有引入Spring Security,有的话需要在安全配置里放行/actuator/**。
放行Actuator端点的配置大概是这样:
@Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth -> auth .requestMatchers("/actuator/**").permitAll() .anyRequest().authenticated()); return http.build(); } }生产环境当然不能这么简单放行,通常会给Actuator单独配一个管理端口,或者加认证。但环境准备阶段先放行跑通,后面再收紧,这个顺序比较合理。
6.2 Prometheus目标DOWN:网络、路径、格式三查
Targets页面显示DOWN时,按这个链路排查:第一步,在Prometheus所在机器上用curl http://应用IP:端口/actuator/prometheus,看能不能拿到数据,拿不到就是网络或应用本身的问题;第二步,能拿到数据但Prometheus还是DOWN,检查metrics_path配置是否和实际路径一致,特别是应用配了context-path的情况;第三步,路径也对但还是DOWN,看Prometheus日志里的具体报错,有时候是返回内容格式不符合预期,比如被网关改写了响应。
我遇到过一次很隐蔽的情况:应用前面挂了一个反向代理,代理对/actuator/prometheus这个路径做了重写,导致Prometheus拿到的响应不是标准格式,目标一直DOWN。后来在代理配置里放行了这个路径才解决。所以链路里每多一层组件,就多一个排查点。
6.3 Grafana数据源连不上:URL和网络是重点
数据源测试失败,先确认Prometheus的Web界面能不能正常访问,能访问说明Prometheus本身没问题。然后检查Grafana里填的URL,本地部署填http://localhost:9090,容器部署要注意localhost的指向问题。如果Prometheus配了基础认证,Grafana数据源里也要填对应的用户名密码。还有一种情况是跨域问题,不过Prometheus默认允许跨域,一般不会卡在这。
6.4 指标缺失或面板无数据:指标名和标签要对上
面板导入后部分图表无数据,最常见的原因是指标名不匹配。不同版本的Micrometer暴露的指标名会有差异,比如http_server_requests_seconds_count在某些版本里可能是http_server_requests_seconds。解决办法是在Prometheus的Graph页面输入指标名的前缀,看自动补全出来的实际名称是什么,然后回Grafana改查询语句。另一个原因是标签筛选太严,比如面板里写了{application="xxx"}但你的应用没打这个标签,数据自然查不出来。
7. 把环境固化成可复现的配置
环境跑通只是第一步,真正有价值的是让这套环境可以随时重建、团队里每个人都能一键搭起来。我的做法是把所有配置都纳入版本管理:SpringBoot的application.yml、Prometheus的prometheus.yml、Grafana的数据源和面板配置(可以通过Provisioning机制导出成YAML文件),全部放进代码仓库。这样新同事拉下代码,按README跑几条命令,环境就起来了,不用再重复踩一遍我踩过的坑。
Grafana的Provisioning配置放在conf/provisioning/datasources/目录下,一个YAML文件就能定义好数据源,启动时自动加载。面板也可以通过conf/provisioning/dashboards/目录自动导入。这套机制在团队协作场景下特别省事,避免了“我本地面板配好了但你那边没有”的尴尬。
环境准备这件事,看起来是体力活,但每一步背后的原理和取舍都值得琢磨。把这一层打扎实,后面做指标采集、告警规则、性能分析时,你会发现自己少走了很多弯路。我在实际项目里的体会是,监控系统的价值不在于装了多少组件,而在于当问题发生时,你能不能第一时间从面板上看出问题在哪。而这一切的起点,就是现在这套看起来不起眼的环境。