古い構成図を捨てる? OpenTelemetryのトレースから実行時サービスマップを復元する仕組み

Java / Spring

そのアーキテクチャ図は、今も本当に正しいか

新しいプロジェクトに参加したとき、まず確認したくなるのがアーキテクチャ図です。しかし、マイクロサービスの構成は短期間で変化します。サービスの追加、データベースの変更、メッセージキューの導入が続けば、設計書と実際の通信経路が食い違うのは珍しくありません。

JetBrainsのOpenTelemetryプラグインは、この問題に対して実行時に収集したトレースからサービスマップを自動生成するというアプローチを取ります。ソースコードや設計書を推測するのではなく、アプリケーションが実際に動作した際の通信をもとに、サービス間の依存関係を可視化する機能です。

静的な構成図ではなく、実行時の事実を見る

従来、システム構成を把握する方法には、主に次のようなものがありました。

  • 設計書や手作業で作成したアーキテクチャ図を読む
  • ソースコードや設定ファイルを静的解析する
  • ログやメトリクスから通信の痕跡を探す

これらはそれぞれ有用ですが、実際の実行経路を完全に表現できるとは限りません。設計書は更新されないことがあり、静的解析では実行時設定や外部サービスとの接続を把握しにくく、ログだけでは呼び出し関係を追跡しづらい場合があります。

トレースは、1つのリクエストがシステム内をどのように移動したかを表します。トレースを構成するスパンには、処理単位ごとの情報が含まれます。OpenTelemetryでは、HTTPクライアントやHTTPサーバー、データベース、メッセージングなどの操作を表す標準的なセマンティック規約が定義されています。

この標準化されたスパンを利用すれば、特定のフレームワークやベンダーのAPIに依存せず、実際の通信経路から構成を組み立てられます。

JetBrains IDE内での処理の流れ

プラグインがサービスマップを作成する流れは、概ね次のようになります。

  1. OpenTelemetryプラグインを有効にしてIDEを起動する
  2. プラグインがローカルの軽量なOpenTelemetryバックエンドを起動する
  3. IDEからアプリケーションを実行する
  4. プラグインがOpenTelemetryの送信先を示す環境変数をアプリケーションへ渡す
  5. アプリケーションが生成したスパンをローカルバックエンドへ送信する
  6. バックエンドが受信したスパンを非同期に処理する
  7. サービスや通信関係を内部モデルへ追加・更新する
  8. Service Mapタブで最新の構造モデルを図として表示する

重要なのは、最初に完全なトレースを集めてから一括解析するのではなく、スパンが到着するたびにサービスマップを更新する点です。アプリケーションを動かし続けることで、観測できた通信が徐々にマップへ反映されます。

なぜスパンの処理は難しいのか

一見すると、スパンから呼び出し元と呼び出し先を読み取るだけに見えます。しかし、実際のテレメトリーデータは、静的な図のように整然とは届きません。

到着順序は保証されない

スパンは独立して送信されるため、親スパンが子スパンより先に処理されるとは限りません。子側のスパンが先に届き、後から親側のスパンが届くこともあります。

トレースの終了を即座には判断できない

ある時点で受信したスパンを見て、トレースが完全に終了したと断定することは困難です。ネットワーク遅延などによって、後から別のスパンが到着する可能性があるためです。

スパンの種類を属性から推定する必要がある

OpenTelemetryのスパンは、すべてが厳密に型付けされたオブジェクトとして届くわけではありません。キーと値の形式で保持された属性を読み取り、HTTP通信なのか、データベースアクセスなのか、メッセージングなのかを判定します。

このため、サービスマップの生成は単純なログ集計ではなく、順不同のストリームを継続的に解釈する処理になります。

HTTP通信の関係をどう復元するか

HTTP通信では、通常、呼び出し側と受け側の両方にスパンが生成されます。

  • 呼び出し側のサービスがHTTPクライアントスパンを生成する
  • 受け側のサービスがHTTPサーバースパンを生成する
  • リクエストに含まれるトレースコンテキストによって、両者の親子関係が表現される

バックエンドは、HTTPクライアントスパンが現れると、その子にあたるサーバースパンを探します。反対に、サーバースパンが先に到着した場合は、親となるクライアントスパンを探します。

対応するスパンがすでに見つかっていれば、サービス間のエッジを作成または更新します。まだ相手が届いていなければ、スパンを一時的に保持し、後から到着する情報との対応付けを待ちます。

これにより、スパンの到着順が前後しても、サービス間のHTTP通信を段階的に復元できます。

データベースとメッセージキューも対象になる

