OTEP-0258: コンテキストとバゲッジの伝搬のための環境変数仕様

これは、プロセス間でのコンテキストとバゲッジの伝搬のためのキャリアとして、環境変数をOpenTelemetry仕様に追加する提案です。

目次

動機

コンテキストとバゲッジの伝搬のためのキャリアとして環境変数を使う仕様を定義する動機は、OpenTelemetry Specificationリポジトリで長らくオープンになっているissue #740に端を発しています。 このイシューは非常に長い間オープンのままになっており、その間に TRACEPARENT および TRACESTATE 環境変数を使う複数の実装がすでに存在しています。

issue #740は、次のような、ネットワーク通信を使って境界をまたぐ通信を行わないシステムにおけるいくつかのユースケースを挙げています。

  • ETL
  • バッチ処理
  • CI/CDシステム

環境変数キャリアを通じた任意のテキストマップ伝搬をOpenTelemetry Specificationに追加することで、上記に挙げたシステム内での分散トレーシングが可能になります。

この直近のニーズに対応するために、業界内、そしてOpenTelemetry内において、すでに相当量の先行技術が構築されていますが、現時点でOpenTelemetryはこの形式の伝搬のための仕様を定義していません。

特に、CI/CDワーキンググループ内でセマンティック規約を定義していく中で、CI/CDシステム内でネイティブなトレーシングを業界が採用できるようにするためには、この仕様の定義が必要になります。

設計

プロセス間でネットワーク通信が発生しないシステムにおいて、親プロセス、兄弟プロセス、子プロセスの間でコンテキストとバゲッジを伝搬するために、環境に注入されたキー・バリューのペアを任意のTextMapPropagatorが読み書きできるようにする仕様を用います。

コンテキストの例

プロセスのフォークという文脈で、次の図を考えてみます。

[!NOTE] この図は、プロセスのフォークの単なる例であり、単純化されたものです。 exec() のような、より高性能なプロセス生成方法も他に存在します。

環境変数によるコンテキストの伝搬

上の図では、親プロセスがフォークされて子プロセスを生成し、元の親プロセスから環境変数を継承しています。 ここで定義されている環境変数、すなわち TRACEPARENTTRACESTATEBAGGAGE は、子プロセスを親プロセスと結び付けられるように、子プロセスへコンテキストを伝搬するために使われます。 TRACEPARENT がなければ、トレーシングバックエンドは子プロセスのスパンを親スパンにつなげることができず、エンドツーエンドのトレースを形成できません。

[!NOTE] 以下では専らW3C仕様を環境変数に変換したものに従っていますが、本提案はW3Cに限定されるものではなく、むしろ、既知の環境変数名の候補集合を伴うテキストマップ伝搬の仕組みに焦点を当てています。 詳細はコア仕様の変更の節を参照してください。

上記の例がW3C仕様と一致することを踏まえ、以下はW3Cで定義されているヘッダーに対する環境変数の文脈上のマッピングです。

traceparent(小文字)ヘッダーはW3C Trace-Context仕様で定義されており、次の有効なフィールドを含みます。

  • version
  • trace-id
  • parent-id
  • trace-flags

これは、環境において次のように設定できます。

export TRACEPARENT=00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

[!NOTE] TRACEPARENTの値は、上記のフィールド値を符号なし整数値としてASCII文字列にシリアライズし、 - で区切って結合したものです。

tracestate(小文字)ヘッダーはW3C Trace-Stateで定義されており、キー・バリューのペア構造で任意の不透明な値を含めることができます。 その目的は、追加のベンダー固有のトレース情報を提供することです。

baggage(小文字)ヘッダーはW3C Baggageで定義されており、シグナル間でコンテキストを伝搬するためのキー・バリューのペアの集合です。 OpenTelemetryでは、バゲッジはBaggage APIを通じて伝搬されます。

OpenTofuプロトタイプ例における分散トレーシング

OpenTofu Controllerデプロイメントという次の実世界の例を考えてみます。

OpenTofuの実行

このモデルでは、OpenTofu Controllerがトレースの開始点であり、実際の trace_id を保持し、ルートスパンを生成します。 OpenTofu Controllerは、OpenTofuコマンドを実行するための独自の環境とプロセスを持つrunnerをデプロイします。 もしキャリアの仕組みなしにこれらのプロセスをトレーシングしようとすると、それらはすべて別々のトレースの中の無関係なルートスパンとして表示されてしまいます。 しかし、環境変数をキャリアとして活用することで、各スパンはルートスパンに結び付けられ、以下に示す実際のOpenTofuトレースの画像のように単一のトレースを形成できます。

OpenTofuトレース

さらに、init スパンは plan スパンと apply スパンにバゲッジを渡すことができます。 その一例が、モジュールのバージョンとリポジトリの情報です。 この情報は init プロセス中にのみ決定・把握されます。 それ以降のプロセスは、モジュールを名前でしか知りません。 BAGGAGE を使うことで、残りのプロセスもこの重要な情報を把握できるようになり、エラーを元のモジュールバージョンとソースコードに結び付けられるようになります。

環境変数をキャリアとする仕様を定義することは、通常のHTTPマイクロサービスアーキテクチャの外にあるシステムに対しても、より優れたオブザーバビリティを可能にするという点で、業界に広範な影響を与えるでしょう。

上記のプロトタイプ例は、OpenTofu Tracing RFCこのコメントで言及されているリソースに由来します。

コア仕様の変更

