何を捨てるかを決める仕事
一言でいうと
サンプリングはコストを減らす仕組みではなく、何を見るかを決める設計です。間違って決めると、肝心のトレースが残っていません。
なぜ必要なのか
トレースはリクエストごとに数十個のスパンを作ります。毎秒1,000リクエストなら、1日に数十億スパンになります。すべて保存すると、保存コストがアプリケーションのコストを超えます。
ところが、やみくもに1%だけ残すと、障害が起きたときそのリクエストのトレースがない確率は99%です。そのためサンプリングは「どれだけ減らすか」ではなく「何を必ず残すか」という視点で考えます。
3つの方式
| 方式 | いつ決めるか | 長所 | 代償 |
|---|---|---|---|
| ヘッド | リクエスト開始時 | 安くてシンプルで、伝播が簡単です | 遅いリクエストだけを選べません |
| テール | トレース完了後 | エラーや遅いものだけを正確に選べます | スパンをためておく必要があり、高価です |
| レートリミット | 毎秒N個を上限 | 急増してもコストが跳ねません | 急増区間のサンプルが少なくなります |
実務での組み合わせは、たいてい次のとおりです。
- ヘッドで10–20%に絞って基本ラインを下げます
- エラーは100%強制します(
traceparentのサンプルフラグを立てて伝播します) - 重要なエンドポイントだけテールサンプリングで、遅いものを拾います
サンプリングの決定は伝播させる必要がある
ここが最も間違いやすいところです。サンプリングはトレース単位で決める必要がありますが、各サービスがばらばらに決めると、トレースが断片になります。
프런트(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つが付いてきます。
- メモリ: 待ち時間(通常5–30秒)×毎秒のトレース数だけたまります
- ルーティング: 同じトレースのスパンは同じコレクターに届く必要があります。そのため、コレクターの前に
trace_id基準のロードバランサーを置きます。置かないとトレースが断片になり、判断が間違います
この2つを知らずにテールサンプリングを有効にすると、コレクターがOOMで落ち、落ちている間のトレースは丸ごと消えます。
よくある勘違い
「サンプリング率を上げればより正確になる」という考え: メトリクスはサンプリングと無関係である必要があります。リクエスト数・エラー率・レイテンシはメトリクスで測り、トレースは「そのリクエストがどこで時間を使ったか」を見るためのツールです。トレースで率を計算すると、サンプリングの偏りがそのまま入り込みます。
「エラーだけ残せばよい」という考え: エラーではない遅いリクエストのほうが、多くの情報をくれます。エラーはログにも残りますが、正常な応答なのに3秒かかったリクエストは、トレースがなければ説明できません。
実務で本当に大切なこと
サンプリングの決定は最初に1回行い、最後まで伝播させる必要があります。途中のサービスが自分の判断で決め直すと、トレースが半分だけ残ります。
traceparentの最後のフラグ(01/00)がその決定を運びます。自動計装はこれを守ってくれますが、自前でHTTPクライアントを作るコードがヘッダーを新しく書き込むと、その時点で決定がリセットされます。