OTEP-0225: OpenTelemetryの設定

本ドキュメントでは、ファイルとして表現でき、公開されたスキーマを通じて検証できる設定モデルという形式で、新しい設定インターフェースを提案します。

動機

OpenTelemetryは、エンドユーザーが望む動作モードに基づいてさまざまな方法で動作できるコードを規定しています。 そのため、ユーザーがこの情報を伝えられるように、設定インターフェースを提供する必要があります。 現在、OpenTelemetryはこのインターフェースを、SDKが公開するAPIと環境変数という形で規定しています。 この環境変数インターフェースは、伝達できる情報の構造や、サポートできるプリミティブの点で限界があります。

環境変数インターフェースの限界

環境変数インターフェースには、以下の限界が確認されています。

  • フラットである。構造化データは、より上位の命名規則やデータエンコーディング方式を使う場合にのみ許容されます。構造化された設定がないことで制限を受ける設定の例には、以下が含まれます。
    • 複数のスパンプロセッサー、定期メトリクスリーダー、ログレコードプロセッサーの設定。
    • ビューの設定。
    • 親ベースサンプラーの引数の設定(親がリモートでサンプリングされている場合とされていない場合、親がローカルでサンプリングされている場合とされていない場合のサンプラー)。
  • ランタイムに依存する。異なるシステムはこのインターフェースを異なる形で公開します(Linux、BSD、Windows)。これは通常、異なるシステム上で設定インターフェースと正しくやり取りするために、それぞれ固有の手順が必要になることを意味します。
  • 値が限定的である。多くのシステムでは文字列値のみが使用を許可されていますが、OpenTelemetryはこの型以外にも多くの設定値を規定しています。たとえば、OTEL_RESOURCE_ATTRIBUTESはリソース属性として使うキーバリューペアのリストを指定しますが、配列値を指定したり、値を非文字列型として解釈すべきことを示す方法はありません。
  • 検証が限定的である。検証は受信側でしか行えず、入力を検証するためのメタ設定言語がありません。
  • バージョニングが欠けている。環境変数にバージョニングのサポートがないことは、時間の経過に伴う進化を妨げます。

説明

設定モデルまたは設定ファイルを使うことで、ユーザーは現在環境変数経由で利用可能なすべてのオプションを設定できます。

ゴール

  • 設定は言語実装に依存しないものでなければなりません。 一部の言語でしか解釈できない構造や記述を含んではなりません。 これは、一部の言語向けに特化した拡張を設定に含められる可能性を排除するものではありませんが、設定がすべての実装言語で解釈可能でなければならないことを意味します。
  • 広くサポートされているフォーマットであること。 理想的には、ファイルにエンコードされた情報は、すべてのOpenTelemetry実装言語のネイティブツールを使ってデコードできます。 ただし、エンコーディングフォーマットをネイティブにサポートしない言語が独自のパーサーを書けることも必要です。
  • 設定フォーマットは構造化データをサポートしなければなりません。 最低限、配列と連想配列をサポートする必要があります。
  • フォーマットは少なくとも真偽値、文字列、倍精度浮動小数点数(IEEE 754-1985)、符号付き64ビット整数の値型をサポートしなければなりません。
  • カスタムのスパンプロセッサー、エクスポーター、サンプラー、その他のユーザー定義の拡張コンポーネントを、このフォーマットを使って設定できます。
  • SDKだけでなく、計装も設定します。
  • 進化をサポートしつつ、安定性の保証を提供しなければなりません。
  • 設定の構造はスキーマを通じて検証できます。
  • ユーザーがこれらのファイルに機密情報を保存しなくて済む選択肢を提供するため、環境変数による置換をサポートします。

スコープ外

  • 式を通じて設定ファイル内に追加の設定ファイルを埋め込むこと。 追加の設定プロバイダーは、将来この用途をサポートすることを選択してもよい(MAY)でしょう。

内部の詳細

OpenTelemetryの設定のためのスキーマは、言語実装がその定義を活用してコードを自動生成したり、エンドユーザーの設定を検証したりできるように、リポジトリで公開される予定です。 これにより、すべての実装が、サポートするスキーマのどのバージョンに対しても一貫した体験を提供できるようになります。 提案されているそのようなスキーマの例はこちらにあります。

ワーキンググループは、スキーマを定義する言語としてJSON Schemaの使用を提案しています。 これは以下を提供します。

  • クライアントサイドでの検証のサポート
  • コード生成
  • 言語をまたいだ広範なサポート

最小限のAPI表面積を提供するため、実装は以下をサポートしなければなりません(MUST)。

Parse(file) -> config