OpenTelemetry Specificationは、テキストマッププロパゲータを通じてコンテキスト伝搬を環境へと拡張するための定義で更新されるべきです。

この更新には次のものを含むべきです。

  • プロセス間でコンテキストを伝搬するために使用できる、 TRACEPARENTTRACESTATEBAGGAGE のような共通の環境変数の集合。 これらの環境変数名は、レガシーサポートの理由(B3の使用など)でオーバーライド可能であるべきですが、デフォルトの標準はW3C仕様に沿うべきです。
  • オペレーティングシステムの制約による、許可される環境変数名と値についての仕様。
  • 実装者が TextMapPropagator を通じて環境からコンテキストを注入・抽出する方法についての仕様。
  • 新しいプロセスを生成する前にプロセスが環境変数をどのように更新すべきかについての仕様。

コンテキストのキャリアとしての環境変数の仕様を定義することで、SDKやその他のツールが、標準的で観測可能な方法でコンテキストのゲッターとセッターを実装できるようになります。 したがって、現行のOpenTelemetryの各言語のメンテナーは、この仕様に準拠した言語固有の実装を開発する必要があります。

環境変数のためのテキストマッププロパゲータについては、すでにOpenTelemetry内に2つの実装が存在します。

  • Python SDK - この実装は、呼び出し元プロセスから呼び出し先プロセスへのコンテキスト伝搬のために、Pythonの環境ディクショナリをキャリアとして使用します。 このプルリクエストはマージされていないようです。
  • Swift SDK - この実装は、コンテキストを注入・抽出するために、W3Cプロパゲータと並んで TRACEPARENT および TRACESTATE 環境変数を使用します。

プログラミングの慣習、オペレーティングシステムの制約、先行技術、そして以下の情報から、コンテキストプロパゲータの仕様に沿った大文字の環境変数をキャリアとして活用することが推奨されます。

UNIXの制限

UNIXのシステムユーティリティは環境変数に大文字を使用し、小文字はアプリケーション用に予約されています。 大文字を使うことで、内部のアプリケーション変数との衝突を防げます。

シェルおよびユーティリティ(XCU)仕様のユーティリティで使用される環境変数名は、ポータブル文字集合で定義された文字のうち、大文字、数字、および _ (アンダースコア)のみで構成されます。 実装によっては他の文字も許可される場合がありますが、アプリケーションはそのような名前の存在を許容しなければなりません。 大文字と小文字はそれぞれ固有の識別性を保持し、区別なく統合されることはありません。 小文字を含む環境変数名の名前空間は、アプリケーション用に予約されています。 アプリケーションは、標準ユーティリティの動作を変更することなく、この名前空間から任意の環境変数を定義できます。

出典: The Open Group, The Single UNIX® Specification, Version 2, Environment Variables

Windowsの制限

Windowsは環境変数について大文字小文字を区別しません。 それにもかかわらず、OS間で大文字の名前を使用することが推奨されます。

一部の言語ではすでにこれを実施しています。 このCPythonのイシューでは、Pythonが環境変数を自動的に大文字化する仕組みについて議論されています。 このイシューはマージされ、その挙動を明確にするためにこのドキュメントが追加されました。

許可される文字

互換性を確保するために、環境変数の仕様は、キー・バリューのペアがRFC 7230に準拠した有効なHTTPヘッダーフィールドを構成するUS-ASCII文字のみで構成されなければならない(MUST)という、現行の TextMapPropagator の仕様に従うべき(SHOULD)です。

環境変数のキーは、IEEE Std 1003.1-2017に記載されているような、一般的に知られている環境変数と衝突すべきではありません(SHOULD NOT)。

重要な点として、Windowsでは環境変数名に = 文字を使うことが許可されていません。 詳細はMS Env Varsを参照してください。

また、環境変数がサポートできる文字数には32,767文字という上限があります。

トレードオフと緩和策

大文字小文字の区別

Windowsでは環境変数のキーが大文字小文字を区別しないため、自動的に計装されたコンテキスト伝搬用の変数が既存のアプリケーションの環境変数と衝突する可能性があります。 この挙動を明記し、各言語がこの問題をどのように緩和するかを文書化することが重要です。

セキュリティ

環境変数に機密情報を入れてはいけません。 環境変数の性質上、適切なアクセス権を持つ攻撃者は、本来知り得ないはずの情報を取得できてしまう可能性があります。 さらに、環境変数の完全性が侵害される可能性もあります。

先行技術と代替技術

opentelemetry-specification #740で言及されている TRACEPARENTTRACESTATE の環境変数の利用者は多数存在します。

さらに、環境変数をコンテキストキャリアとして使うPython SDKでのプロトタイプ実装もありました。

選ばれなかった代替案とその理由

キャリアとしてファイルを使用する

ファイルシステムに保存され、環境変数を通じて参照されるJSONファイルを使用すれば、Windowsにおける大文字小文字の区別の問題を回避する必要はなくなりますが、次のような多くの問題が生じます。

  1. 作成し、確実にクリーンアップする必要がある、帯域外のファイルが追加で必要になります。
  2. 場合によっては(たとえば sudo が使われる場合)、そのファイルのパーミッション管理が容易ではないことがあります。
  3. これは、現在環境変数を使用している重要な先行技術から逸脱することになります。

未解決の問題

著者は、現時点で未解決の問題を抱えていません。

将来の可能性

  1. ヘッダー、メタデータ、その他の手段を通じてトレースコンテキストを伝搬できるようなネットワークプロトコルで通信しないシステムにおける、分散トレーシングの実現。