OTEP-0121: SDK向け動的設定サービス

この提案は、メトリクス収集周期を設定するプロトタイプを開発するためのリクエストです。 メトリクスごとの設定とトレーシングの設定も追加する予定であり、詳細は後のイテレーションに委ねられます。

これはこのプルリクエストに関連しています。

Motivation

通常の使用中、ユーザーは10分ごとにメトリクスを収集したいと考えるかもしれません。 その後、本番環境の問題を調査する際に、同じユーザーが一部のプロセスを再設定し、30秒ごとにメトリクスを収集するようにすることで、デバッグに利用できる情報を簡単に増やすことができます。 この変更は一元化されており、新しい設定での再デプロイを必要としないため、設定を更新する際の摩擦とリスクが低くなります。

Explanation

このOTEPは、実験的機能であるopen-telemetry/opentelemetry-specification#62の提案であり、概念実証として開発されます。 これは、OpenTelemetry SDKやCollectorのいずれの内部でも開発が行われないことを意味します。 これはopentelemetry-go-contribopentelemetry-collector-contribで実装されるため、この機能はすべてオプションになります。

ユーザーはアプリケーションを計装する際に、リモート設定サービスのエンドポイント、関連付けられたResource、そして設定サービスからの読み取りに失敗した場合に使用されるデフォルト設定でSDKを設定できます。

次に、ユーザーは設定サービスをセットアップする必要があります。 これはCollectorを通じて行うことができ、Collectorは任意の設定サービス実装を公開するように設定できます。 実装によっては、これによりCollectorは、スタンドアロンの設定サービスとして動作することも、監視バックエンドのプロトコルをOpenTelemetryの設定プロトコルに準拠するように「変換」することで、ユーザーの監視バックエンドのリモート設定へのブリッジとして動作することもできます。

Internal details

将来的には、メトリクスごとの設定を追加する予定です。 たとえば、これによりユーザーは5xxサーバーエラー数を毎分収集し、CPU使用率の統計を10分ごとに収集できるようになります。 リモート設定プロトコルはこれを念頭に置いて設計されており、単なるメトリクス収集周期以上の詳細を含んでいます。

私たちのリモート設定プロトコルは、次の呼び出しをサポートします。

service MetricConfig {
  rpc GetMetricConfig (MetricConfigRequest) returns (MetricConfigResponse);
}

設定サービスへのリクエストは次のようになります。

message MetricConfigRequest{

  // Required. The resource for which configuration should be returned.
  opentelemetry.proto.resource.v1.Resource resource = 1;

  // Optional. The value of ConfigResponse.fingerprint for the last configuration
  // that the caller received and successfully applied.
  bytes last_known_fingerprint = 2;
}

一方、レスポンスは次のようになります。

message MetricConfigResponse {

  // Optional. The fingerprint associated with this MetricConfigResponse. Each
  // change in configs yields a different fingerprint.
  bytes fingerprint = 1;

  // A Schedule is used to apply a particular scheduling configuration to
  // a metric. If a metric name matches a schedule's patterns, then the metric
  // adopts the configuration specified by the schedule.

  message Schedule {

    // A light-weight pattern that can match 1 or more
    // metrics, for which this schedule will apply. The string is used to
    // match against metric names. It should not exceed 100k characters.
    message Pattern {
      oneof match {
        string equals = 1;       // matches the metric name exactly
        string starts_with = 2;  // prefix-matches the metric name
      }
    }

    // Metrics with names that match at least one rule in the inclusion_patterns are
    // targeted by this schedule. Metrics that match at least one rule from the
    // exclusion_patterns are not targeted for this schedule, even if they match an
    // inclusion pattern.

    // For this iteration, since we only want one Schedule that applies to all metrics,
    // we will not check the inclusion_patterns and exclusion_patterns.
    repeated Pattern exclusion_patterns = 1;
    repeated Pattern inclusion_patterns = 2;

    // Describes the collection period for each schedule in seconds.
    int32 period_sec = 3;
  }

  // For this iteration, since we only want one Schedule that applies to all metrics,
  // we will have a restriction that schedules must have a length of 1, and we will
  // not check the patterns when we apply the collection period.
  repeated Schedule schedules = 2;

  // Optional. The client is suggested to wait this long (in seconds) before
  // pinging the configuration service again.
  int32 suggested_wait_time_sec = 3;
}

