分散トレーシングとSpan・Traceの基本概念
マイクロサービスアーキテクチャが普及した現在、一つのリクエストが複数のサービスを通過する処理フローは複雑になっています。ユーザーが「注文する」という単純な操作でも、内部では認証サービス、在庫確認、決済処理、配送手配など、多くのサービスが連携して動作しています。こうした複雑な処理の流れを追跡し、ボトルネックやエラーの原因を特定することが、現代のシステム運用では必須の課題です。分散トレーシングは、この課題を解決するための強力な技術です。
分散トレーシングが必要な理由
マイクロサービスアーキテクチャの課題
従来のモノリシック(一体型)アプリケーションでは、すべての処理が同一のプロセス内で実行されるため、動作を追跡するのは比較的単純でした。ログを見れば処理の流れが明確に記録されていました。
しかしマイクロサービスでは状況が大きく異なります。一つのリクエストが複数のサービスに分散し、非同期通信やメッセージキューを通じて処理されることもあります。この場合、個々のサービスのログを見ているだけでは、全体的な処理フローが見えません。
従来の監視手法では見えないもの
メトリクス(CPU、メモリ、リクエスト数)やログは、個々のサービスの健全性を把握するには有用です。しかし、それだけでは全体像が見えません。
例えば、注文処理の応答時間が長い場合、どのサービスが遅延しているのかを判断するのに時間がかかります。認証サービスのログ、在庫確認サービスのログ、決済サービスのログを個別に確認し、時刻を見比べながら原因を特定する必要があります。
分散トレーシングは、このような課題を解決します。一つのリクエストの流れを「トレース」として統一的に追跡し、各サービスでの処理時間を可視化することで、ボトルネックを迅速に特定できるようになります。
Trace・Span・Span IDの定義
分散トレーシングを理解するには、3つの核となる概念を押さえることが重要です。これらはトレーシングシステムの基本的な構成要素です。
Trace(トレース)
Traceは、一つのリクエストがシステム全体を通じて処理される過程全体を表します。ユーザーの操作が起点となり、複数のサービスを経由して完了するまでの一連の処理を、一つの「トレース」として捉えます。
例えば、オンラインショップで注文を確定するまでの処理全体が、一つのTraceです。認証→在庫確認→決済→配送手配という一連の処理が、すべて同一のTraceに属します。
Trace ID
Trace IDは、個々のTraceを一意に識別するための識別子です。UUID(universally unique identifier)のような形式で、システム内で唯一無二の値として生成されます。
Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736Trace IDは、リクエストが各サービスを通じて処理される際に、すべてのサービスに引き継がれます。これにより、複数のサービスに散らばるログやスパンを、同一のリクエストとして関連付けることができます。
Span(スパン)
Spanは、Trace内の個別の処理単位を表します。各Spanは、特定のサービスやコンポーネントで実行される処理を記録します。
一つのTraceは複数のSpanで構成されています。先ほどの注文処理の例では、以下のようなSpanが存在します。
- API Gatewayでのリクエスト受け取り(Span1)
- 認証サービスでの認証処理(Span2)
- 在庫サービスでの在庫確認(Span3)
- 決済サービスでの決済処理(Span4)
- 配送サービスでの配送手配(Span5)
各Spanは以下の情報を持ちます。
| 属性 | 説明 |
|---|---|
| Span ID | 個々のSpanを識別するID |
| Parent Span ID | 親Spanの識別子(ルートSpanの場合は空) |
| 操作名 | その処理が何をしているかを示す名前 |
| 開始時刻 | Spanが開始した時刻 |
| 終了時刻 | Spanが終了した時刻 |
| ステータス | 成功・失敗・エラー等の状態 |
Span ID と Parent Span ID
Span IDは、個別のSpanを識別するための識別子です。Trace ID同様、UUID形式の一意な値です。
Parent Span IDは、親子関係のあるSpan間で使用されます。あるサービスが別のサービスを呼び出す場合、呼び出し元のSpanをParent、呼び出され側をChild(子)として、この関係を記録します。
Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736
├─ Span ID: 2e41d8e3f2c75bea
Parent Span ID: (なし - ルートSpan)
操作名: order-checkout
│
├─ Span ID: 7a3c4f8b1d2e5f9a
│ Parent Span ID: 2e41d8e3f2c75bea
│ 操作名: authenticate-user
│
├─ Span ID: 5b2a9c4e8f1d7e3a
│ Parent Span ID: 2e41d8e3f2c75bea
│ 操作名: check-inventory
│
└─ Span ID: 9d1e6f3a4c8b2e5a
Parent Span ID: 2e41d8e3f2c75bea
操作名: process-paymentSpanの階層構造
分散トレーシングにおいて、Spanは単なる並列な関係ではなく、階層的な構造を持ちます。この構造を理解することは、トレースの可視化と分析に欠かせません。
ルートSpan
Trace内で最初に生成されるSpanをルートSpanと呼びます。ユーザーのリクエストがシステムに到着した際、API GatewayやLoad Balancerで生成される最上位のSpanです。ルートSpanはParent Span IDを持ちません。
チャイルドSpan
ルートSpanから呼び出された処理は、チャイルドSpan(子Span)として記録されます。その処理がさらに別のサービスを呼び出す場合、さらに下の階層のSpanが生成されます。
このような階層構造により、処理の流れが明確に可視化されます。どのSpanが親で、どれが子かを把握することで、処理フロー全体の依存関係が理解しやすくなります。
トレース構造の具体例
具体的な実装例を通じて、Traceの構造がどのように機能するかを見てみましょう。
注文処理の実装例
あるオンラインショップの注文システムを想定します。ユーザーが注文を確定してから、配送完了までのフローです。
時間軸での追跡
各Spanの開始時刻と終了時刻を記録することで、処理にかかった時間が明確になります。
| Span名 | 開始時刻 | 終了時刻 | 経過時間 | ステータス |
|---|---|---|---|---|
| receive-order(API Gateway) | 10:00:00.000 | 10:00:00.800 | 800ms | OK |
| verify-token(認証) | 10:00:00.010 | 10:00:00.150 | 140ms | OK |
| create-order(注文作成) | 10:00:00.160 | 10:00:00.500 | 340ms | OK |
| charge-card(決済) | 10:00:00.360 | 10:00:00.395 | 35ms | OK |
| reserve-stock(在庫予約) | 10:00:00.160 | 10:00:00.680 | 520ms | OK |
| update-inventory(DB更新) | 10:00:00.200 | 10:00:00.620 | 420ms | OK |
| schedule-delivery(配送手配) | 10:00:00.510 | 10:00:00.790 | 280ms | OK |
この表から、重要な関係性が見えてきます。receive-order(API Gateway)がルートスパンとなり、その子スパンとして各処理が実行されています。特に、reserve-stock(520ms)と update-inventory(420ms)の処理に時間がかかっていることが一目瞭然です。また、create-order内でcharge-cardが子スパンとして実行され、その後に schedule-delivery がルートスパンの子として実行されていることがわかります。このような時間軸の可視化により、ボトルネック特定が容易になります。
重要な属性・メタデータ
Span内には、処理の詳細を記録するための様々な属性が存在します。これらの属性により、トレースの分析がより有用になります。
操作名(Operation Name)
操作名は、そのSpanが何をしているかを示す人間が読みやすい名前です。order-checkout、verify-token、query-databaseといった具合に、処理内容を簡潔に表現します。
これはトレース分析時に、どの処理を見ているかを素早く理解するために重要です。
ステータス(Status)
Spanの実行結果を表します。
- OK:処理が正常に完了した
- ERROR:処理中にエラーが発生した
- UNSET:ステータスが設定されていない
エラーが発生した場合、そのSpanと紐付けたエラーメッセージや例外情報も記録されます。
イベント(Events)
Span実行中に発生した重要なマイルストーンを記録します。
例えば、決済処理のSpan内では以下のようなイベントが記録されるかもしれません。
request-sent: 決済APIへリクエストを送信response-received: APIからレスポンスを受信validation-failed: カード検証エラーretry-attempt: リトライ実行
カスタム属性(Custom Attributes)
ビジネスロジックに関連した情報を任意に記録できます。
例えば注文処理では、以下のような属性が有用です。
| 属性名 | 値 | 説明 |
|---|---|---|
user.id | user-12345 | ユーザー識別子 |
order.id | order-98765 | 注文識別子 |
order.total_amount | 15000 | 注文金額 |
payment.method | credit_card | 支払い方法 |
inventory.items_count | 5 | 注文アイテム数 |
region | jp-tokyo | 処理が実行されたリージョン |
カスタム属性を活用することで、トレースデータと業務データを結びつけ、より実用的な分析が可能になります。
まとめ
分散トレーシングの基本概念を理解することで、マイクロサービスアーキテクチャにおける可視性が劇的に向上します。Trace、Span、Span ID、Parent Span IDといった概念の相互関係を把握することで、複雑なシステムの動作を追跡できるようになります。
次のステップでは、これらの概念に基づいて、実際にOpenTelemetryを使用してトレーシングを実装する方法を学んでいきます。