---
title: 分散トレーシング
url: https://doc.liz6.com/ja/distributed-systems/08-observability/01-distributed-tracing
locale: ja
area: distributed-systems
tags:
- distributed-systems
- observability
date: 2026-06-30
modified: 2026-07-16
description: リクエストが10以上のマイクロサービスにまたがる場合、どこに時間がかかっているかをどうやって特定するか？分散トレーシングは、各リクエストに trace ID を割り当て、サービス間の呼び出し関係を context propagation を通じて span context（trace ID + parent span ID + サンプリングフラグ）として伝播させ、各ノードに散在する断片を結合して完全な呼び出しチェーンを構築します。サンプリング戦略はコスト管理の鍵となります：全量サンプリングは高コストすぎ、固定比率サンプリングはロングテールのエラーを見逃す可能性があり、tail-based サンプリングは処理完了後にどの trace を保持するかを決定します。
---

# 分散トレーシング

> リクエストが10以上のマイクロサービスにまたがる場合、どこに時間がかかっているかをどうやって特定するか？分散トレーシングは、各リクエストに trace ID を割り当て、サービス間の呼び出し関係を context propagation を通じて span context（trace ID + parent span ID + サンプリングフラグ）として伝播させ、各ノードに散在する断片を結合して完全な呼び出しチェーンを構築します。サンプリング戦略はコスト管理の鍵となります：全量サンプリングは高コストすぎ、固定比率サンプリングはロングテールのエラーを見逃す可能性があり、tail-based サンプリングは処理完了後にどの trace を保持するかを決定します。

## Dapper (Google 2010) → OpenTelemetry

Google の Dapper 論文はオープンソースエコシステムに影響を与えました：Zipkin (Twitter) → Jaeger (Uber) → OpenTracing + OpenCensus → **OpenTelemetry** (CNCF、現在の標準)。

## Trace と Span

<svg viewBox="0 0 720 406" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="Trace と Span の構造: 1つの Trace は複数の Span で構成され、各 Span はいくつかの主要フィールドを記録します">
  <rect width="720" height="406" fill="#ffffff"/>
  <text x="360" y="26" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">Trace と Span の構造: 1つの Trace は複数の Span で構成されます</text>
  <rect x="50" y="38" width="620" height="38" rx="8" fill="#4f46e5"/>
  <text x="64" y="61" font-size="12.5" fill="#ffffff"><tspan font-weight="700">Trace</tspan> — 1つのエンドツーエンドリクエストにおけるすべての span の集合</text>
  <line x1="70" y1="76" x2="70" y2="101" stroke="#94a3b8" stroke-width="1.6"/>
  <line x1="70" y1="101" x2="90" y2="101" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="90" y="84" width="580" height="34" rx="8" fill="#0d9488"/>
  <text x="104" y="105" font-size="12.5" fill="#ffffff"><tspan font-weight="700">Span</tspan> — 1つの RPC 呼び出しの記録で、以下の 7 つのフィールドを含みます</text>
  <line x1="110" y1="118" x2="110" y2="327" stroke="#94a3b8" stroke-width="1.6"/>
  <line x1="110" y1="147" x2="130" y2="147" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="130" y="134" width="540" height="26" rx="5" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="142" y="151" font-size="12" font-weight="700" fill="#115e59">trace_id</text>
  <text x="260" y="151" font-size="11" fill="#475569">trace の一意の ID</text>
  <line x1="110" y1="177" x2="130" y2="177" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="130" y="164" width="540" height="26" rx="5" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="142" y="181" font-size="12" font-weight="700" fill="#115e59">span_id</text>
  <text x="260" y="181" font-size="11" fill="#475569">この span の一意の ID</text>
  <line x1="110" y1="207" x2="130" y2="207" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="130" y="194" width="540" height="26" rx="5" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="142" y="211" font-size="12" font-weight="700" fill="#115e59">parent_id</text>
  <text x="260" y="211" font-size="11" fill="#475569">呼び出し元の span の ID（root span には parent がない）</text>
  <line x1="110" y1="237" x2="130" y2="237" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="130" y="224" width="540" height="26" rx="5" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="142" y="241" font-size="12" font-weight="700" fill="#115e59">operation</text>
  <text x="260" y="241" font-size="11" fill="#475569">"GET /api/users" / "SELECT * FROM users"</text>
  <line x1="110" y1="267" x2="130" y2="267" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="130" y="254" width="540" height="26" rx="5" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="142" y="271" font-size="12" font-weight="700" fill="#115e59">start/end</text>
  <text x="260" y="271" font-size="11" fill="#475569">タイムスタンプ</text>
  <line x1="110" y1="297" x2="130" y2="297" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="130" y="284" width="540" height="26" rx="5" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="142" y="301" font-size="12" font-weight="700" fill="#115e59">status</text>
  <text x="260" y="301" font-size="11" fill="#475569">OK / ERROR（エラー詳細を含む）</text>
  <line x1="110" y1="327" x2="130" y2="327" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="130" y="314" width="540" height="26" rx="5" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="142" y="331" font-size="12" font-weight="700" fill="#115e59">attributes</text>
  <text x="260" y="331" font-size="11" fill="#475569">カスタムタグ（http.status_code, db.statement, ...）</text>
  <rect x="50" y="352" width="620" height="40" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="64" y="368" font-size="12" fill="#3730a3">重要な構造: parent_id により、各サービスノードに散在する span が 1 つの木構造に結合される——</text>
  <text x="64" y="384" font-size="12" fill="#3730a3">root span には parent がない。子 span はすべて呼び出し元の span_id を記録する。</text>