Parseと呼ばれるAPIはファイルオブジェクトを受け取ります。 このメソッドはファイルの内容を読み込み、パースし、設定をスキーマに対して検証します。 JSONまたはYAMLのうち少なくとも一方をサポートしなければなりません(MUST)。 追加の依存関係なしにいずれかのフォーマットをサポートできる場合、そのフォーマットが優先されるべきです(SHOULD)。 どちらもネイティブにサポートされていない、あるいは両方サポートされている場合は、YAMLを優先する選択肢とすべきです。 依存関係の懸念からYAMLがサポートされない場合、ユーザーが自分で依存関係をインストールすることで明示的に有効化できる方法があってもよい(MAY)でしょう。

このメソッドは、検証済みの設定モデルを返します。 このAPIは、以下の理由により、実装として慣用的な方法でエラーを返す、または例外を送出してもよい(MAY)でしょう。

  • ファイルが存在しない、または無効である
  • パースされた設定がスキーマに照らして無効である

PythonのParseの例


filepath = "./config.yaml"


try:
  cfg = opentelemetry.Parse(filepath)
except Exception as e:
  print(e)

filepath = "./config.json"

try:
  cfg = opentelemetry.Parse(filepath)
except Exception as e:
  raise e

GoのParseの例


filepath := "./config.yaml"
cfg, err := otel.Parse(filepath)
if err != nil {
  return err
}

filepath := "./config.json"
cfg, err := otel.Parse(filepath)
if err != nil {
  return err
}

実装は、ユーザーが設定ファイルを指定するための環境変数を指定できるようにしなければなりません(MUST)。 これにより、コマンドライン引数をサポートしない実装のエンドユーザーに柔軟性を提供します。 この変数の名前として、以下が提案されています。

  • OTEL_CONFIG_FILE

設定ファイルのフォーマットは、この変数のファイル拡張子を使って検出されます。

Configurer

Configurer設定モデルを解釈し、設定済みのSDKコンポーネントを生成します。

複数のConfigurerを異なる設定で作成できます。 生成されたSDKコンポーネントがアプリケーションと計装に正しく組み込まれることを保証するのは、呼び出し側の責任です。

Configurerは将来、更新された設定モデルを生成済みのSDKコンポーネントに適用する機能で拡張されてもよい(MAY)でしょう。

Create(config) -> Configurer

設定モデルからConfigurerを作成します。

TracerProvider、MeterProvider、LoggerProviderの取得

設定モデルを解釈し、設定オブジェクトの詳細を厳密に反映し、OpenTelemetryの環境変数設定方式を無視する、SDKのTracerProvider、MeterProvider、LoggerProviderを返します。

設定モデル

SDKと計装ライブラリがパースロジックを実装することなく設定を受け取れるようにするため、実装はConfigurationモデルを提供してもよい(MAY)でしょう。 このオブジェクトは以下のようになります。

  • ファイルまたはデータ構造からすでにパースされている
  • 構造的に有効である(SDKや計装がオブジェクトを解釈する際にエラーが発生する可能性は残る)

設定ファイル

以下は設定ファイルフォーマットの例を示します(完全な例はこちら)。

# include version specification in configuration files to help with parsing and schema evolution.
file_format: 0.1
sdk:
  # Disable the SDK for all signals.
  #
  # Boolean value. If "true", a no-op SDK implementation will be used for all telemetry
  # signals. Any other value or absence of the variable will have no effect and the SDK
  # will remain enabled. This setting has no effect on propagators configured through
  # the OTEL_PROPAGATORS variable.
  #
  # Environment variable: OTEL_SDK_DISABLED
  disabled: false
  # Configure resource attributes and resource detection for all signals.
  resource:
    # Key-value pairs to be used as resource attributes.
    #
    # Environment variable: OTEL_RESOURCE_ATTRIBUTES
    attributes:
      # Sets the value of the `service.name` resource attribute
      #
      # Environment variable: OTEL_SERVICE_NAME
      service.name: !!str "unknown_service"
  # Configure context propagators. Each propagator has a name and args used to configure it. None of the propagators here have configurable options so args is not demonstrated.
  #
  # Environment variable: OTEL_PROPAGATORS
  propagators: [tracecontext, baggage]
  # Configure the tracer provider.
  tracer_provider:
    # Span exporters. Each exporter key refers to the type of the exporter. Values configure the exporter. Exporters must be associated with a span processor.
    exporters:
      # Configure the zipkin exporter.
      zipkin:
        # Sets the endpoint.
        #
        # Environment variable: OTEL_EXPORTER_ZIPKIN_ENDPOINT
        endpoint: http://localhost:9411/api/v2/spans
        # Sets the max time to wait for each export.
        #
        # Environment variable: OTEL_EXPORTER_ZIPKIN_TIMEOUT
        timeout: 10000
    # List of span processors. Each span processor has a name and args used to configure it.
    span_processors:
      # Add a batch span processor.
      #
      # Environment variable: OTEL_BSP_*, OTEL_TRACES_EXPORTER
      - name: batch
        # Configure the batch span processor.
        args:
          # Sets the delay interval between two consecutive exports.
          #
          # Environment variable: OTEL_BSP_SCHEDULE_DELAY
          schedule_delay: 5000
          # Sets the maximum allowed time to export data.
          #
          # Environment variable: OTEL_BSP_EXPORT_TIMEOUT
          export_timeout: 30000
          # Sets the maximum queue size.
          #
          # Environment variable: OTEL_BSP_MAX_QUEUE_SIZE
          max_queue_size: 2048
          # Sets the maximum batch size.
          #
          # Environment variable: OTEL_BSP_MAX_EXPORT_BATCH_SIZE
          max_export_batch_size: 512
          # Sets the exporter. Exporter must refer to a key in sdk.tracer_provider.exporters.
          #
          # Environment variable: OTEL_TRACES_EXPORTER
          exporter: zipkin
      # custom processor
      - name: my-custom-processor
        args:
          foo: bar
          baz: qux

  # Configure the meter provider.
  ...

