使用 Prometheus 監控 Spring Boot 應用程式

這篇文章記錄如何在 Spring Boot 應用程式中加入 micrometer-registry-prometheus,並透過 Spring Boot Actuator 暴露 Prometheus 可以抓取的 Metrics endpoint。

Grafana 與 Prometheus 的安裝流程已經在前一篇文章介紹過,因此本文會專注在 Spring Boot 與 Prometheus 之間的設定:Spring Boot 要如何產生監控指標、Prometheus 要如何抓取這些指標,以及設定完成後要如何確認資料是否正常。

整體架構

在這個監控流程中,Spring Boot 應用程式本身不會主動把資料推送到 Prometheus,而是透過 Actuator 開放 /actuator/prometheus endpoint。Prometheus 會依照設定的間隔定期呼叫這個 endpoint,將應用程式的 Metrics 抓回來儲存。

整體流程如下:

  1. Spring Boot 加入 Actuator 與 Prometheus Registry 相依套件
  2. Spring Boot 開放 /actuator/prometheus endpoint
  3. Prometheus 設定 scrape target,定期抓取 Spring Boot Metrics
  4. 在 Prometheus Web UI 確認 target 狀態與查詢 Metrics
  5. Grafana 透過 Prometheus 資料來源建立 Dashboard

本文會聚焦在前四個步驟,Grafana Dashboard 的操作不會在這篇文章中展開。


Spring Boot 加入監控相依套件

首先在 Spring Boot 專案中加入 spring-boot-starter-actuatormicrometer-registry-prometheus

spring-boot-starter-actuator 用來提供應用程式健康狀態、Metrics、環境資訊等管理 endpoint;micrometer-registry-prometheus 則會把 Micrometer 收集到的 Metrics 轉換成 Prometheus 可以讀取的格式。

如果專案使用 Maven,可以在 pom.xml 中加入以下相依套件:

1
2
3
4
5
6
7
8
9
10
11
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
</dependencies>

如果專案使用 Gradle,可以加入以下設定:

1
2
3
4
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
}

加入相依套件後,Spring Boot 啟動時會自動建立 Prometheus Meter Registry,並準備好 Prometheus 格式的 Metrics 輸出能力。

開放 Prometheus Endpoint

Actuator 預設不會把所有 endpoint 都暴露在 HTTP 上,因此需要在 application.yml 中設定要開放的 endpoint。

以下設定會開放 healthinfoprometheus,並將應用程式名稱加到 Metrics 標籤中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
spring:
application:
name: spring-boot-demo

management:
endpoints:
web:
exposure:
include: health,info,prometheus
endpoint:
health:
show-details: always
probes:
enabled: true
prometheus:
metrics:
export:
enabled: true
metrics:
tags:
application: ${spring.application.name}

這裡的重點是 management.endpoints.web.exposure.include 必須包含 prometheus,這樣 Spring Boot 才會對外提供 /actuator/prometheus

management.endpoint.health.probes.enabled 會額外啟用 liveness 與 readiness probe,方便在 Kubernetes 或其他平台中檢查應用程式狀態。
management.prometheus.metrics.export.enabled 則明確啟用 Prometheus metrics export。
management.metrics.tags.application 則是替所有 Metrics 加上一個固定的 application 標籤。這裡使用 ${spring.application.name},讓標籤值跟 Spring Boot 應用程式名稱保持一致。當 Prometheus 同時監控多個 Spring Boot 應用程式時,這個標籤可以用來區分資料來源。

如果專案使用 application.properties,可以改成以下寫法:

1
2
3
4
5
6
spring.application.name=spring-boot-demo
management.endpoints.web.exposure.include=health,info,prometheus
management.endpoint.health.show-details=always
management.endpoint.health.probes.enabled=true
management.prometheus.metrics.export.enabled=true
management.metrics.tags.application=${spring.application.name}

設定完成後,重新啟動 Spring Boot 應用程式,並開啟以下網址確認 endpoint 是否正常:

1
http://localhost:8080/actuator/prometheus

