コンテンツにスキップ

OTEL Agent サンプリング戦略

著者:宋龙奇

はじめに

分散型サービスにおけるトレーシングでは、リクエストがサービス間をどのように移動するかを観察することが非常に重要です。しかし、多くの場合、データは大量に重複しています。これらのデータは本当に重要なのでしょうか?そのような場合、適切なサンプリング戦略を用いることで、不要なトラフィックコストを削減できます。

サンプリングとは、データを受け入れ、エクスポートまたは保存する準備をすることを指します。サンプリングは、データが破棄されると誤解されることがありますが、それは正しくありません。

前回の記事 OpenTelemetry サンプリングのベストプラクティス では、主に opentelemetry-collector のサンプリング戦略について紹介しました。この記事では、主に Java Agent 側のサンプリングについて説明します。

サンプリング戦略

ヘッドサンプリング

ヘッドサンプリングの最も一般的な形式は、一貫した確率サンプリングです。これは決定論的サンプリングとも呼ばれます。この場合、TraceID とサンプリングするトレースの割合に基づいてサンプリングの決定が行われます。これにより、トレース全体がサンプリングされることが保証されます。例えば、全トレースの 5% をサンプリングする場合、生成されたすべてのトレースが 5% の割合でサンプリングされます。

ヘッドサンプリングには多くの利点があります:

  • 設定が簡単
  • 理解が容易
  • 高効率
  • 任意の場所に設定可能

一方、欠点も明らかです。トレース全体のデータに基づいてサンプリングの決定を下すことはできません。トレース内でエラーが発生した場合でも、サンプリングしてアップロードされるとは限らず、問題の調査には不利です。

そのため、テールサンプリングについて理解する必要があります。

テールサンプリング

テールサンプリングとは、トレース内のすべてまたはほとんどの Span を考慮して、トレースをサンプリングするかどうかを決定することを指します。「テールサンプリング」を使用すると、トレースのさまざまな部分に由来する特定の基準に基づいてトレースをサンプリングできます。これは「ヘッドサンプリング」では不可能です。

テールサンプリングのいくつかのシナリオ:

  • エラーを含むすべてのトレースをサンプリング
  • 全体的なレイテンシーに基づいてサンプリング
  • 特定のイベントに基づいてサンプリング
  • HTTP コードが 200 以外のものをサンプリング
  • その他の基準によるサンプリング

テールサンプリングには多くの課題があります:

  • テールサンプリングは実装が難しい場合があります。使用可能なサンプリング技術の種類によっては、「設定して終わり」というものではありません。システムの変化に伴い、サンプリング戦略も変化します。大規模で複雑な分散システムでは、サンプリング戦略を実装するルールも必然的に大規模で複雑になります。
  • テールサンプリングは運用が難しい場合があります。テールサンプリングを実装するノードは、すべてのデータを受信しなければなりません。場合によっては、数十のノードからのトレースデータを受信し、計算を行う必要があります。計算速度が受信量に追いつかないこともあり、これらに基づいて、適切なテールサンプリングノードは大量のリソースを必要とします。
  • 現在、テールサンプリングのオプションを提供できるのは、一部の大規模ベンダーに限られる可能性があります。

ヘッドサンプリングとテールサンプリングの概要は以上です。

最後に、大量の Span データを生成するアプリケーションでは、ヘッドサンプリングを選択する必要があります。これにより、トレースチャネルが過負荷で詰まることがなくなります。

OTEL Java Agent では、ヘッドサンプリング に基づく戦略が採用されています。この点は事前に理解しておく必要があります。

他の言語のサポートや Collector の設定については、公式ドキュメント を参照してください。


Agent のサンプラー Sampler

OTEL では、サンプリングを設定するための 2 つの環境変数があります:OTEL_TRACES_SAMPLEROTEL_TRACES_SAMPLER_ARG

サンプリング設定の選択方法

