> Source: https://www.ymotongpoo.com/works/oteps/otep-0258/


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

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

## 目次 {#table-of-contents}

* [動機](#motivation)
* [設計](#design)
  * [コンテキストの例](#example-context)
    * [OpenTofuプロトタイプ例における分散トレーシング](#distributed-tracing-in-opentofu-prototype-example)
* [コア仕様の変更](#core-specification-changes)
  * [UNIXの制限](#unix-limitations)
  * [Windowsの制限](#windows-limitations)
  * [許可される文字](#allowed-characters)
* [トレードオフと緩和策](#trade-offs-and-mitigations)
  * [大文字小文字の区別](#case-sensitivity)
  * [セキュリティ](#security)
* [先行技術と代替技術](#prior-art-and-alternatives)
  * [選ばれなかった代替案とその理由](#alternatives-and-why-they-were-not-chosen)
* [未解決の問題](#open-questions)
* [将来の可能性](#future-possibilities)

## 動機 {#motivation}

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

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

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

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

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

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

[cicd-wg]: https://github.com/open-telemetry/community/blob/main/projects/ci-cd-phase-2.md
[issue-740]: https://github.com/open-telemetry/opentelemetry-specification/issues/740#issue-665588273
[tmp]: https://opentelemetry.io/docs/specs/otel/context/api-propagators/#textmap-propagator

## 設計 {#design}

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

### コンテキストの例 {#example-context}

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

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

![環境変数によるコンテキストの伝搬](./img/0258-env-context-parent-child-process.png)

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

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

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

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

* `version`
* `trace-id`
* `parent-id`
* `trace-flags`

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

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

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

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

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

[w3c-parent]: https://www.w3.org/TR/trace-context-2/#traceparent-header-field-values
[w3c-state]: https://www.w3.org/TR/trace-context-2/#tracestate-header
[w3c-bag]: https://www.w3.org/TR/baggage/#baggage-http-header-format

#### OpenTofuプロトタイプ例における分散トレーシング {#distributed-tracing-in-opentofu-prototype-example}

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

![OpenTofuの実行](./img/0258-env-context-opentofu-tracing.png)

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

![OpenTofuトレース](./img/0258-env-context-opentofu-trace.png)

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

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

[bag-api]: https://opentelemetry.io/docs/specs/otel/baggage/api/

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

[otcom]: https://github.com/opentofu/opentofu/pull/2028#issuecomment-2411588695
[otrfc]: https://github.com/opentofu/opentofu/pull/2028

## コア仕様の変更 {#core-specification-changes}

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

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

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

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

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

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

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

[python-env]: https://github.com/Div95/opentelemetry-python/tree/feature/env_propagator/propagator/opentelemetry-propagator-env
[swift-env]: https://github.com/open-telemetry/opentelemetry-swift-core/blob/c84cdc1760e20fc3a448c4e8aaae490f7d48ac67/Sources/OpenTelemetrySdk/Trace/Propagation/EnvironmentContextPropagator.swift

### UNIXの制限 {#unix-limitations}

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

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

出典: [The Open Group, The Single UNIX® Specification, Version 2, Environment Variables](https://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html)

### Windowsの制限 {#windows-limitations}

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

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

[cpython]: https://github.com/python/cpython/issues/101754
[cpython-doc]: https://docs.python.org/3/library/os.html#os.environ

### 許可される文字 {#allowed-characters}

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

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

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

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

[std1003]: https://pubs.opengroup.org/onlinepubs/9799919799/

[ms-env]: https://learn.microsoft.com/en-us/windows/win32/procthread/environment-variables

## トレードオフと緩和策 {#trade-offs-and-mitigations}

### 大文字小文字の区別 {#case-sensitivity}

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

### セキュリティ {#security}

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

## 先行技術と代替技術 {#prior-art-and-alternatives}

[opentelemetry-specification #740](https://github.com/open-telemetry/opentelemetry-specification/issues/740)で言及されている `TRACEPARENT` や `TRACESTATE` の環境変数の利用者は多数存在します。

* [Jenkins OpenTelemetry Plugin](https://github.com/jenkinsci/opentelemetry-plugin)
* [otel-cli generic wrapper](https://github.com/equinix-labs/otel-cli)
* [Maven OpenTelemetry Extension](https://github.com/open-telemetry/opentelemetry-java-contrib/tree/main/maven-extension)
* [Ansible OpenTelemetry Plugin](https://github.com/ansible-collections/community.general/pull/3091)
* [go-test-trace](https://github.com/rakyll/go-test-trace/commit/22493612be320e0a01c174efe9b2252924f6dda9)
* [Concourse CI](https://github.com/concourse/docs/pull/462)
* [BuildKite agent](https://github.com/buildkite/agent/pull/1548)
* [pytest](https://github.com/chrisguidry/pytest-opentelemetry/issues/20)
* [Kubernetes test-infra Prow](https://github.com/kubernetes/test-infra/issues/30010)
* [hotel-california](https://github.com/parsonsmatt/hotel-california/issues/3)

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

[python-sdkenv]: https://github.com/open-telemetry/opentelemetry-specification/issues/740#issuecomment-919657003

## 選ばれなかった代替案とその理由 {#alternatives-and-why-they-were-not-chosen}

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

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

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

## 未解決の問題 {#open-questions}

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

## 将来の可能性 {#future-possibilities}

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

