Spring Boot Actuator
ActuatorはSpring Bootのダッシュボードです。ヘルスステータス, メトリクス, 設定情報を提供し, 運用チームに「俯瞰的な視点」を与えてくれます。
1. 学ぶ内容
- Actuatorエンドポイント概要:
/health//info//metrics//env//beans - エンドポイントの有効化と公開ポリシー:
management.endpoints.web.exposure.include - カスタムHealthIndicatorでデータベースや外部サービスの接続性を監視
@ReadOperation/@WriteOperationカスタムエンドポイント- セキュリティ強化:エンドポイントのアクセス制御とネットワーク分離
2. 運用エンジニアのリアルストーリー
(1) ペインポイント:本番環境がブラックボックス
BobはOrderFlow本番環境の運用保守を担当していますが, アプリケーションは彼にとって完全なブラックボックスです。データベース接続は正常か?メモリはどれくらい使用されているか?どのAPIの応答が遅いのか?問題が発生するたびに, Aliceにログ追加やアプリケーション再起動を依頼してトラブルシューティングを行う必要があり, 平均MTTR (平均復旧時間)は1時間を超えています。CharlieはMTTRを10分以内に短縮するよう要求しています。
(2) Actuatorによる解決策
Actuatorはすぐに使え, 幅広い運用エンドポイントを提供しています:
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
curl http://localhost:8080/actuator/health
# {"status":"UP","components":{"db":{"status":"UP"},"diskSpace":{"status":"UP"}}}
(3) 成果
BobがActuatorを使い始めた後, /healthでデータベース接続を監視し, /metricsでAPI応答時間を追跡し, カスタムHealthIndicatorで外部決済ゲートウェイをチェックするようになり, MTTRは1時間から5分に短縮されました。
3. Actuatorエンドポイント概要
(1) 組み込みエンドポイント一覧
| エンドポイント | 説明 | デフォルト公開 |
|---|---|---|
/actuator/health |
アプリのヘルスステータス | ✅ はい |
/actuator/info |
アプリ情報 | ✅ はい |
/actuator/metrics |
指標一覧 | ❌ いいえ |
/actuator/metrics/{name} |
特定のメトリクス値 | ❌ いいえ |
/actuator/env |
環境設定 | ❌ いいえ |
/actuator/beans |
Bean一覧 | ❌ いいえ |
/actuator/loggers |
ログレベル | ❌ いいえ |
/actuator/threaddump |
スレッドダンプ | ❌ いいえ |
/actuator/heapdump |
ヒープダンプ | ❌ いいえ |
/actuator/prometheus |
Prometheus形式のメトリクス | ❌ いいえ |
(1) ▶ サンプル:Actuator依存関係の有効化
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
出力:
// 実行成功
(2) ▶ サンプル:エンドポイント公開の設定
management:
endpoints:
web:
exposure:
include: health,info,metrics,env,loggers
base-path: /actuator
endpoint:
health:
show-details: when-authorized
metrics:
enabled: true
info:
env:
enabled: true
出力:
設定が反映されました。
| 公開ポリシー | 設定値 | 説明 |
|---|---|---|
| ヘルスと情報のみ | include: health,info |
最も安全, デフォルト |
| 必要に応じて公開 | include: health,info,metrics |
推奨 |
| すべて表示 | include: "*" |
開発環境のみ |
| 特定を除外 | exclude: env,beans |
全体から除外 |
4. ヘルスエンドポイントの詳細解説
(1) ヘルスステータス集約ルール
graph TD
A["Health Status<br/>Aggregator"] --> B["DiskSpace<br/>UP"]
A --> C["DataSource<br/>UP"]
A --> D["PaymentGateway<br/>DOWN"]
A --> E["Redis<br/>UP"]
D --> F["Overall: DOWN<br/>Any DOWN → DOWN"]
| ステータス | 意味 | 集約ルール |
|---|---|---|
| UP | 正常 | — |
| DOWN | 異常 | 1つでもDOWN → 全体DOWN |
| DEGRADED | 低下 | 非重要コンポーネントの低下 |
| OUT_OF_SERVICE | サービス利用不可 | 1つでも → 全体OUT_OF_SERVICE |
| UNKNOWN | 不明 | デフォルト |
(1) ▶ サンプル:カスタムHealthIndicator
@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {
private final RestTemplate restTemplate;
public PaymentGatewayHealthIndicator(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
public Health health() {
try {
ResponseEntity<String> response = restTemplate.getForEntity(
"https://api.payment-gateway.com/ping", String.class);
if (response.getStatusCode().is2xxSuccessful()) {
return Health.up()
.withDetail("gateway", "reachable")
.withDetail("responseTime", response.getHeaders().getDate())
.build();
}
return Health.down()
.withDetail("gateway", "unhealthy")
.withDetail("statusCode", response.getStatusCode())
.build();
} catch (Exception e) {
return Health.down()
.withDetail("gateway", "unreachable")
.withDetail("error", e.getMessage())
.build();
}
}
}
出力:
// 実行成功
{
"status": "DOWN",
"components": {
"diskSpace": {"status": "UP", "details": {"total": 536870912000, "free": 268435456000}},
"db": {"status": "UP", "details": {"database": "MySQL", "validationQuery": "SELECT 1"}},
"paymentGateway": {"status": "DOWN", "details": {"gateway": "unreachable", "error": "Connection refused"}}
}
}
5. メトリクスエンドポイントの詳細解説
(1) ▶ サンプル:HTTPリクエストメトリクスの確認
# 利用可能なすべてのメトリクスを一覧表示
curl http://localhost:8080/actuator/metrics
# 特定のメトリクスを取得:HTTPサーバーリクエスト
curl http://localhost:8080/actuator/metrics/http.server.requests
# フィルタリングしたメトリクスを取得
curl "http://localhost:8080/actuator/metrics/http.server.requests?tag=uri:/api/v1/orders&tag=status:200"
出力:
{"status":"ok","data":{}}
| 一般的なメトリクス | 説明 |
|---|---|
http.server.requests |
HTTPリクエスト所要時間の分布 |
jvm.memory.used |
JVMメモリ使用量 |
jvm.threads.live |
アクティブスレッド数 |
process.cpu.usage |
プロセスCPU使用率 |
disk.total / disk.free |
ディスク容量 |
hikaricp.connections.active |
データベース接続プール |
(2) ▶ サンプル:カスタムビジネスメトリクス
@Service
public class OrderMetrics {
private final Counter orderCreatedCounter;
private final Timer orderCreationTimer;
private final AtomicLong pendingOrders;
public OrderMetrics(MeterRegistry registry) {
this.orderCreatedCounter = Counter.builder("orderflow.orders.created")
.description("Total orders created")
.tag("service", "orderflow")
.register(registry);
this.orderCreationTimer = Timer.builder("orderflow.orders.creation.time")
.description("Order creation time")
.register(registry);
this.pendingOrders = registry.gauge("orderflow.orders.pending",
new AtomicLong(0));
}
public void recordOrderCreated() {
orderCreatedCounter.increment();
pendingOrders.incrementAndGet();
}
public void recordOrderCompleted() {
pendingOrders.decrementAndGet();
}
public Timer getCreationTimer() {
return orderCreationTimer;
}
}
出力:
// 実行成功
6. カスタムエンドポイント
(1) ▶ サンプル:カスタムOrderStatsエンドポイント
@Endpoint(id = "orderstats")
@Component
public class OrderStatsEndpoint {
private final OrderRepository orderRepository;
public OrderStatsEndpoint(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@ReadOperation
public Map<String, Object> orderStats() {
long total = orderRepository.count();
long pending = orderRepository.countByStatus("PENDING");
long completed = orderRepository.countByStatus("COMPLETED");
long cancelled = orderRepository.countByStatus("CANCELLED");
return Map.of(
"totalOrders", total,
"pendingOrders", pending,
"completedOrders", completed,
"cancelledOrders", cancelled,
"completionRate", total > 0
? String.format("%.2f%%", (double) completed / total * 100)
: "N/A"
);
}
@ReadOperation
public Map<String, Object> orderStatsByStatus(
@Selector String status) {
long count = orderRepository.countByStatus(status.toUpperCase());
return Map.of("status", status, "count", count);
}
}
出力:
// 実行成功
management:
endpoint:
orderstats:
enabled: true
endpoints:
web:
exposure:
include: health,info,orderstats
curl http://localhost:8080/actuator/orderstats
# {"totalOrders":1523,"pendingOrders":45,"completedOrders":1420,"cancelledOrders":58,"completionRate":"93.30%"}
curl http://localhost:8080/actuator/orderstats/PENDING
# {"status":"PENDING","count":45}
7. セキュリティ強化
(1) エンドポイントセキュリティポリシー
| レベル | ポリシー | 説明 |
|---|---|---|
| ネットワーク層 | 専用管理ポート | management.server.port=8081 |
| ネットワーク層 | 内部IPにバインド | management.server.address=127.0.0.1 |
| アプリケーション層 | Spring Security制御 | .requestMatchers("/actuator/**").hasRole("ADMIN") |
| エンドポイント層 | 機密エンドポイントの無効化 | management.endpoint.env.enabled=false |
(1) ▶ サンプル:本番レベルのセキュリティ設定
# application-prod.yml
management:
server:
port: 8081 # 管理ポートの分離
address: 127.0.0.1 # localhostのみにバインド
endpoints:
web:
exposure:
include: health,prometheus,orderstats
base-path: /actuator
endpoint:
health:
show-details: when-authorized
env:
enabled: false
beans:
enabled: false
heapdump:
enabled: false
出力:
監視設定が読み込まれました
Prometheusターゲット:3件アクティブ
Grafanaダッシュボード:準備完了
| 環境 | 公開ポリシー | 管理ポート |
|---|---|---|
| 開発 | include: "*" |
ポート8080 |
| テスト | include: health,info,metrics,env |
同じポート8080 |
| 本番 | include: health,prometheus |
専用ポート8081 + 内部ネットワーク |
8. 総合例:OrderFlow Actuatorの完全設定
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus,orderstats
endpoint:
health:
show-details: when-authorized
orderstats:
enabled: true
metrics:
tags:
application: ${spring.application.name}
info:
env:
enabled: true
info:
app:
name: OrderFlow Service
version: @project.version@
description: Eコマース注文管理マイクロサービス
// PaymentGatewayHealthIndicator.java
@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {
private final RestTemplate restTemplate;
public PaymentGatewayHealthIndicator(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
public Health health() {
try {
ResponseEntity<String> resp = restTemplate
.getForEntity("https://api.payment.com/ping", String.class);
return resp.getStatusCode().is2xxSuccessful()
? Health.up().withDetail("gateway", "reachable").build()
: Health.down().withDetail("statusCode", resp.getStatusCode()).build();
} catch (Exception e) {
return Health.down().withDetail("error", e.getMessage()).build();
}
}
}
// OrderStatsEndpoint.java
@Endpoint(id = "orderstats")
@Component
public class OrderStatsEndpoint {
private final OrderRepository orderRepository;
public OrderStatsEndpoint(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@ReadOperation
public Map<String, Object> stats() {
return Map.of(
"total", orderRepository.count(),
"pending", orderRepository.countByStatus("PENDING"),
"completed", orderRepository.countByStatus("COMPLETED")
);
}
}
❓ よくある質問
/healthはロードバランサーとK8sプローブ向けであり, 機密情報は公開しません。詳細付きの/healthは運用チーム向けで, ADMIN権限が必要です。本番環境ではshow-details: when-authorizedを使用してください。HealthAggregatorを実装するか, StatusAggregator Beanを使用します。デフォルトルールは:Any DOWN → Overall DOWNです。statusOrderで優先度をカスタマイズできます。/actuator/envはパスワードを公開しますか?password, secret, key, tokenを含むキー値をマスクし (••••••と表示), 元の値は表示しません。それでも本番環境ではこのエンドポイントを無効にすることをお勧めします。micrometer-registry-prometheus依存関係を追加し, prometheusエンドポイントを公開し, Prometheus設定に収集ターゲットを追加します。詳しくは後のレッスン (L23)で解説します。📖 まとめ
- Actuatorはヘルス, メトリクス, 設定, スレッドなどの運用エンドポイントを提供。デフォルトではhealthとinfoのみ公開
- カスタムHealthIndicatorで外部サービスへの接続性を監視
- メトリクスエンドポイントはHTTP, JVM, 接続プールなどの組み込みメトリクスを提供
- カスタムエンドポイントは
@Endpoint+@ReadOperation/@WriteOperationを使用 - 本番セキュリティ:専用管理ポート + 内部ネットワークバインド + 限定公開 + Spring Security
show-details: when-authorizedでヘルス詳細の表示を制御
📝 練習問題
-
基本問題 (難易度 ⭐):OrderFlowにActuatorを追加し, health, info, metricsエンドポイントを公開するように設定し,
/actuator/healthが「UP」ステータスを返すことを確認してください。 -
応用問題 (難易度 ⭐⭐):カスタム
PaymentGatewayHealthIndicatorとOrderStatsEndpointを実装し, 本番環境用に専用管理ポートを設定してください。 -
チャレンジ (難易度 ⭐⭐⭐):
micrometer-registry-prometheus依存関係を追加し,/actuator/prometheusエンドポイントを公開し, カスタムビジネスメトリクス (注文作成数, 注文作成時間)を作成し, メトリクスの命名規則とタグ設計を検討してください。



