Spring Boot Actuatorは、本番環境でアプリケーションを監視・運用するための強力な機能セットを提供します。本記事では、Actuatorエンドポイントの有効化から、カスタムヘルスインジケータの実装、Prometheus形式でのメトリクスエクスポート、Grafanaダッシュボードとの連携までを実践的に解説します。

実行環境と前提条件

本記事の内容を実践するにあたり、以下の環境を前提としています。

項目 バージョン・要件
Java 17以上
Spring Boot 3.4.x
ビルドツール Maven または Gradle
IDE VS Code または IntelliJ IDEA

事前に以下の準備を完了してください。

  • Spring Bootプロジェクトが作成済みであること
  • 基本的なREST APIの実装経験があること

Spring Boot Actuatorとは

Spring Boot Actuatorは、アプリケーションの健全性監視、メトリクス収集、環境情報の取得などを行うための本番環境向け機能です。主な特徴は以下のとおりです。

  • ヘルスチェックエンドポイント: アプリケーションやデータベースの死活監視
  • メトリクスエンドポイント: JVM、HTTP、データベースなどのメトリクス収集
  • 情報エンドポイント: アプリケーションのバージョン、Git情報の表示
  • 環境エンドポイント: プロパティや環境変数の確認

Actuatorの依存関係を追加する

Mavenの場合

pom.xmlに以下の依存関係を追加します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<dependencies>
    <!-- Spring Boot Actuator -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
    <!-- Prometheus形式でのメトリクスエクスポート用 -->
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
</dependencies>

Gradleの場合

build.gradleに以下を追加します。

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

Actuatorエンドポイントの有効化と公開設定

依存関係を追加しただけでは、すべてのエンドポイントが公開されるわけではありません。application.ymlで適切に設定する必要があります。

基本設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
      base-path: /actuator
  endpoint:
    health:
      show-details: when_authorized
      show-components: when_authorized
    prometheus:
      enabled: true
  info:
    env:
      enabled: true

設定項目の解説

設定項目 説明
management.endpoints.web.exposure.include HTTP経由で公開するエンドポイントを指定
management.endpoints.web.base-path Actuatorエンドポイントのベースパス(デフォルト: /actuator
management.endpoint.health.show-details ヘルスチェックの詳細表示条件
management.endpoint.prometheus.enabled Prometheusエンドポイントの有効化

show-detailsの設定値

説明
never 詳細を表示しない(デフォルト)
when_authorized 認証済みユーザーにのみ詳細を表示
always 常に詳細を表示

すべてのエンドポイントを公開する場合

開発環境では、すべてのエンドポイントを公開することで詳細な情報を確認できます。

1
2
3
4
5
management:
  endpoints:
    web:
      exposure:
        include: "*"

本番環境では必要最小限のエンドポイントのみを公開することを推奨します。

主要なActuatorエンドポイント

以下は頻繁に使用されるActuatorエンドポイントの一覧です。

エンドポイント パス 説明
health /actuator/health アプリケーションの健全性を確認
info /actuator/info アプリケーション情報を表示
metrics /actuator/metrics 利用可能なメトリクス一覧を表示
prometheus /actuator/prometheus Prometheus形式でメトリクスをエクスポート
env /actuator/env 環境プロパティを表示
loggers /actuator/loggers ログレベルの表示・変更
beans /actuator/beans 登録されているBeanの一覧

ヘルスチェックエンドポイントの詳細

基本的なヘルスチェック

アプリケーションを起動して/actuator/healthにアクセスすると、以下のようなレスポンスが返されます。

1
2
3
{
  "status": "UP"
}

詳細表示を有効にした場合

show-details: alwaysを設定すると、詳細な情報が表示されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "PostgreSQL",
        "validationQuery": "isValid()"
      }
    },
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 499963174912,
        "free": 250000000000,
        "threshold": 10485760,
        "path": "/",
        "exists": true
      }
    },
    "ping": {
      "status": "UP"
    }
  }
}

自動構成されるヘルスインジケータ

Spring Boot Actuatorは、依存関係に基づいて以下のヘルスインジケータを自動で構成します。

インジケータ 条件
DataSourceHealthIndicator DataSourceが存在する場合
DiskSpaceHealthIndicator 常に有効
RedisHealthIndicator Redis接続が設定されている場合
MongoHealthIndicator MongoDB接続が設定されている場合
ElasticsearchRestClientHealthIndicator Elasticsearch接続が設定されている場合
RabbitHealthIndicator RabbitMQ接続が設定されている場合

カスタムヘルスインジケータの実装

外部APIやカスタムリソースの健全性を監視するために、独自のヘルスインジケータを実装できます。

基本的なカスタムヘルスインジケータ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.example.demo.health;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
public class ExternalApiHealthIndicator implements HealthIndicator {

    private final ExternalApiClient externalApiClient;

    public ExternalApiHealthIndicator(ExternalApiClient externalApiClient) {
        this.externalApiClient = externalApiClient;
    }

