ElIauk

Prometheus + Grafana 监控 Spring Boot

监控不是堆一个 Dashboard 很多项目的“监控”止步于 Grafana 上挂一个看着很专业的 JVM 面板,真出问题时却没人看得懂:CPU 曲线很平稳,接口却超时;堆内存没满,服务却疯狂 Full GC。问题通常不在 Grafana,而在指标本身——只采了默认指标,没有业务维度的分位数、错误率

评论
Prometheus + Grafana 监控 Spring Boot

监控不是堆一个 Dashboard

很多项目的“监控”止步于 Grafana 上挂一个看着很专业的 JVM 面板,真出问题时却没人看得懂:CPU 曲线很平稳,接口却超时;堆内存没满,服务却疯狂 Full GC。问题通常不在 Grafana,而在指标本身——只采了默认指标,没有业务维度的分位数、错误率和饱和度。

Prometheus + Grafana 的正确用法是:先定义要回答的问题(QPS 多少、P99 多少、错误率多少、资源还剩多少),再决定采哪些指标、配什么告警。

暴露指标:Actuator + Micrometer

Spring Boot 3.3/3.4 只需要两个依赖:spring-boot-starter-actuator 和 micrometer-registry-prometheus。

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus,metrics
  endpoint:
    health:
      probes:
        enabled: true
  metrics:
    tags:
      application: ${spring.application.name}
    distribution:
      percentiles-histogram:
        http.server.requests: true
  prometheus:
    metrics:
      export:
        enabled: true

percentiles-histogram 是关键。不开启时,Micrometer 只输出 _count、_sum、_max,histogram_quantile 算不出 P99;开启后会额外输出 _bucket,才能做分位数和 SLO 统计。代价是时间序列数量增加,桶不要设得太密。

management.server.port 如果单独开了管理端口,Prometheus 抓取地址要跟着改。生产环境不要把 actuator 直接暴露在公网,至少用网络策略限制来源,或者加一层 Basic Auth。

Prometheus 抓取与告警

静态配置适合小规模,K8s 里更常用注解发现或 ServiceMonitor:

scrape_configs:
  - job_name: spring-boot
    metrics_path: /actuator/prometheus
    scrape_interval: 15s
    static_configs:
      - targets: ['order-service:8080']

告警规则要围绕“用户能感知的指标”,而不是“CPU 超过 80%”这种间接指标:

groups:
  - name: spring-boot-slo
    rules:
      - alert: HighHttp5xxRate
        expr: |
          sum by (application) (rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
          /
          sum by (application) (rate(http_server_requests_seconds_count[5m]))
          > 0.05
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.application }} 5xx 比例超过 5%"

for 很重要,没有它,一次发布重启带来的毛刺就会触发告警。告警发到哪里由 Alertmanager 负责,Prometheus 自己不发通知,这一步经常被漏掉。

几个真正有用的 PromQL

指标名和标签要先对齐:Boot 3 + Micrometer 的 HTTP 计时器叫 http_server_requests_seconds,常用标签是 application、uri、status、method。下面几条查询都基于这套标签。

接口 QPS:

sum by (application) (rate(http_server_requests_seconds_count[1m]))

P99 耗时,依赖 _bucket 序列:

histogram_quantile(0.99,
  sum by (le, application) (
    rate(http_server_requests_seconds_bucket[5m])
  )
)

堆内存使用率:

sum by (application) (jvm_memory_used_bytes{area="heap"})
/ sum by (application) (jvm_memory_max_bytes{area="heap"})

连接池饱和度,接近 1 就该扩容或查慢 SQL:

hikaricp_connections_active / hikaricp_connections_max

GC 平均停顿:

rate(jvm_gc_pause_seconds_sum[5m]) / rate(jvm_gc_pause_seconds_count[5m])

看这些指标时有个基本原则:先看用户侧(QPS、错误率、P99),再看资源侧(CPU、内存、连接池)。资源指标正常但 P99 上涨,问题多半在依赖、锁或者 GC;资源指标先恶化,再顺着饱和点往下查。Grafana 面板不用追求多,一张黄金指标图加一张 JVM 图,足够覆盖大部分故障。指标量太大时,用 metric_relabel_configs 丢掉不需要的 series,别让每个面板都扫全量数据。

常见坑

坑一:/actuator/prometheus 返回 404。 要么没引 micrometer-registry-prometheus,要么端点没在 exposure.include 里,要么访问的是管理端口而不是业务端口。

坑二:histogram_quantile 返回 NaN。 没有 _bucket 序列,通常是没开 percentiles-histogram。另外 rate 的窗口要大于抓取间隔的 4 倍,15 秒抓取配 [1m] 才稳。

坑三:URI 标签基数爆炸。 http_server_requests_seconds 带 uri 标签,正常应该是 /orders/{id} 这种模板。但如果 404 请求把原始路径带进来,或者代码里拼接了 ID,序列数会迅速涨到几十万。用 MeterFilter 过滤或改写标签。

坑四:把实例级指标直接求和。 多副本时 sum 会掩盖单实例异常。排查时要按 instance 拆开看,别只看总量。

坑五:Target 显示 DOWN。 先 curl 目标地址确认能返回指标,再查服务发现、端口、防火墙和 Prometheus 的 up 指标,不要一上来就怀疑 Grafana。

坑六:Grafana 面板用了旧指标名。 从 Boot 2 升到 Boot 3,部分 HTTP 和 JVM 指标的标签有调整,旧面板会直接显示 No data。升级时先对比 /actuator/prometheus 的实际输出,再改面板,别急着怀疑采集链路。

排查步骤

  1. curl http://服务:端口/actuator/prometheus 确认指标能出来,关键字搜 http_server_requests。
  2. 打开 Prometheus 的 Targets 页面,确认该实例是 UP,没有报权限或超时;顺便看 scrape_duration_seconds,接近抓取间隔说明指标太多或应用响应太慢。
  3. 在 Prometheus 表达式浏览器里单独跑一次 P99 查询,确认不是 Grafana 变量的问题。
  4. 检查告警规则的 expr 和 for,用 promtool check rules 做语法校验。
  5. 最后看 Alertmanager 有没有收到告警,很多“告警没发出来”只是通知链路没配。

参考

评论

推荐阅读

订阅邮件

订阅更新,及时收到最新文章。

作者