YAMAGUCHI::weblog

噛み付き地蔵に憧れて、この神の世界にやってきました。マドンナみたいな男の子、コッペです。

Tail Sampling Processorを使ってAPMの使用量を効率化しよう

はじめに

こんにちは、Google Cloudでオブザーバビリティの担当をしているものです。このエントリーはOpenTelemetry Advent Calendar 2022の2日目の記事です。1日目は @katzchang の「OpenTelemetry Collectorでログファイルの更新を取り込む」でした。

さて、みなさんは分散トレースを活用しているでしょうか。分散トレースはマイクロサービスアーキテクチャのみならず、モノリスなシステムにおいてもレイテンシーボトルネックを発見する上で有用なテレメトリーです。まだ活用されていないという方はぜひ活用していきましょう!

分散トレースのサンプリング

活用されているみなさまにおかれましては、分散トレースの取得において、トレースのサンプリング方法について頭を悩ませていることと思います。テスト環境などではサンプリングレートを100%にして、あらゆる処理のトレースを取得することが可能かもしれませんが、本番環境でそれを行うと、APMサービスで従量課金となっている場合に使用料金が跳ね上がってしまいます。そこで、通常は取得するトレースのサンプリングを行うわけですが、どのような基準でサンプリングを行うかが悩みの種となります。

一番簡単な方法はランダムサンプリングです。すべてのトレースの中から、中身によらず一定の割合のトレースだけ取得するというものです。たとえば全体のうち1%だけ取得する、といった具合です。しかしながらこの手法の課題点は、レイテンシーに問題があった場合のトレースが取得できない可能性があるということです。(レイテンシーに問題があるかどうかは事前にはわからないので仕方がない)

そこでテイルサンプリングという手法を用いて、特別なトレースのみを送信するという手法が求められます。これは、アプリケーションから直接APMバックエンドに送信する場合には、アプリケーション内でトレースのレイテンシーを計算した上で選別するという作業を行わなければなりませんが、各種エージェントを使っている場合にはエージェント内で特定のトレース以外をドロップしてくれれば実現できます。

OpenTelemetry Collectorにおいてもテイルサンプリングを実現するための Tail Sampling Processor があるのでそれを利用してみましょう。

Tail Sampling Processor

github.com

Tail Sampling Processorはコントリビューションレポジトリで提供されているCollector用のプロセッサーの一つです。これはトレースの様々な条件をもとに、エクスポートするトレースをサンプリングしてくれるプロセッサーです。このプロセッサーのサンプリング用ポリシーの一つに latency というものがあって、これを使うことで一定時間以上のレイテンシーがかかっているトレースのみをバックエンドに送信する事が可能です。

実際に次のような設定をしてみます。(processors.tail_sampling.policies のところで、600ms以上のトレースのみをサンプリングする設定をしています。)

receivers:
  otlp:
    protocols:
      grpc:

processors:
  tail_sampling:
    decision_wait: 10s
    num_traces: 10
    policies:
      [
        {
          name: longer-than-600s,
          type: latency,
          latency: { threshold_ms: 600 },
        },
      ]

exporters:
  googlecloud:
    project: sample-project-1234
    retry_on_failure:
      enabled: true
    log:
      default_log_name: opentelemetry.io/collector-exported-log

extensions:
  memory_ballast:
    size_in_percentage: 30
  zpages:

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [tail_sampling]
      exporters: [googlecloud]
  telemetry:
    logs:
      level: "debug"
  extensions: [zpages, memory_ballast]

実際にこの設定をしたコレクターに対してトレースを投げてみます。意図的に600ms以上のトレースを3つ、続いて600ms未満のトレースを4つ生成してみると、次のようなログが表示されました。(これは service.telemetry.logs.leveldebug にしているから得られる表示で、通常は出てきません。)

2022-12-02T14:26:43.351+0900    debug   tailsamplingprocessor@v0.66.0/processor.go:202  Sampling policy evaluation completed    {"kind": "processor", "name": "tail_sampling", "pipeline": "traces", "batch.len": 3, "sampled": 3, "notSampled": 0, "droppedPriorToEvaluation": 0, "policyEvaluationErrors": 0}
...
2022-12-02T14:27:18.350+0900    debug   tailsamplingprocessor@v0.66.0/processor.go:202  Sampling policy evaluation completed    {"kind": "processor", "name": "tail_sampling", "pipeline": "traces", "batch.len": 4, "sampled": 0, "notSampled": 4, "droppedPriorToEvaluation": 0, "policyEvaluationErrors": 0}
...

このログを見ると、600ms以上かかっているトレースは "sampled": 3 として、サンプリングされたことがわかります。一方で、 600ms未満のトレースはどうだったかというと "notSampled": 4 と表示されていて、サンプリングされていないようです。

実際にこれが送信されたか、Cloud Traceのダッシュボードで確認してみましょう。

小さいのでアップしてみると

14:26:43の付近に800ms前後のトレースが3つ記録されているのがわかります!一方で、14:27:18付近には生成した4つの600ms未満のトレースは記録されていません。これはコレクターでドロップされて、エクスポートされなかったことを意味しています。

このようにTail Sampling Processorを使うと、トレースの中身(レイテンシーアトリビュート、ステータスなど)によって柔軟にフィルタリングが可能になります!

おわりに

OpenTelemetry Collectorはまだまだ実運用のノウハウが共有されていません。このような便利なプロセッサーやレシーバーの情報がどんどん共有されることをきたしています!

明日は @munisystem さんです。