PrometheusとGrafanaでメトリクスを可視化しても、「どのサービスで遅延が起きているか」を特定するのは難しいですよね。それを解決するのが分散トレーシングです。
この記事では Spring Boot 3.2以上 を前提に、Micrometer TracingとZipkinを使って2サービス間でトレースIDが伝播していることをZipkin UIで確認するまでの手順を解説します。RestClientはSpring Boot 3.2で追加されたHTTPクライアントです。3.0/3.1をお使いの方はWebClientを代替として使ってください(RestTemplate・WebClientの使い方も参考にどうぞ)。
分散トレーシングとは
オブザーバビリティの三本柱は メトリクス 、 ログ 、 トレース です。
トレーシングはリクエストが複数サービスをまたいで処理される際の「軌跡」を追うものです。1つのリクエストに トレースID が発行され、サービスをまたぐごとに子スパンが作られます。Zipkinで可視化するとウォーターフォール形式で、どのサービスがどれだけ時間を使っているか一目で確認できます。
Spring Cloud SleuthからMicrometer Tracingへ
Spring Boot 2.xではトレーシングといえば Spring Cloud Sleuth でした。しかしSpring Boot 3.x(Spring Framework 6)への移行でSleuthは廃止となり、後継として Micrometer Tracing に統合されています。
Micrometer TracingはBraveとOpenTelemetryの2つのTracerをブリッジ経由で選択できる構造です。今回はBraveを使ってZipkinに送る構成にします。
サンプル構成
この記事では次の2サービスを使います。
- order-service (ポート8080)— RestClientで inventory-service を呼び出す
- inventory-service (ポート8081)— 在庫確認APIを提供する
ZipkinはDockerでローカルに立ち上げます。
依存関係を追加する
micrometer-tracing-bridge-brave と zipkin-reporter-brave の2つが必要です。
<!-- Micrometer Tracing(Braveブリッジ) -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<!-- Zipkinへのレポーター -->
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Spring Boot 2.x(Sleuth)との違いを比較しておきます。
<!-- Spring Boot 2.x(Sleuth)の場合 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
バージョン管理はSpring Boot BOMに任せておけば大丈夫です。
application.propertiesの設定
spring.application.name=order-service
# 開発時は全件サンプリング(本番は0.1〜0.3程度に下げる)
management.tracing.sampling.probability=1.0
# デフォルトはlocalhost:9411。Kubernetes環境などで変更する場合に明示する
management.zipkin.tracing.endpoint=http://localhost:9411/api/v2/spans
spring.application.name はZipkin UIでのサービス名になるので必ず設定しましょう。
ZipkinをDockerで起動する
docker run -d -p 9411:9411 openzipkin/zipkin
http://localhost:9411 でUIが開きます。docker-composeで管理したい場合はこちらです(Dockerfileの詳しい使い方は Dockerコンテナ化ガイド も参考にしてください)。
services:
zipkin:
image: openzipkin/zipkin
ports:
- "9411:9411"
order-serviceの実装
Spring Boot 3.2+では RestClient.Builder BeanがObservationRestClientCustomizerによって自動計装されるため、追加設定なしでトレースIDがHTTPヘッダーに付与されます。
@Configuration
public class RestClientConfig {
@Bean
public RestClient restClient(RestClient.Builder builder) {
return builder.baseUrl("http://localhost:8081").build();
}
}
@RestController
public class OrderController {
private final RestClient restClient;
public OrderController(RestClient restClient) {
this.restClient = restClient;
}
@GetMapping("/orders/{id}")
public String getOrder(@PathVariable String id) {
String inventory = restClient.get()
.uri("/inventory/{id}", id)
.retrieve()
.body(String.class);
return "Order: " + id + ", Inventory: " + inventory;
}
}
Brave使用時はB3ヘッダー(b3)でトレースIDが伝播します。OpenTelemetryブリッジを使う場合はW3C Trace Context形式(traceparent)になるため、別途設定が必要です。
inventory-serviceの実装
@RestController
public class InventoryController {
@GetMapping("/inventory/{id}")
public String getInventory(@PathVariable String id) throws InterruptedException {
Thread.sleep(200); // 遅延を入れてトレースを見やすくする
return "in-stock";
}
}
受け取ったB3ヘッダーからトレースIDが自動で引き継がれます。こちら側に特別な設定は不要です。
ログへのtraceId・spanId埋め込み
Micrometer TracingはMDCに traceId と spanId を自動セットします。logback-spring.xml のパターンに追加するだけです。
<pattern>%d{HH:mm:ss} [%X{traceId},%X{spanId}] %-5level %logger{36} - %msg%n</pattern>
ログ設定の詳細は Logback・SLF4Jの記事 を参照してください。ログとZipkinのトレースを traceId で横断調査できるようになります。
動作確認する
両サービスを起動してcurlでリクエストを送ります。
curl http://localhost:8080/orders/123
各サービスのログで traceId が一致していることを確認します。
# order-service
[abc123def,111aaa] INFO OrderController - ...
# inventory-service(同じtraceIdが伝播している)
[abc123def,222bbb] INFO InventoryController - ...
http://localhost:9411 を開き Run Query を押すとトレースが表示されます。
Zipkin UIの見方
- 絞り込み — サービス名(
order-serviceなど)や期間を指定して Find Traces ボタンで検索します。遅いリクエストを探すときはDuration(最小レイテンシ)でフィルタリングすると便利です - ウォーターフォール — トレースをクリックすると各スパンの開始・終了時刻が横棒グラフで表示されます。inventory-serviceの
Thread.sleep(200)による200msの遅延がはっきり見えるはずです - エラー確認 — エラーが発生したスパンは赤くハイライトされます。スパンをクリックすると例外メッセージやHTTPステータスコードなど詳細を確認できます
Kafkaなど非同期処理について
KafkaやSpring @Async などの非同期処理では、HTTPヘッダーによる自動伝播は使えません。spring-kafka を使う場合はMicrometer Tracingが自動計装を提供しています。詳しくは Spring Boot + Kafkaの記事 を参照してください。
本番環境での設定
sampling.probability=1.0 は全件トレースするため開発環境向けです。本番では 0.1〜0.3程度 に下げてオーバーヘッドを抑えましょう。
Kubernetes環境ではZipkinのエンドポイントをServiceドメイン名で指定します。
management.zipkin.tracing.endpoint=http://zipkin.monitoring.svc.cluster.local:9411/api/v2/spans
B3とW3C Trace Contextを使い分けたい場合は management.tracing.propagation.type=w3c で切り替えできます。他サービスがW3C形式を要求する場合や、OTelコレクター経由で転送する場合に有効です。
Kubernetesへのデプロイについては Kubernetesデプロイメントガイド も参考にしてください。
まとめ
Spring Boot 3.xへの移行でSleuthはなくなりましたが、Micrometer Tracingへの乗り換えは依存を2つ入れ替えるだけでほぼコード変更不要です。
メトリクス (Prometheus+Grafana)、 ログ (Logback)、 トレース (Micrometer+Zipkin)の三本柱が揃えば、マイクロサービスの問題調査が格段に楽になります。まずはローカルで試してみてください。