OTEL_TRACES_SAMPLER の設定は以下の通りです:

  • "always_on": AlwaysOnSampler デフォルト設定は 1.0、つまりサンプリングしない
  • "always_off": AlwaysOffSampler 常にすべてのトレースを収集
  • "traceidratio": TraceIdRatioBased trace id に基づく確率サンプリング
  • "parentbased_always_on": ParentBased(AlwaysOnSampler に基づく)
  • "parentbased_always_off": ParentBased(AlwaysOffSampler に基づく)
  • "parentbased_traceidratio": ParentBased(TraceIdRatioBased に基づく)
  • "parentbased_jaeger_remote": ParentBased(JaegerRemoteSampler に基づく)
  • "jaeger_remote": JaegerRemoteSampler
  • "xray": AWS X-Ray Centralized Sampling (third party)

OTEL_TRACES_SAMPLER_ARG の設定

  • 1.0 デフォルトは 1.0、つまり全サンプリング
  • [0-1.0] 確率サンプリング、例:0.25 は 25% のサンプリング率
  • サンプリング設定が traceidratio および parentbased_traceidratio の場合のみ、OTEL_TRACES_SAMPLER_ARG が有効になります。

よく使われる設定

OTEL_TRACES_SAMPLER=parentbased_traceidratio(親ベースの Trace ID 比率):このサンプリング戦略は、親の Trace ID に基づいて子がサンプリングされるべきかどうかを判断します。新しいリクエストが到着すると、指定された比率の範囲内に親の Trace ID があるかどうかをチェックします。該当する場合、子もサンプリングされます。この戦略により、リクエストとその関連操作間の関連性が確保されます。これは分散トレーシングシステムに非常に有用です。

OTEL_TRACES_SAMPLER=traceidratio(Trace ID 比率):このサンプリング戦略は、各リクエストの Trace ID に基づいて、そのリクエストをサンプリングするかどうかを判断します。各リクエストには一意の Trace ID があります。この戦略を使用する場合、各リクエストは指定された比率に基づいて独立してサンプリングされるかどうかが決定されます。この戦略は、リクエスト間の関連性を考慮する必要がなく、各リクエスト自体のサンプリング率のみに焦点を当てる場合に適しています。

以上をまとめると、OTEL_TRACES_SAMPLER=parentbased_traceidratio は親の Trace ID に基づいて子のサンプリング率を決定し、OTEL_TRACES_SAMPLER=traceidratio は各リクエストの Trace ID に基づいて独立してサンプリング率を決定します。適切な戦略の選択は、リクエスト間の関連性に対する重視度に依存します。

よく使われるコマンドは以下の通りです:

-Dotel.traces.sampler=traceidratio
-Dotel.traces.sampler.arg=0.2

カスタム Sampler プラグイン

OTEL Agent はプラグインインターフェース Extensions を提供しており、カスタム Sampler を使用してサンプリングを実装できます。

以下は、カスタム Sampler が実装する必要があるインターフェースです:

public class DemoSampler implements Sampler {
  @Override
  public SamplingResult shouldSample(
      Context parentContext,
      String traceId,
      String name,
      SpanKind spanKind,
      Attributes attributes,
      List<LinkData> parentLinks) {
    if (spanKind == SpanKind.INTERNAL && name.contains("greeting")) {
      return SamplingResult.create(SamplingDecision.DROP);
    } else {
      return SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE);
    }
  }

  @Override
  public String getDescription() {
    return "DemoSampler";
  }
}

インターフェースの実装では、parentContext, traceId, name, spanKind, attributes, parentLinks のパラメータがあることがわかります。

したがって、カスタム Sampler では、複数の次元からそのトレースをサンプリングするかどうかを判断できます:

  • タグによるフィルタリング
  • traceId による比率サンプリング
  • name など

しかし、これでも比率サンプリングにおいてエラーを含む span が削除されることを避けられません。これは、shouldSample() が span の初期化時に呼び出されるのに対し、エラーは通常 span の終了時に該当する Event に追加されるためです。

フィードバック

このページは役に立ちましたか?