コンテンツにスキップ

OpenTelemetry サンプリングのベストプラクティス


全リンクデータは、関係者が業務上の問題をタイムリーかつ正確に発見するのに大いに役立ちます。しかし、企業の業務で問題が発生する確率は一般的に非常に低く、全リンクサンプリングには長所と短所があります。

長所

  • リンクデータが完全

短所

  • リソースの浪費。データが完全であるがゆえにデータストレージリソースのコストが大幅に増加し、異常リンクデータの検索コストも高くなる

全リンクデータ収集に基づくサンプリングとして、OpenTelemetry は2種類のサンプラーをサポートしています。

  1. 確率サンプリングプロセッサ(probabilisticsamplerprocessor)

  2. テールサンプリングプロセッサ(tailsamplingprocessor)

確率サンプリングプロセッサ

名前の通り、確率サンプリングプロセッサは、ある確率に従ってサンプリングを行います。OpenTelemetry は2種類の確率サンプリングをサポートしています。

  1. sampling.priority OpenTracing で定義されたセマンティック規約

  2. TraceId ハッシュ

sampling.priority のセマンティック規約は TraceId ハッシュよりも優先されます。TraceId ハッシュサンプリングは、TraceId から決定されるハッシュ値に基づきます。TraceId ハッシュを機能させるには、特定のレイヤーのすべてのコレクター(例:同じロードバランサーの背後にあるもの)が同じ hash_seed を持つ必要があります。また、異なるコレクターレイヤーで異なる hash_seed を利用することで、追加のサンプリング要件をサポートすることもできます。設定の詳細については、config.go を参照してください。

以下の設定オプションを変更できます。

  • hash_seed(デフォルトなし):ハッシュアルゴリズムを計算するための整数。特定のレイヤーのすべてのコレクター(例:同じロードバランサーの背後にあるもの)は同じ hash_seed を持つ必要があります。

これは、複数レイヤーのコレクターを使用して目的のサンプリングレートを実現する場合に重要です。例:最初のレイヤーが10%、2番目のレイヤーが10%、全体のサンプリングレートが1%(10%×10%)の場合。

すべてのレイヤーが同じシードを使用すると、1つのレイヤーを通過したすべてのデータが、設定されたサンプリングレートに関係なく、次のレイヤーも通過します。異なるレイヤーで異なるシードを使用することで、各レイヤーのサンプリングレートが期待通りに動作することを保証します。

  • sampling_percentage(デフォルト = 0):トレースをサンプリングするパーセンテージ。>= 100 はすべてのトレースを収集することを意味します。

確率サンプリングプロセッサの設定

processors:
  # 確率サンプリングプロセッサ
  probabilistic_sampler:
    hash_seed: 22
    sampling_percentage: 15.3

確率サンプリングプロセッサの有効化

service:
  extensions: [pprof, zpages, health_check]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch,probabilistic_sampler]
      exporters: [otlp]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp]

テールサンプリングプロセッサ

テールサンプリングプロセッサは、定義された一連のポリシーに基づいてトレースをサンプリングします。現在、このプロセッサはコレクターの単一インスタンスにのみ適用されます。技術的には、traceId 対応のロードバランシングを使用して複数のコレクターインスタンスをサポートできますが、この設定はまだテストされていません。設定の詳細については、config.go を参照してください。

以下の設定オプションが必要です。

  • policies(デフォルトなし):サンプリング決定を行うためのポリシー

現在、複数のポリシーがサポートされています。これらには以下が含まれます。

  • always_sample:すべてのトレースをサンプリングします。
  • latency:トレースの継続時間に基づくサンプリング。継続時間は、最も早い開始時間と最も遅い終了時間を確認し、その間の出来事を考慮せずに決定されます。
  • numeric_attribute:数値属性に基づくサンプリング
  • probabilistic:一定の割合のトレースをサンプリングします。
  • status_code:ステータスコード(OKERRORUNSET)に基づくサンプリング。多くの人がこれを response body JSON の code(現在多くのプロジェクトでそのように定義されています)と誤解していますが、実際は異なります。
  • string_attribute:文字列属性値の一致に基づくサンプリング。完全一致と正規表現値の一致をサポートします。
  • rate_limiting:レートベースのサンプリング
  • and:複数のポリシーに基づくサンプリング。AND ポリシーを作成します。
  • composite:上記のサンプラーの組み合わせに基づくサンプリング。各サンプラーには順序とレート配分があります。レート配分は、各ポリシー順序に一定の割合のスパンを割り当てます。例えば、max_total_spans_per_second を 100 に設定した場合、rate_allocation を次のように設定できます。
  • test-composite-policy-1 = max_total_spans_per_second の 50 % = 50 spans_per_second
  • test-composite-policy-2 = max_total_spans_per_second の 25 % = 25 spans_per_second
  • 残りの容量を確実に満たすために、ポリシーの1つとして always_sample を使用します。

