Go のゼロコード自動計装の選び方

Go アプリケーション向けに、OpenTelemetry eBPF Instrumentation(OBI)と Go コンパイル時計装(otelc)のどちらを選ぶかのガイド。

Go アプリケーションに対して、OpenTelemetry はソースコードを変更せずにアプリケーションを計装するための2つの異なる「ゼロコード」アプローチ、ランタイムアタッチとビルド時ウィービングを提供しています。

このガイドでは、OpenTelemetry eBPF Instrumentation(OBI)とOpenTelemetry Go コンパイル時計装(otelc)を比較し、環境に適したツールの選択を支援します。

Note: Go の自動計装 SDK については、Auto SDKを参照してください。 このガイドは eBPF とコンパイル時のゼロコードアプローチの比較に焦点を当てています。

これは意思決定ガイドであり、ランキングではありません。 どちらのプロジェクトも OpenTelemetry の公式コンポーネントです。 多くの構成では、それぞれ異なる問題を解決し、組み合わせて使うこともできます。

2つのメンタルモデル

ゼロコード計装を評価する際、2つのアプローチの基本的なアーキテクチャの違いを理解することが役立ちます。

  • ランタイム(eBPF): 計装は、アプリケーションとは別のプロセスとして(多くの場合ホストノード上で)並行して実行されます。 アプリケーションを外部から、具体的にはアプリケーション層、カーネル層、ネットワーク層で観察します。 トレースコンテキスト伝搬のためにアプリケーションのメモリ空間に送信ヘッダーを注入する場合がありますが、アプリケーションのビルドプロセスの変更は必要ありません。

  • コンパイル時(otelc): 計装は、コンパイルプロセス中にアプリケーションのソースに注入されます。 生成されたバイナリにはテレメトリーロジックがネイティブに含まれているため、外部のランタイムエージェントは不要です。

OBI

OpenTelemetry eBPF Instrumentation(OBI)は、eBPF(eBPF とは?)を使用して、アプリケーションの実行ファイルと OS のネットワーク層を自動的に検査します。

  • フックの仕組み: Linux カーネルとアプリケーションに eBPF プローブをデプロイし、システムコールやネットワークパケットの発生時にトレーススパンとメトリクスをキャプチャします。
  • 言語カバレッジ: 広範でポリグロットです。 Java、.NET、Go、Python、Ruby、Node.js、C、C++、Rust などをサポートしています。
  • 運用要件: Linux 環境(カーネル 5.8 以上または特定のバックポート)、BTF サポート、および昇格された特権(root または特定の Linux ケーパビリティ)が必要です。
  • 生成されるテレメトリー: トレース(HTTP/S、gRPC、データベースクエリの受信スパンと送信スパン)、RED(Rate、Errors、Duration)メトリクス、ネットワークフロー、ログエンリッチメントのキャプチャに優れています。 プロセスに出入りするものをキャプチャしますが、アプリケーションのフレームワークやライブラリに固有の内部スパンは、アプリケーションコードを変更するか、設定ファイルでコードセクションを手動で定義することで、ユーザーが手動で指定する必要があります。

otelc

OpenTelemetry Go コンパイル時計装(otelc)は、Go ツールチェインの -toolexec メカニズムを使用して、コンパイルプロセス中に OpenTelemetry の計装をアプリケーションバイナリに直接自動的に注入します。

  • フックの仕組み: パッケージのコンパイルをインターセプトし、関数を計装ルールとマッチングし、コンパイル済みバイナリに直接 OpenTelemetry フックを注入します。
  • 言語カバレッジ: Go のみ。
  • 運用要件: Go のビルドパイプラインへのアクセスが必要です。 計装がバイナリに組み込まれるため、Go バイナリが実行できる場所であればどこでも動作し、実行時に特別な OS 特権やカーネル機能は不要です。
  • 生成されるテレメトリー: 高忠実度のプロセス内の深いスパンを提供します。 内部関数呼び出しのトレース、アプリケーション固有のコンテキストのキャプチャ、サードパーティの Go 依存関係の計装が可能です。

トレードオフ