    @Override
    public Health health() {
        try {
            boolean isHealthy = externalApiClient.checkHealth();
            
            if (isHealthy) {
                return Health.up()
                    .withDetail("service", "External API")
                    .withDetail("status", "Available")
                    .build();
            } else {
                return Health.down()
                    .withDetail("service", "External API")
                    .withDetail("status", "Unavailable")
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("service", "External API")
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}

外部APIクライアントの例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.demo.health;

import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class ExternalApiClient {

    private final RestTemplate restTemplate;
    private static final String HEALTH_CHECK_URL = "https://api.example.com/health";

    public ExternalApiClient(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public boolean checkHealth() {
        try {
            restTemplate.getForEntity(HEALTH_CHECK_URL, String.class);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

データベース接続のカスタムヘルスインジケータ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.example.demo.health;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

@Component
public class DatabaseHealthIndicator implements HealthIndicator {

    private final DataSource dataSource;

    public DatabaseHealthIndicator(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Health health() {
        try (Connection connection = dataSource.getConnection();
             PreparedStatement statement = connection.prepareStatement("SELECT 1");
             ResultSet resultSet = statement.executeQuery()) {
            
            if (resultSet.next()) {
                return Health.up()
                    .withDetail("database", "Connected")
                    .withDetail("validationQuery", "SELECT 1")
                    .build();
            }
            
            return Health.down()
                .withDetail("database", "Query failed")
                .build();
                
        } catch (Exception e) {
            return Health.down()
                .withDetail("database", "Connection failed")
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}

ヘルスグループの設定

複数のヘルスインジケータをグループ化して、用途別に異なるエンドポイントで公開できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
management:
  endpoint:
    health:
      group:
        liveness:
          include: ping
        readiness:
          include: db,externalApi
        custom:
          include: externalApi
          show-details: always

この設定により、以下のエンドポイントが利用可能になります。

  • /actuator/health/liveness: Kubernetes Liveness Probe用
  • /actuator/health/readiness: Kubernetes Readiness Probe用
  • /actuator/health/custom: カスタムグループ

Prometheusメトリクス形式でのエクスポート

Prometheusエンドポイントの確認

micrometer-registry-prometheusを追加すると、/actuator/prometheusエンドポイントが有効になります。

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

以下のような形式でメトリクスが出力されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# HELP jvm_memory_used_bytes The amount of used memory
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{area="heap",id="G1 Eden Space",} 4.2991616E7
jvm_memory_used_bytes{area="heap",id="G1 Old Gen",} 1.6777216E7
jvm_memory_used_bytes{area="heap",id="G1 Survivor Space",} 8388608.0
jvm_memory_used_bytes{area="nonheap",id="CodeCache",} 1.4680064E7

# HELP http_server_requests_seconds Duration of HTTP server request handling
# TYPE http_server_requests_seconds summary
http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/api/users",} 150.0
http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/api/users",} 2.5

# HELP process_cpu_usage The "recent cpu usage" for the Java Virtual Machine process
# TYPE process_cpu_usage gauge
process_cpu_usage 0.0025

主要なメトリクス

メトリクス名 説明
jvm_memory_used_bytes JVMのメモリ使用量
jvm_gc_pause_seconds GCの停止時間
http_server_requests_seconds HTTPリクエストの処理時間
process_cpu_usage CPU使用率
hikaricp_connections_active アクティブなDB接続数
tomcat_threads_current 現在のTomcatスレッド数

カスタムメトリクスの追加

Micrometerを使用して、独自のメトリクスを追加できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.example.demo.service;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private final Counter orderCounter;
    private final Timer orderProcessingTimer;

    public OrderService(MeterRegistry meterRegistry) {
        this.orderCounter = Counter.builder("orders.created.total")
            .description("Total number of orders created")
            .tag("type", "all")
            .register(meterRegistry);
        
        this.orderProcessingTimer = Timer.builder("orders.processing.time")
            .description("Time taken to process orders")
            .register(meterRegistry);
    }

    public Order createOrder(OrderRequest request) {
        return orderProcessingTimer.record(() -> {
            // 注文処理ロジック
            Order order = processOrder(request);
            orderCounter.increment();
            return order;
        });
    }

    private Order processOrder(OrderRequest request) {
        // 実際の処理
        return new Order();
    }
}

アノテーションを使用したメトリクス収集

@Timedアノテーションを使用すると、より簡潔にメトリクスを収集できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package com.example.demo.controller;

import io.micrometer.core.annotation.Timed;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping
    @Timed(value = "users.list.time", description = "Time taken to list users")
    public List<User> getUsers() {
        return userService.findAll();
    }
}

@Timedを有効にするには、TimedAspectをBean登録する必要があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.example.demo.config;

import io.micrometer.core.aop.TimedAspect;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MetricsConfig {

    @Bean
    public TimedAspect timedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);
    }
}

Prometheus + Grafana連携

Prometheusの設定

prometheus.ymlに以下のスクレイプ設定を追加します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'spring-boot-app'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['host.docker.internal:8080']
        labels:
          application: 'demo-api'
          environment: 'development'

Docker Composeでの構成例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    networks:
      - monitoring

  prometheus:
    image: prom/prometheus:v2.48.0
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:10.2.0
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    networks:
      - monitoring
    depends_on:
      - prometheus

networks:
  monitoring:
    driver: bridge

volumes:
  prometheus-data:
  grafana-data:

監視構成のアーキテクチャ

以下はSpring Boot ActuatorとPrometheus、Grafanaの連携アーキテクチャです。

flowchart LR
    subgraph Application
        SB[Spring Boot App]
        AC[Actuator]
        MR[Micrometer Registry]
    end
    
    subgraph Monitoring
        PM[Prometheus]
        GF[Grafana]
    end
    
    SB --> AC
    AC --> MR
    MR -->|/actuator/prometheus| PM
    PM -->|データソース| GF
    GF -->|ダッシュボード| User((運用者))

Grafanaダッシュボードの設定

  1. Grafanaにログイン(初期認証: admin/admin)
  2. Configuration > Data Sources > Add data source
  3. Prometheusを選択し、URL に http://prometheus:9090 を設定
  4. Dashboards > Import からダッシュボードをインポート

推奨のダッシュボードID(Grafana公式ギャラリーから):

ダッシュボードID 名称 用途
4701 JVM (Micrometer) JVMメトリクス監視
11378 Spring Boot Statistics Spring Boot統計情報
12900 Spring Boot APM Dashboard アプリケーションパフォーマンス監視

Kubernetes環境でのヘルスチェック設定

KubernetesのLiveness ProbeとReadiness Probeに対応した設定例です。

application.ymlの設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
management:
  endpoint:
    health:
      probes:
        enabled: true
      group:
        liveness:
          include: livenessState
        readiness:
          include: readinessState,db
  health:
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true

Kubernetesマニフェストの例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo-api
  template:
    metadata:
      labels:
        app: demo-api
    spec:
      containers:
        - name: demo-api
          image: demo-api:latest
          ports:
            - containerPort: 8080
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 3
            failureThreshold: 3
          resources:
            requests:
              memory: "512Mi"
              cpu: "250m"
            limits:
              memory: "1Gi"
              cpu: "500m"

セキュリティ設定

本番環境ではActuatorエンドポイントを適切に保護する必要があります。

Spring Securityを使用した設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.example.demo.config;

import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                // health と prometheus は認証なしでアクセス可能
                .requestMatchers(EndpointRequest.to("health", "prometheus")).permitAll()
                // その他のActuatorエンドポイントは管理者のみ
                .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ADMIN")
                // APIエンドポイントは認証必須
                .requestMatchers("/api/**").authenticated()
                .anyRequest().permitAll()
            )
            .httpBasic(httpBasic -> {});
        
        return http.build();
    }
}

別ポートでActuatorを公開

Actuatorエンドポイントをアプリケーションとは別のポートで公開することで、内部ネットワークからのみアクセス可能にできます。

1
2
3
4
management:
  server:
    port: 8081
    address: 127.0.0.1

期待される結果

本記事の設定を完了すると、以下の結果が期待できます。

ヘルスチェック確認

1
2
3
4
5
# 基本的なヘルスチェック
curl http://localhost:8080/actuator/health

# 期待されるレスポンス
{"status":"UP"}

Prometheusメトリクス確認

1
2
3
4
5
6
curl http://localhost:8080/actuator/prometheus | head -20

# 期待されるレスポンス(一部)
# HELP jvm_memory_used_bytes The amount of used memory
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{area="heap",id="G1 Eden Space",} 4.2991616E7

利用可能なエンドポイント一覧

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
curl http://localhost:8080/actuator

# 期待されるレスポンス
{
  "_links": {
    "self": {"href": "http://localhost:8080/actuator"},
    "health": {"href": "http://localhost:8080/actuator/health"},
    "prometheus": {"href": "http://localhost:8080/actuator/prometheus"},
    "metrics": {"href": "http://localhost:8080/actuator/metrics"},
    "info": {"href": "http://localhost:8080/actuator/info"}
  }
}

まとめ

本記事では、Spring Boot Actuatorを使用したREST APIの監視について解説しました。

  • Actuatorエンドポイントの有効化: 依存関係の追加とapplication.ymlでの公開設定
  • カスタムヘルスインジケータ: 外部APIやデータベースの健全性監視を独自実装
  • Prometheusメトリクス: Micrometer経由でのメトリクスエクスポートとカスタムメトリクス追加
  • Grafana連携: ダッシュボードによる可視化と監視
  • Kubernetes対応: LivenessとReadinessプローブの設定
  • セキュリティ: Spring Securityによるエンドポイント保護

Actuatorを適切に設定することで、本番環境での問題検知と迅速な対応が可能になります。運用チームと連携し、アプリケーションの健全性を継続的に監視できる体制を構築してください。

参考リンク