如果設定正確,畫面上會看到 Prometheus text format 的資料,例如:

1
2
3
# HELP jvm_memory_used_bytes The amount of used memory
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{application="spring-boot-demo",area="heap",id="G1 Eden Space"} 1.2582912E7

看到類似上述內容,就代表 Spring Boot 已經成功輸出 Prometheus 可以抓取的 Metrics。

調整 Management Port

預設情況下,Actuator endpoint 會與應用程式使用相同 port。例如應用程式跑在 8080,Prometheus endpoint 就會是 http://localhost:8080/actuator/prometheus

如果希望管理用 endpoint 與一般 API 分開,也可以另外指定 management port:

1
2
3
4
5
6
7
8
9
10
server:
port: 8080

management:
server:
port: 8081
endpoints:
web:
exposure:
include: health,info,prometheus

調整後,Prometheus 要抓取的網址會變成:

1
http://localhost:8081/actuator/prometheus

這種做法適合在正式環境中將應用程式流量與監控流量分開管理,也比較方便針對 management port 設定防火牆或內部網路存取規則。

設定 Prometheus Scrape Target

Spring Boot 端完成設定後,接著要修改 Prometheus 設定檔,讓 Prometheus 定期抓取 /actuator/prometheus

開啟 Prometheus 設定檔:

1
sudo nano /etc/prometheus/prometheus.yml

scrape_configs 中加入 Spring Boot 應用程式的 target:

1
2
3
4
5
6
7
8
9
10
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]

- job_name: "spring-boot-demo"
metrics_path: "/actuator/prometheus"
scrape_interval: 15s
static_configs:
- targets: ["localhost:8080"]

如果前面有另外設定 management.server.port: 8081,這裡的 target 就要改成 localhost:8081

1
2
3
4
5
- job_name: "spring-boot-demo"
metrics_path: "/actuator/prometheus"
scrape_interval: 15s
static_configs:
- targets: ["localhost:8081"]

這段設定中有幾個重點:

  • job_name 是 Prometheus 中用來識別這組監控任務的名稱
  • metrics_path 要對應 Spring Boot 暴露的 Prometheus endpoint
  • scrape_interval 代表 Prometheus 抓取資料的間隔
  • targets 是 Spring Boot 應用程式所在的主機與 port

如果 Prometheus 與 Spring Boot 執行在不同機器,targets 不能使用 localhost,必須改成 Spring Boot 所在機器的 IP 或網域名稱。例如:

1
2
3
4
5
- job_name: "spring-boot-demo"
metrics_path: "/actuator/prometheus"
scrape_interval: 15s
static_configs:
- targets: ["192.168.1.50:8080"]

修改完成後,重新啟動 Prometheus:

1
sudo systemctl restart prometheus

確認 Prometheus 狀態是否正常:

1
sudo systemctl status prometheus

在 Prometheus 確認 Target 狀態

Prometheus 重新啟動後,開啟瀏覽器進入 Prometheus Web UI:

1
http://localhost:9090

接著進入 Status -> Targets,確認 spring-boot-demo 這個 job 是否出現在列表中。

如果狀態顯示為 UP,代表 Prometheus 已經可以成功連線到 Spring Boot 的 /actuator/prometheus endpoint。

如果狀態顯示為 DOWN,可以先檢查以下幾個項目:

  • Spring Boot 應用程式是否正在執行
  • /actuator/prometheus 是否可以直接透過瀏覽器或 curl 開啟
  • Prometheus 設定中的 targets 是否使用正確 IP 與 port
  • 如果有設定 management.server.port,Prometheus 是否抓取到正確的 management port
  • 防火牆或容器網路是否允許 Prometheus 連線到 Spring Boot

也可以直接使用 curl 測試 Spring Boot 是否有正常輸出 Metrics:

1
curl http://localhost:8080/actuator/prometheus

查詢 Spring Boot Metrics

Target 狀態變成 UP 後,就可以在 Prometheus Web UI 的查詢頁面測試 Metrics。

例如查詢 JVM 記憶體使用量:

1
jvm_memory_used_bytes