以下の設定オプションも変更できます。

  • decision_wait(デフォルト = 30 秒):サンプリング決定を行う前に、トレースの最初のスパンから待機する時間
  • num_traces(デフォルト = 50000):メモリ内に保持するトレースの数
  • expected_new_traces_per_sec(デフォルト = 0):予想される新しいトレースの数(データ構造の割り当てに役立ちます)

例:

processors:
  tail_sampling:
    decision_wait: 10s
    num_traces: 100
    expected_new_traces_per_sec: 10
    policies:      [
          {
            name: test-policy-1,
            type: always_sample
          },
          {
            name: test-policy-2,
            type: latency,
            latency: {threshold_ms: 5000}
          },
          {
            name: test-policy-3,
            type: numeric_attribute,
            numeric_attribute: {key: key1, min_value: 50, max_value: 100}
          },
          {
            name: test-policy-4,
            type: probabilistic,
            probabilistic: {sampling_percentage: 10}
          },
          {
            name: test-policy-5,
            type: status_code,
            status_code: {status_codes: [ERROR, UNSET]}
          },
          {
            name: test-policy-6,
            type: string_attribute,
            string_attribute: {key: key2, values: [value1, value2]}
          },
          {
            name: test-policy-7,
            type: string_attribute,
            string_attribute: {key: key2, values: [value1, val*], enabled_regex_matching: true, cache_max_size: 10}
          },
          {
            name: test-policy-8,
            type: rate_limiting,
            rate_limiting: {spans_per_second: 35}
         },
         {
            name: test-policy-9,
            type: string_attribute,
            string_attribute: {key: http.url, values: [\/health, \/metrics], enabled_regex_matching: true, invert_match: true}
         },
         {
            name: and-policy-1,
            type: and,
            and: {
              and_sub_policy:               [
                {
                  name: test-and-policy-1,
                  type: numeric_attribute,
                  numeric_attribute: { key: key1, min_value: 50, max_value: 100 }
                },
                {
                    name: test-and-policy-2,
                    type: string_attribute,
                    string_attribute: { key: key2, values: [ value1, value2 ] }
                },
              ]
            }
         },
         {
            name: composite-policy-1,
            type: composite,
            composite:              {
                max_total_spans_per_second: 1000,
                policy_order: [test-composite-policy-1, test-composite-policy-2, test-composite-policy-3],
                composite_sub_policy:                  [
                    {
                      name: test-composite-policy-1,
                      type: numeric_attribute,
                      numeric_attribute: {key: key1, min_value: 50, max_value: 100}
                    },
                    {
                      name: test-composite-policy-2,
                      type: string_attribute,
                      string_attribute: {key: key2, values: [value1, value2]}
                    },
                    {
                      name: test-composite-policy-3,
                      type: always_sample
                    }
                  ],
                rate_allocation:                  [
                    {
                      policy: test-composite-policy-1,
                      percent: 50
                    },
                    {
                      policy: test-composite-policy-2,
                      percent: 25
                    }
                  ]
              }
          },
        ]

プロセッサの使用に関する詳細な例については、tail_sampling_config.yaml を参照してください。

確率サンプリングプロセッサと確率ポリシーを使用したテールサンプリングプロセッサの比較

確率サンプリングプロセッサと確率テールサンプリングプロセッサのポリシーは、非常に似た方法で動作します。設定可能なサンプリングパーセンテージに基づいて、受信したトレースを一定の割合でサンプリングします。ただし、全体的な処理パイプラインに応じて、どちらかを優先して使用する必要があります。

経験則として、確率サンプリングを追加したい場合で、かつ...

...まだテールサンプリングプロセッサを使用していない場合:確率サンプリングプロセッサを使用します。確率サンプリングプロセッサの実行は、テールサンプリングプロセッサよりも効率的です。確率サンプリングポリシーは traceId に基づいて決定を行うため、より多くのスパンが到着するのを待ってもその決定に影響はありません。

...すでにテールサンプリングプロセッサを使用している場合:確率サンプリングポリシーを追加します。テールサンプリングプロセッサを実行するコストはすでに発生しているため、確率ポリシーを追加してもその影響はごくわずかです。さらに、テールサンプリングプロセッサ内でこのポリシーを使用することで、他のポリシーによってサンプリングされたトレースが破棄されないことが保証されます。

デモ

このデモでは、主に OpenTelemetry データを Guance にプッシュします。

準備

  1. ソースコードをダウンロード:https://github.com/lrwh/observable-demo/tree/main/opentelemetry-collector-sampling

  2. DataKit がインストールされていることを確認します。