環境変数名と設定ファイルのキーの間には一貫したマッピングがないことに注意してください。

環境変数による置換

設定ファイルは環境変数の展開をサポートしなければなりません(MUST)。 これは、設定ファイルが機密データを参照する必要があり、それを安全に保存できないシナリオに対応するものですが、環境変数の展開は機密データに限定されるものではありません。

開発の出発点として、環境変数展開の構文はCollectorのものを反映してもよい(MAY)でしょう。

たとえば、API_KEY=1234という環境が与えられている場合、以下の設定ファイルの内容は、

file_format: 0.1
sdk:
  tracer_provider:
    exporters:
      otlp:
        endpoint: https://example.host:4317/v1/traces
        headers:
          api-key: ${env:API_KEY}

置換後、以下のようになります。

file_format: 0.1
sdk:
  tracer_provider:
    exporters:
      otlp:
        endpoint: https://example.host:4317/v1/traces
        headers:
          api-key: 1234

実装は、設定ファイルの内容を検証・パースする前に、環境変数の置換を実行しなければなりません(MUST)。

設定ファイルが未定義の環境変数を参照している場合、実装はエラーを返す、または例外を送出しなければなりません(MUST)。

環境変数とファイル設定の重複の扱い

設定ファイルと環境変数の両方が存在する場合の振る舞いは、最終的な設計で決定されます。 以下は検討すべき4つの選択肢です。

  1. 実装は設定ファイルを優先し、環境変数を無視する
  2. 実装は設定ファイルよりも環境変数を優先する
  3. 例外が発生し、アプリケーションの起動が失敗する
  4. 振る舞いは未規定のままとする

設定ファイルにおける環境変数置換のサポートは、ユーザーに環境変数から設定ファイルへ移行するための仕組みを提供します。

バージョンの保証と後方互換性

設定スキーマの各バージョンは、メジャーバージョンとマイナーバージョンを持ちます。 設定は、それが準拠するメジャーバージョンとマイナーバージョンを指定します。 1.0に達する前は、各マイナーバージョンの変更はメジャーバージョンの変更と同等です。 つまり、互換性についての保証はなく、すべての変更が許可されます。 1.0以降は、以下の安定性保証を提供します。

  • メジャーバージョンについて: 保証はありません。
  • マイナーバージョンについて: 未定(TBD)

許容される変更:

  • メジャーバージョンについて: すべての変更が許可されます。
  • マイナーバージョンについて: 未定(TBD)

設定を検証するSDKは、サポートされていないバージョンの設定に遭遇した場合、失敗しなければなりません(MUST)。 一般的には、これは認識できないメジャーバージョンに遭遇した場合に失敗することを意味します。 SDKは、メジャーバージョンごとにバリデーター/パーサーのライブラリを保持し、設定のバージョンを使って正しいインスタンスを選択・使用するという方法を選んでもよいでしょう。 マイナーバージョンの違い(1.0未満のマイナーバージョンを除く)は、受け入れ可能でなければなりません(MUST)。

トレードオフと緩和策

OpenTelemetryを設定するための追加の方法

本OTEPで提案された実装が進められる場合、ユーザーはOpenTelemetryを設定するための、また別のメカニズムを提示されることになります。 これは、プロジェクトに不慣れなユーザーに混乱をもたらす可能性があります。 ベストプラクティスとドキュメントをユーザーに提供することで、この混乱を緩和できる可能性があります。

設定方法が多いことで、ユーザーが何が設定されているかわからなくなる可能性

設定のためのメカニズムが複数存在するため、有効になっている設定が期待していたものと異なる可能性があります。 これは現在でも起こり得ることであり、緩和する一つの方法として、有効なOpenTelemetryの設定を一覧表示する仕組みを提供することが考えられます。