サービスマップは、HTTPの呼び出し関係だけを対象にしているわけではありません。ニュースで紹介されている対象には、次のような依存関係が含まれます。

対象 マップ上での扱い
HTTP通信 クライアント側とサーバー側のスパンを関連付け、サービス間の接続を表現する
データベース データベースアクセスを表すクライアントスパンの属性から依存先を推定する
メッセージキュー プロデューサーとコンシューマーの関係を、親子関係やスパンリンクなどから関連付ける

データベースアクセスは、単一のクライアントスパンとして表現されることが多いため、属性からデータベースノードを推定します。

メッセージングは、利用するシステムや計装方法によって表現が異なります。プロデューサーとコンシューマーが親子関係で結び付く場合もあれば、スパンリンクで関連付けられる場合もあります。サービスマップ側では、こうした違いを考慮しながら依存関係を復元します。

実務で役立つ場面

この機能の価値は、きれいな図を自動生成することだけではありません。開発中のシステムに対して、想定外の通信を早期に発見できる点が重要です。

1. 新しいコードベースの把握

新しいチームやプロジェクトに参加した直後は、設計書を読むだけでは現在の通信経路を把握できないことがあります。アプリケーションを実行し、代表的な操作を行えば、実際に呼び出されたサービスや外部依存先を視覚的に確認できます。

2. 想定外の依存関係の発見

1回のAPI呼び出しに対して、想定していなかった別サービスへの通信やデータベースアクセスが発生していることがあります。サービスマップは、こうした見落としをリリース前に発見する手がかりになります。

3. メッセージング経路の確認

非同期処理は、同期的なHTTP通信に比べて追跡しにくい傾向があります。プロデューサー、メッセージキュー、コンシューマーの関係を確認できれば、イベント駆動の処理経路を理解しやすくなります。

4. リファクタリングの影響確認

サービス分割や通信方式の変更を行った際に、実際の呼び出し関係が意図どおり変化したかを確認できます。コードレビューや設計レビューを補完する観測手段として利用できます。

導入時に理解しておきたい限界

実行時のトレースから作られるサービスマップは、常にシステム全体を完全に表す図ではありません。あくまで、収集できたテレメトリーと、その時点で実行された経路から復元したマップです。

特に、次の点には注意が必要です。

  • 実行されていない機能や通信はマップに現れない
  • アプリケーションやライブラリが適切なOpenTelemetryスパンを出力している必要がある
  • サービス間でトレースコンテキストが正しく伝播されないと、関係を結び付けにくい
  • スパン属性が不足している場合、通信種別や依存先を正確に判定できない可能性がある
  • 開発環境と本番環境で設定や経路が異なる場合、表示される構成も異なる

したがって、サービスマップは設計書の完全な代替というより、設計書やコード、実行ログと組み合わせて使う観測結果と考えるのが適切です。

また、マップに表示される通信量や関係は、どの操作を実行したかに左右されます。広い範囲の構成を確認したい場合は、主要なAPI、バッチ、イベント処理など、複数のユースケースを実際に動かす必要があります。

言語やIDEをまたいで使える理由

この機能は、特定の言語やフレームワーク専用のAPIを解析するのではなく、OpenTelemetryのスパンとセマンティック規約を利用します。そのため、期待される形式でスパンを出力し、コンテキストを正しく伝播できるアプリケーションであれば、JVM、.NET、Python、Goなど複数の環境で同じ考え方を適用できます。

紹介されている対応先には、IntelliJ IDEA、GoLand、PyCharm、WebStorm、RiderなどのJetBrains IDEが含まれます。チーム内で異なる言語を使っていても、OpenTelemetryを共通の観測形式として扱える点は、マイクロサービス環境における大きな利点です。

まとめ:構成図を「作る」より、実行時に「確かめる」

マイクロサービスの構成は、設計時点で固定されるものではありません。デプロイ設定、機能追加、外部サービスの変更によって、実際の依存関係は継続的に変化します。

OpenTelemetryプラグインのサービスマップは、こうした変化を実行時のトレースから捉え、HTTP、データベース、メッセージキューを含む通信関係を段階的に可視化します。順不同で届くスパンや、完全な終了を判断できないトレースをストリーム処理することで、動作中のシステムに追随する仕組みを実現しています。

利用時には、マップが「テストや実行で観測された範囲」を示すことを理解しておく必要があります。その前提に立てば、設計書の更新漏れを探す、想定外の依存関係を見つける、リファクタリング後の通信経路を確認するといった、日常の開発・運用に実用的な情報を提供してくれるでしょう。

出典: JetBrains Blog

タイトルとURLをコピーしました