サービス名 ポート 説明
otel-collector otel/opentelemetry-collector-contrib:0.69.0
springboot_server 8080:8080 opentelemetry-agent バージョン 1.21.0、ソースコード:https://github.com/lrwh/observable-demo/tree/main/springboot-server
  1. DataKit で OpenTelemetry 収集を有効にします。

サービスの起動

docker-compose up -d

以下の例では、主にテールサンプリングポリシーをテストシナリオとして使用します。

テールサンプリングプロセッサの設定

processors:
  # テールサンプリングプロセッサ
  tail_sampling:
    decision_wait: 10s
    num_traces: 100
    expected_new_traces_per_sec: 100
    policies:
      [
        {
          name: policy-1,
          type: status_code,
          status_code: {status_codes: [ERROR]}
        },
        {
          name: policy-2,
          type: probabilistic,
          probabilistic: {sampling_percentage: 20}
        }
      ]

上記のルールは OR の関係であり、policy-1policy-2 のいずれかが成立した場合にサンプリングが行われます。

テールサンプリングプロセッサの有効化

service:
  extensions: [pprof, zpages, health_check]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch,tail_sampling]
      exporters: [otlp]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp]

probabilistic の使用

  tail_sampling:
    decision_wait: 10s
    num_traces: 100
    expected_new_traces_per_sec: 100
    policies:
      [
        {
          name: policy-2,
          type: probabilistic,
          probabilistic: {sampling_percentage: 20}
        }
      ]
  1. サービス API gateway に5回アクセスし、毎回正常に戻ります。

curl http://localhost:8080/gateway

  1. Guance でトレース情報を確認します。サンプリングルールに従って、最大で1つのトレースデータがサンプリングされます。

status_code の使用

processors:
  # テールサンプリングプロセッサ
  tail_sampling:
    decision_wait: 10s
    num_traces: 100
    expected_new_traces_per_sec: 100
    policies:
      [
        {
          name: policy-1,
          type: status_code,
          status_code: {status_codes: [ERROR]}
        }
      ]
  1. クライアントを起動するための設定

curl http://localhost:8080/setClient?c=true

現在のデモではクライアントサービスは起動していないため、後続の gateway インターフェースの呼び出しで異常が発生します。

  1. gateway サービスに5回アクセスし、{"msg":"client 呼び出し失敗","code":500} が返されます。

  2. Guance でトレース情報を確認します。サンプリングルールに従って、異常が発生した場合はその異常がすべてサンプリングされます。

span_count の使用

スパン数が2以下の場合、レポートしません。設定は以下の通りです。

  tail_sampling:
    decision_wait: 10s
    num_traces: 100
    expected_new_traces_per_sec: 100
    policies:
      [
        {
         name: p2,
         type: span_count,
         span_count: {min_spans: 3 }
         }
      ]

string_attribute の使用

シナリオ:GET リクエストのリンクのみを収集します。

  tail_sampling:
    decision_wait: 10s
    num_traces: 100
    expected_new_traces_per_sec: 100
    policies:
      [
        {
         name: policy-string,
         type: string_attribute,
         string_attribute: {key: http.method, values: [ GET ] }
         }
      ]
string_attribute が効かない?

はい、その通りです。string_attribute は必ずしも100%効くわけではありません。http.method を例にとると、GET リクエストをマッチさせた場合、その GET リクエストがさらに POST リクエストのリンクを呼び出すことがあります。完全なリンクには GET と POST の両方が存在するため、競合が発生します。GET がマッチするため、マッチ要件を満たすので、リンクに POST があるからといってフィルタリングされることはありません。

and の使用

and:and 条件を満たす場合にサンプリングしてレポートし、それ以外の場合は破棄します。

シナリオ:GET リクエストをレポートし、サンプリングレート20%でサンプリングし、それ以外は破棄します。

  tail_sampling:
    decision_wait: 10s
    num_traces: 100
    expected_new_traces_per_sec: 100
    policies:
      [
        {
         name: and-policy-1,
         type: and,
         and: {
           and_sub_policy:
             [
                 {
                     name: test-and-policy-2,
                     type: string_attribute,
                     string_attribute: { key: http.method, values: [ GET ] }
                 },
                 {
                     name: policy-2,
                     type: probabilistic,
                     probabilistic: {sampling_percentage: 20}
                 }
            ]
         }
      }
    ]

サンプリング記述形式の注意点

サンプリングの形式が正しくないと、サンプリングが機能しなくなる可能性があります。各単語、記号、数値の間にスペースを入れることをお勧めします。

誤った記述例

{
 name: p2,
 type: span_count,
 span_count:{min_spans: 3 }
 }
または
{
 name: p2,
 type: span_count,
 span_count: {min_spans:3 }
 }

正しい記述例
{
 name: p2,
 type: span_count,
 span_count: {min_spans: 3 }
 }

フィードバック

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