設定ファイルにおけるエラーや困難さ

設定ファイルは誤設定の機会を生み出します。 これを緩和する方法としては、誤設定が発生した際に明確なメッセージを提供し、早期に失敗させることが考えられます。

先行技術と代替技術

ワーキンググループは、着想と指針を得るためにOpenTelemetry CollectorとOpenTelemetry Operatorを参照しました。

代替のスキーマ言語

JSON Schemaを推奨として選定するにあたり、ワーキンググループは以下の選択肢を検討しました。

  • Cue - スキーマを定義するための、よりシンプルで有望な言語ですが、ワーキンググループは以下の理由からCUEを採用しないことにしました。
    • Go以外の言語でCUEファイルを検証するために利用できるツールが限られていました。
    • 習熟度と学習曲線の問題が、OpenTelemetryのユーザーとコントリビューターの両方にとって問題を生むことになります。
  • Protobuf - OpenTelemetryですでに広く使われているprotobufは、スキーマを定義する選択肢として調査する価値がありました。ワーキンググループは以下の理由からprotobufを採用しないことにしました。
    • 検証エラーはシリアライズエラーの結果として生じるため、解釈が難しい場合があります。
    • スキーマ定義言語の制約により、型安全性を維持しようとするとエルゴノミクスが悪化します。

未解決の疑問

ノーコードの設定とプログラムによる設定をどう扱うか

ノーコードの設定(環境変数またはファイル設定のいずれか)とプログラムによる設定の両方が存在する場合、SDKはどのように設定されるべきでしょうか。 注: この疑問は、環境変数インターフェースのみが利用可能な現在においてもすでに存在します。

  • 解決策1: 環境の解釈をコンポーネントに組み込むべきではないことを明確にします。 代わりに、SDKは環境を明示的に解釈し、設定済みのSDKインスタンスを返すコンポーネントを持つべきです。 これは現在のJava SDKの動作方法であり、関心事をうまく分離しています。

使用すべき正確な設定ファイルフォーマットは何か

本OTEPには、設定ファイルフォーマットの例が含まれています。 この例のフォーマットは、ここで提案されている設定ファイルスキーマがすべて実現可能な選択肢として評価できるように定められたものです。 これは、必要なOTelの設定を記述でき、かつスキーマによって記述できる設定ファイルフォーマットがあることを示す、概念実証として機能しました。 ただし、ここで示す設定ファイルフォーマットは、OpenTelemetryが使用する最終的な、あるいは最適な設計として意図されたものではありません。

その最終的な設計がどのようなものになるかは、本OTEPがOpenTelemetry仕様に実装される際の議論に委ねられます。 これは、本OTEPが解決することを明示的に意図していない事柄です。

以下は、最終的な設計で解決する必要がある既存の疑問です。

  1. トレースエクスポーターはスパンプロセッサーと同じレベルに置かれるべきでしょうか。
  2. サンプラーの設定はサンプラーから分離されるべきでしょうか。
  3. sdkキーは適切でしょうか。代替の設定は独自のキーの下に置き、SDKの設定はトップレベルに置くべきでしょうか。
  4. disabledキーはenabledに名称変更されるべきでしょうか。

このリストは網羅的であることを意図していません。 最終的な設計に関連する疑問はさらに存在する可能性が高く、OpenTelemetry仕様に実装される際に議論されるでしょう。

将来の可能性

追加の設定プロバイダー

設定の当初の提案がコード内表現とファイル表現のみを記述しているとはいえ、設定のための追加のソース(リモート、OpAMPなど)が望まれる可能性があります。 設定モデルとコンポーネントの実装は、これを可能にするために拡張可能であるべきです。

自動計装との統合

設定モデルは、各言語実装における既存の自動計装ツールと連携するように統合できる可能性があります。

Java

Java実装は、システムプロパティを介してさまざまなパラメータを設定できるJARを提供します。 この実装は、システムプロパティとしてその設定をサポートすることで、設定ファイルを活用できる可能性があります。

java -javaagent:path/to/opentelemetry-javaagent.jar \
     -Dotel.config.file=./config.yaml
     -jar myapp.jar

Python

Python実装には、ユーザーが自動計装を活用できるコマンドが用意されています。 opentelemetry-instrumentコマンドは、設定ファイルを渡すために--configフラグを使える可能性があります。

# install the instrumentation package
pip install opentelemetry-instrumentation
# use a --config parameter to pass in the configuration file
# NOTE: this parameter does not currently exist and would need to be added
opentelemetry-instrument --config ./config.yaml ./python/app.py

OpAMP

この設定は将来、OpAMPプロトコルと組み合わせて使われ、SDKのリモート設定をOpenTelemetryがサポートする機能として利用可能にするかもしれません。