SDKは GetConfig を使用して、サービスから定期的に設定を読み取ります。 この読み取り間隔は状況に応じて変わることがあります。 読み取りに失敗した場合、SDKはデフォルト設定か、直近に読み取りに成功した設定のいずれかを使用します。 新しい設定を読み取った場合は、それを適用します。

SDKからのエクスポート頻度は Schedule に依存します。 現時点では、すべてのメトリクスに対するスケジュールを定義する Schedule は1つしか存在できません。 Schedule には CollectionPeriod があり、これはメトリクスがどのくらいの頻度でエクスポートされるかを定義します。

将来的には、メトリクスごとの設定を追加します。 各 Scheduleinclusion_patternsexclusion_patterns も持ちます。 inclusion_patterns のいずれかに一致し、かつ exclusion_patterns のいずれにも一致しないメトリクスは、CollectionPeriod(たとえば毎分)ごとにエクスポートされます。 あるSchedule のパターンに一致するメトリクスをそのSchedule の収集周期でエクスポートしつつ、別のSchedule のパターンに一致する他のメトリクスをその別のSchedule の収集周期でエクスポートできるコンポーネントが追加されます。

Collectorは、SDKから利用できる DynamicConfig サービスの新しいインターフェースをサポートし、上記の設定サービスプロトコルのカスタム実装が、SDKと任意の設定サービスとの間のオプションのブリッジとして動作できるようにします。 このインターフェースは、任意のバックエンドからリモート設定にアクセスすることをサポートするシムとして実装できます。 Collectorは、DynamicConfig サービスへのリクエストのためのエンドポイントを公開するように設定され、そのエンドポイントで結果を返します。

Trade-offs and mitigations

この機能は、その実現可能性と有用性を示すための純粋な実験として実装されます。 大まかなプロトタイプが示された後に、さらなる調査を行うことができます。

こちらで述べられているように、プロトコルで何を許可するかによっては、設定サービスはOpenTelemetryで計装されたアプリケーションに対する潜在的な攻撃ベクトルになり得ます。 リモート設定プロトコルにおいて、今後の変更については、許可する設定の種類に関して注意が必要であることを強調することができます。

ポーリング間隔(設定を読み取る頻度)を短くすると、設定変更を非常に速く適用できるようになります。 しかし、これは設定サービスにかかる負荷も増加させます。 典型的なユースケースでは、設定変更をすぐに適用する必要はおそらくなく、設定変更もかなり頻度が低いと考えられるため、典型的なポーリング間隔は数分に一度程度の頻度で十分と考えられます。

Prior art and alternatives

Jaegerには Remote sampler というオプションがあり、Adaptive sampler を使うことで中央集権的な設定から動的に読み取ることさえ可能です。

リモート設定における主な比較対象は、プッシュ方式とポーリング方式のどちらを採用するかです。 設定サービスが新しい設定をプッシュする仕組みを持つことの利点は、ユーザーの作業が少なくて済み、設定サービスをセットアップする必要がないことです。 また、計装されたアプリケーションにおいて設定サービスをポーリングすることによる負荷もないため、OpenTelemetry SDKをより軽量に保つことができます。

ポーリング方式を使用すると、多数の計装済みプロセスを持つ大規模な分散アプリケーションの文脈において、より高いパフォーマンスが得られる可能性があります。 これは、設定サービスがプロセスにプッシュしなければならないのではなく、計装されたプロセスが設定サービスをポーリングすることの結果です。 また、ポーリング方式はgRPCだけでなく、より多くのネットワークプロトコルと互換性があります。

Open questions

  • こちらで述べられているように、悪意のある、または偶発的な設定変更がアプリケーションや監視システムを圧迫した場合、何が起こるのでしょうか。 設定変更を行う際に注意を払うことはユーザーの責任なのでしょうか。 パフォーマンスの問題を検知できた場合、テレメトリーのエクスポートを自動的に減らすべきでしょうか。

Future possibilities

このOTEPが実装された場合、他の項目をリモートかつ動的に設定するというオプションもあります。 こちらで述べられているように、ラベルや集計もその候補に含まれます。 こちらで述べられているように、Collectorを設定することも可能です。

将来的には、メトリクスごとの設定に加えて、トレーシングの設定も追加する予定です。