機能OBI(eBPF)otelc(コンパイル時)
デプロイモデル別途デプロイ(例: Kubernetes DaemonSet、サイドカー、またはホストエージェント)。アプリケーションのビルドパイプラインへの変更は不要。go build 時にアプリケーションバイナリに組み込み。ビルドコマンドのラッピングが必要。
ランタイム要件特定の Linux カーネルバージョン、BTF、および昇格された特権(root/ケーパビリティ)が必要。特別なランタイム特権、カーネル機能、OS 要件は不要。
シグナルの忠実度と深度ネットワーク境界とプロトコルインタラクションに優れる。内部関数呼び出しやカスタムビジネスロジックは容易に確認できない。プロセス内の深い可視性。内部関数、カスタムロジック、コードパスを計装可能。
サードパーティライブラリ依存関係が行うネットワーク呼び出しとデータベースクエリを確認可能。Go モジュール依存関係の内部コード実行を計装可能。
ライフサイクル管理アプリケーションの再起動なしに、動的にアタッチ、デタッチ、アップグレード、ダウングレードが可能。バージョンの変更や計装の削除にはアプリケーションのリビルド、再デプロイ、再起動が必要。

どちらを使うべきか

OBI を選ぶ場合

  • デプロイの容易さと即座のフィードバックを重視する場合: OBI エージェントをデプロイすることで、ソフトウェア開発ライフサイクル(SDLC)やビルドプロセスを変更せずにテレメトリーを収集できます。
  • 複数言語のフリートがある場合: Go、Java、Python、.NET などで動作する、統一された単一の自動計装ツールが必要です。
  • ビルドパイプラインへのアクセスがない場合: ビルドプロセスを所有していない、事前コンパイル済みのベンダーバイナリを使用している、またはソースコードにアクセスできません。
  • 境界のオブザーバビリティを重視する場合: 主な目標が、インフラストラクチャ全体でのサービス間通信、RED メトリクス、データベース呼び出しの追跡です。
  • Linux で実行している場合: Linux 環境(Kubernetes の内外を問わず)で運用しており、プロセスを観察するために必要な特権を持つエージェントをデプロイできます。 OBI は実行ファイル名、オープンポート、プロセス PID によるセレクタに加え、コンテナ化されたシナリオ(Docker/Kubernetes)ではコンテナ、Pod、Deployment、Namespace、メタデータラベルによるセレクタを提供しており、さまざまなデプロイシナリオに柔軟に対応できます。

otelc を選ぶ場合

  • Go のビルドパイプラインを所有している場合: Go サービスのコンパイルとデプロイを完全にコントロールできます。
  • 計装の保証が必要な場合: ビルドフェーズでテストを記述し、デプロイ前に収集されるテレメトリーが正確であることを確認できます。
  • 最大限のスパン忠実度が必要な場合: ビジネスロジックと内部アプリケーション状態のプロセス内の深いトレースが必要です。
  • サードパーティの内部をトレースしたい場合: インポートしているが所有していないサードパーティ Go モジュールの実行の可視性が必要です。
  • ランタイムが制限されている場合: root 特権、eBPF ケーパビリティ、または特定のカーネルバージョンを制限する環境(特定のサーバーレス環境や高度にロックダウンされたコンテナ環境など)で運用しています。
  • プラットフォームに依存しないソリューションが必要な場合: アプリケーションが Windows、Linux、macOS、または Go ランタイムがサポートする特殊なアーキテクチャで実行される必要があります。

両方を組み合わせて使う

これらのツールはスタックの異なるレイヤーで動作するため、相互に補完的であり、同じ環境で併用できます。

  • otelc を使用して、Go のビジネスロジック、内部ライブラリ呼び出し、カスタムスパンに対する深く高忠実度のトレースを生成します。
  • OBI を使用して、インフラストラクチャレベルのネットワークオブザーバビリティ、カーネルレベルの TCP メトリクス、および環境内の非 Go コンポーネントやサイドカーへのカバレッジを提供します。

両方の Special Interest Group(SIG)は、将来これら2つのツール間の相互運用性を向上させる予定です。