查詢 HTTP request 次數:

1
http_server_requests_seconds_count

查詢指定應用程式的 HTTP request 次數:

1
http_server_requests_seconds_count{application="spring-boot-demo"}

如果應用程式有收到 HTTP request,通常可以看到 urimethodstatusexceptionoutcome 等標籤,這些資料後續就可以在 Grafana 中用來建立 API 請求量、錯誤率與延遲時間相關的圖表。

以下是完成設定後,在 Grafana 中查看 Spring Boot Micrometer Dashboard 的實際畫面:

Spring Security 設定

如果 Spring Boot 專案有使用 Spring Security,Prometheus 抓取 /actuator/prometheus 時可能會遇到 401 Unauthorized403 Forbidden

這時可以在安全性設定中允許 Prometheus endpoint 被存取。以下是 Spring Security 6 的範例設定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers(EndpointRequest.to(PrometheusScrapeEndpoint.class)).permitAll()
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());

return http.build();
}
}

這段設定只開放 Prometheus scrape endpoint,其他 API 仍然需要通過原本的驗證規則。若是在正式環境中使用,也建議搭配內部網路、防火牆或反向代理限制 Prometheus endpoint 的存取來源。

加入自訂 Metrics

除了 JVM、HTTP request、thread、GC 等 Spring Boot 預設 Metrics,也可以透過 Micrometer 建立自訂指標。

以下範例示範使用 Counter 記錄訂單建立次數:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

private final Counter orderCreatedCounter;

public OrderService(MeterRegistry meterRegistry) {
this.orderCreatedCounter = Counter.builder("order.created")
.description("Total number of created orders")
.tag("module", "order")
.register(meterRegistry);
}

public void createOrder() {
orderCreatedCounter.increment();
}
}

呼叫 createOrder() 後,再開啟 /actuator/prometheus,就可以看到類似以下的 Metrics:

1
2
3
# HELP order_created_total Total number of created orders
# TYPE order_created_total counter
order_created_total{application="spring-boot-demo",module="order"} 1.0

接著可以在 Prometheus 中查詢:

1
order_created_total

自訂 Metrics 適合用來記錄與業務邏輯有關的資訊,例如訂單建立數、付款失敗數、任務處理時間或外部 API 呼叫結果。這些資料通常比單純的 JVM 指標更貼近應用程式的實際運作狀態。

常見問題

/actuator/prometheus 回傳 404

通常是因為缺少 micrometer-registry-prometheus 相依套件,或是 management.endpoints.web.exposure.include 沒有包含 prometheus。可以先確認相依套件是否已加入,並重新啟動 Spring Boot。

Prometheus Target 顯示 DOWN

先確認 Spring Boot 應用程式是否正常執行,再檢查 Prometheus 設定中的 targetsmetrics_path 是否正確。如果 Prometheus 與 Spring Boot 不在同一台機器,targets 不應該使用 localhost

Prometheus 顯示 401 或 403

如果專案有使用 Spring Security,需要允許 Prometheus 存取 /actuator/prometheus。可以只開放 Prometheus scrape endpoint,避免把所有 Actuator endpoint 都直接公開。

查不到 HTTP request Metrics

http_server_requests_seconds_count 需要應用程式實際收到 HTTP request 後才會出現。如果剛啟動應用程式還沒有任何請求,可以先呼叫幾次 API,再回到 Prometheus 查詢。

小結

透過 Spring Boot Actuator 與 micrometer-registry-prometheus,Spring Boot 應用程式可以很容易地輸出 Prometheus 格式的 Metrics。設定的核心重點有三個:加入 Actuator 與 Prometheus Registry 相依套件、開放 /actuator/prometheus endpoint,並在 Prometheus 中設定正確的 scrape target。

完成這些設定後,Prometheus 就能定期收集 Spring Boot 的 JVM、HTTP request 與自訂 Metrics。後續只要在 Grafana 中使用 Prometheus 作為資料來源,就可以進一步建立應用程式監控 Dashboard,觀察 API 流量、錯誤率、回應時間與資源使用狀況。