分散トレーシング
一つのリクエストが複数サービスを横断する経路を、トレースとして追跡する可観測性の手法。
概要
分散トレーシング(distributed tracing)は、1つのリクエストが複数のサービスを渡り歩く経路を、端から端まで1本の線としてつなぎ、どこで何秒かかったかを可視化する監視の手法です。ユーザーが「注文」ボタンを押した1回のリクエストが、内部では認証サービス→在庫サービス→決済サービス→通知サービスと次々に呼び出されている——このとき、リクエスト全体を「トレース(trace)」、各サービス内での個々の処理区間を「スパン(span)」と呼び、スパンを親子関係でつなぎ合わせて一連の処理を再構成します。
これはマイクロサービス化されたシステムの障害調査や性能分析に不可欠な道具です。処理が1つのプロセスに閉じていたモノリスなら、遅い箇所は1台の中を追えば分かりました。しかし処理が何十ものサービスに散らばると、「全体で3秒かかったが、どのサービスのどの処理が遅いのか」が途端に見えなくなります。分散トレーシングは、この見えなくなった1本の流れをサービス横断で復元し、遅延やエラーの発生源を特定できるようにします。
なぜ生まれたか
システムがモノリスだった頃、1回のリクエストの処理はすべて1つのプロセスの中で完結していました。遅ければプロファイラをかけ、エラーが出れば1本のログを追えば原因にたどり着けます。処理はコールスタックという一続きの線でつながっており、全体像は最初から手元にありました。
ところがマイクロサービス化が進むと、この線が断ち切られます。1回のリクエストは複数のサービスへの独立したネットワーク呼び出しに分解され、各サービスはそれぞれ別のプロセス・別のマシンで動き、自分に来た処理のログしか持ちません。「ユーザーからは3秒遅い。だが各サービスのログを個別に見ても、自分は速いと言い張る」——どのサービスも部分しか知らないため、全体の因果関係が誰にも分からなくなります。この「サービスをまたいで1リクエストを追えない」問題を解くために生まれたのが分散トレーシングです。Googleが自社の大規模システム向けに作った「Dapper」の論文(2010年)が原型となり、その考え方がJaegerやZipkin、そして標準規格のOpenTelemetryへと受け継がれました。
詳細
トレースIDを伝播させる
分散トレーシングの核心は驚くほど単純で、「リクエストの入口で一意なトレースID(trace ID)を発行し、以降すべてのサービス呼び出しにそのIDを添えて運ぶ」ことに尽きます。最初のサービスがトレースIDを生成し、次のサービスを呼ぶときにHTTPヘッダなどに埋めて渡します。受け取った側もさらに次へ同じIDを渡す——こうしてリクエストが通った全サービスに同じトレースIDが行き渡ります。各サービスは自分の処理区間を「スパン」として記録し、そこに「このトレースID」「親スパンは誰か」「開始・終了時刻」を刻んで中央のバックエンドへ送ります。あとはトレースIDをキーに全スパンを集めれば、1本のトレースが復元できます。
このように各サービスが記録したスパンは、親子関係と時刻を持っているため、「Aの中でOを呼び、Oの中でIとPを順に呼んだ」という木構造と、それぞれの所要時間が復元されます。
トレース・スパン・コンテキスト
構成要素を整理すると、トレースは「1リクエスト全体」を表す最上位の単位、スパンは「その中の1つの作業単位(あるサービスでのHTTP処理、DBクエリ、外部API呼び出しなど)」を表す区間です。スパンには開始・終了時刻のほか、HTTPステータスやエラー情報、任意の属性(タグ)を付けられ、これが後の絞り込み検索に効きます。そしてサービス間でトレースIDや親スパンIDを運ぶ入れ物が「トレースコンテキスト」で、これを標準化したのがW3Cの traceparent ヘッダです。ヘッダ形式が統一されたおかげで、異なる言語・異なるベンダーのサービスが混在していても、1本のトレースをつなげられるようになりました。
集めたトレースは、可視化ツール上で横棒を階層的に並べた「ウォーターフォール(ガントチャート)」として表示されます。どのスパンが長く伸びているかが一目で分かり、「全体3秒のうち2.8秒は在庫サービスのDBクエリが占めていた」といったボトルネックが視覚的に判明します。エラーが出たスパンは色で強調され、失敗が連鎖のどこで始まったかもすぐに追えます。
実装・運用と落とし穴
実装は、OpenTelemetry(OTel)というベンダー中立の標準が事実上の共通言語になっています。各言語のSDKを組み込み、フレームワークの計装(instrumentation)ライブラリを入れれば、HTTPやDBの呼び出しは自動でスパン化され、送信先(Jaeger、Zipkin、各種クラウドの可観測性サービスなど)は設定で差し替えられます。トレース単体では足りない情報は、同じトレースIDをログやメトリクスにも埋め込むことで相互に紐づけられ、「トレースで遅いスパンを見つけ、そのスパンのログへ飛ぶ」という調査動線が作れます。
運用上の最大の悩みはコストです。全リクエストの全スパンを保存すると、トレースのデータ量は本番トラフィックに比例して膨大になります。そこで「サンプリング」で一部だけを保存するのが定石で、入口で確率的に間引くヘッドサンプリングと、エラーや遅延を含むトレースだけを後から選んで残すテールサンプリングがあります。もう一つの典型的な落とし穴は「伝播の切れ目」です。どこか1つのサービスがトレースコンテキストを次へ渡し忘れると、そこでトレースが分断され、以降が別のトレースに見えてしまいます。非同期処理やメッセージキューをまたぐ場面では、コンテキストをメッセージに載せて手当てする必要があり、ここが計装漏れの起きやすい箇所です。分散トレーシングは「入れれば見える」ものではなく、伝播をどこまで途切れさせずに通せるかが、そのまま可視化の質になります。