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-bravezipkin-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に traceIdspanId を自動セットします。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)の三本柱が揃えば、マイクロサービスの問題調査が格段に楽になります。まずはローカルで試してみてください。