</svg>

<svg viewBox="0 0 720 370" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="Span の呼び出し階層ツリー: /api/order リクエストの親子構造">
  <rect width="720" height="370" fill="#ffffff"/>
  <text x="360" y="26" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">Span の呼び出し階層ツリー: /api/order リクエストの親子構造</text>
  <rect x="50" y="40" width="620" height="30" rx="6" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="64" y="60" font-size="12.5" font-weight="700" fill="#3730a3">Trace: ユーザーリクエスト → /api/order</text>
  <line x1="70" y1="70" x2="70" y2="102" stroke="#94a3b8" stroke-width="1.6"/>
  <line x1="70" y1="102" x2="90" y2="102" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="90" y="82" width="560" height="40" rx="8" fill="#4f46e5"/>
  <text x="104" y="100" font-size="12.5" font-weight="700" fill="#ffffff">Span A · GET /api/order</text>
  <text x="104" y="115" font-size="10.5" fill="#e0e7ff">root span（parent なし）</text>
  <line x1="110" y1="122" x2="110" y2="212" stroke="#94a3b8" stroke-width="1.6"/>
  <line x1="110" y1="158" x2="150" y2="158" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="150" y="138" width="500" height="40" rx="8" fill="#e0e7ff" stroke="#c7d2fe"/>
  <text x="164" y="156" font-size="12" font-weight="700" fill="#3730a3">Span B · auth-service.ValidateToken</text>
  <text x="164" y="171" font-size="10.5" fill="#4f46e5">ユーザートークンの検証</text>
  <line x1="110" y1="212" x2="150" y2="212" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="150" y="192" width="500" height="40" rx="8" fill="#e0e7ff" stroke="#c7d2fe"/>
  <text x="164" y="210" font-size="12" font-weight="700" fill="#3730a3">Span C · order-service.CreateOrder</text>
  <text x="164" y="225" font-size="10.5" fill="#4f46e5">注文の作成</text>
  <line x1="170" y1="232" x2="170" y2="268" stroke="#94a3b8" stroke-width="1.6"/>
  <line x1="170" y1="268" x2="210" y2="268" stroke="#94a3b8" stroke-width="1.6"/>
  <rect x="210" y="248" width="440" height="40" rx="8" fill="#0d9488"/>
  <text x="224" y="266" font-size="12" font-weight="700" fill="#ffffff">Span D · postgres: INSERT INTO orders</text>
  <text x="224" y="281" font-size="10.5" fill="#ccfbf1">実際のデータベース書き込み操作（リーフ span）</text>
  <rect x="50" y="308" width="620" height="44" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="64" y="328" font-size="12" fill="#115e59">parent_id により、auth-service / order-service / postgres の span が 1 つの木構造に結合される:</text>
  <text x="64" y="344" font-size="12" fill="#115e59">root span (A) には parent がない。子 span は呼び出し元の span_id を記録し、階層が決定される。</text>
</svg>

## Context Propagation

サービス間での trace の伝播が重要である——各サービスは「自分がどの trace のどの span であるか」を把握していなければならない:

```
HTTP: traceparent: 00-<trace_id>-<span_id>-<trace_flags>
W3C Trace Context (標準): traceparent ヘッダー
gRPC: メタデータ (grpc-trace-bin)
Kafka: メッセージヘッダー
```

`trace_flags`: bit 0 = SAMPLED（サンプリング対象かどうか）、他の bits は予約済み。

内部実装: トレーサーは下流サービスへの呼び出し時に新しい span（呼び出し元 span の子）を作成し、trace_id + new_span_id を outbound ヘッダーに格納する。下流のミドルウェア/ライブラリ（HTTP クライアント、gRPC スタブ、DB ドライバー）は、レスポンス受信時に trace context を抽出し、自動的に子 span を作成する。

## サンプリング

全量収集 → オーバーヘッドが許容できない（各 span につき約 1μs のオーバーヘッド + ネットワーク/ストレージコスト）。サンプリング戦略が必要:

- **Head-based (確率的)**: trace 開始時にサンプリングするかどうかを決定する（ランダムで 1/100 または 1/1000）。単純だが、エラーを含む trace を見逃す可能性がある。
- **Tail-based (インテリジェント)**: すべての span をキャッシュ（ローカルバッファ）し、trace 完了後に結果（ERROR? SLO を超えるレイテンシ?）に基づいて保持するかどうかを決定する。エラーを見逃さないが、メモリオーバーヘッドが大きい。OpenTelemetry の `tailsampling` プロセッサーがこの戦略を実装している。

## Jaeger 展開 (参考)

```bash
# Agent (ホストごとに 1 つ、UDP を介してアプリから span を受信)
# Collector (セントラル、エージェントから受信 → ストレージに書き込み)
# Storage (Cassandra/Elasticsearch)
# Query (UI + API)
```

## 参考

- **論文**: "Dapper, a Large-Scale Distributed Systems Tracing Infrastructure" (Google, 2010)
- **OpenTelemetry**: opentelemetry.io/docs/specs/otel/trace
- **Jaeger**: jaegertracing.io

*Keywords: 分散トレーシング, Dapper, OpenTelemetry, Span, trace context propagation, W3C Trace Context, サンプリング*
