TT Lab
はじめる
学ぶ 学習パス コース

分散トレーシングが切れる場所

何を捨てるかを決める仕事

TT Labで続きを見る

一言でいうと

サンプリングはコストを減らす仕組みではなく、何を見るかを決める設計です。間違って決めると、肝心のトレースが残っていません。

なぜ必要なのか

トレースはリクエストごとに数十個のスパンを作ります。毎秒1,000リクエストなら、1日に数十億スパンになります。すべて保存すると、保存コストがアプリケーションのコストを超えます。

ところが、やみくもに1%だけ残すと、障害が起きたときそのリクエストのトレースがない確率は99%です。そのためサンプリングは「どれだけ減らすか」ではなく「何を必ず残すか」という視点で考えます。

3つの方式

方式 いつ決めるか 長所 代償
ヘッド リクエスト開始時 安くてシンプルで、伝播が簡単です 遅いリクエストだけを選べません
テール トレース完了後 エラーや遅いものだけを正確に選べます スパンをためておく必要があり、高価です
レートリミット 毎秒N個を上限 急増してもコストが跳ねません 急増区間のサンプルが少なくなります

実務での組み合わせは、たいてい次のとおりです。

  1. ヘッドで10–20%に絞って基本ラインを下げます
  2. エラーは100%強制します(traceparentのサンプルフラグを立てて伝播します)
  3. 重要なエンドポイントだけテールサンプリングで、遅いものを拾います

サンプリングの決定は伝播させる必要がある

ここが最も間違いやすいところです。サンプリングはトレース単位で決める必要がありますが、各サービスがばらばらに決めると、トレースが断片になります。

프런트(20%) → API(20%) → 결제(20%)

각자 정하면 세 서비스가 모두 남길 확률 = 0.2³ = 0.8%
→ 완전한 트레이스는 거의 안 남고, 조각난 스팬만 쌓인다

W3Cのtraceparentヘッダーの最後のバイトがサンプルフラグです。最初に1回決めて、後ろではその決定に従うだけにする必要があります。

traceparent: 00-4bf92f...36-00f067aa0ba902b7-01
                                              ↑ 01 = 샘플됨, 00 = 아님

OpenTelemetry SDKのParentBased(root=TraceIdRatioBased(0.2))がこれを実装しています。親がいれば親の決定に従い、いない場合(ルートの場合)だけ20%のサイコロを振ります。TraceIdRatioBasedだけを使うとサービスごとに別々に振ってしまい、上の0.8%の問題が生じます。

TraceIdRatioBasedがランダムではなくtrace IDのハッシュで決める理由もここにあります。同じtrace IDはどのサービスで計算しても同じ答えになるため、設定が同じなら決定が自然に一致します。

エラーを100%残す方法

「エラーはすべて残す」という言葉は簡単ですが、ヘッドサンプリングでは不可能です。リクエストの開始時には、それが失敗するかどうかわからないからです。回避策が2つあります。

アプリケーションがフラグを立てる: エラーを検知した時点で、現在のスパンを強制的に記録対象として印を付けます。すでに過ぎた親スパンは救えませんが、その下は残ります。

テールサンプリングを使う: コレクターがトレース全体を見てから決めるため、正確です。ポリシーは次のような形です。

tail_sampling:
  decision_wait: 10s
  policies:
    - name: errors            # 오류는 전부
      type: status_code
      status_code: {status_codes: [ERROR]}
    - name: slow              # 2초 넘는 것은 전부
      type: latency
      latency: {threshold_ms: 2000}
    - name: baseline          # 나머지는 5%
      type: probabilistic
      probabilistic: {sampling_percentage: 5}

ポリシーはORで評価されます。1つでも当てはまれば残します。

テールサンプリングの隠れたコスト

コレクターがトレース全体をメモリにためておいてから判断します。そのため、次の2つが付いてきます。

この2つを知らずにテールサンプリングを有効にすると、コレクターがOOMで落ち、落ちている間のトレースは丸ごと消えます。

よくある勘違い

「サンプリング率を上げればより正確になる」という考え: メトリクスはサンプリングと無関係である必要があります。リクエスト数・エラー率・レイテンシはメトリクスで測り、トレースは「そのリクエストがどこで時間を使ったか」を見るためのツールです。トレースで率を計算すると、サンプリングの偏りがそのまま入り込みます。

「エラーだけ残せばよい」という考え: エラーではない遅いリクエストのほうが、多くの情報をくれます。エラーはログにも残りますが、正常な応答なのに3秒かかったリクエストは、トレースがなければ説明できません。

実務で本当に大切なこと

サンプリングの決定は最初に1回行い、最後まで伝播させる必要があります。途中のサービスが自分の判断で決め直すと、トレースが半分だけ残ります。

traceparentの最後のフラグ(01/00)がその決定を運びます。自動計装はこれを守ってくれますが、自前でHTTPクライアントを作るコードがヘッダーを新しく書き込むと、その時点で決定がリセットされます。