サンプリング
残すのはトレースの一部です。1 本のトレースの一部ではありません。
判断はルートで一度だけ行われます
トレースは、まるごと残すか、まるごと捨てるかです。判断はルートのスパンが 始まるときに行われ、すべての子に受け継がれるので、実行が半分だけ記録される ことはありません。
他のすべては、この規則から出てきます。スパンのうち無作為な 30% が欠けた トレースは、サンプリングされたトレースには読めません。観測している側の システムの不具合に読めます。実行されなかったように見えるツール呼び出し、 親のないモデル呼び出し、検索があるはずの場所の空白。その空白がエージェント ではなく可観測性の道具のせいだと分かるまでに、午後を 1 つ使うことになります。
既定はすべてを残します
サンプリングを設定しなければポリシーは always で、量が問題に
なるまでは、それが正しい既定です。サンプリングは、あなたが後で読みたく
なる実行を差し出す取引です。その代価は、ストレージか帯域がそうさせるとき
にだけ払ってください。
5 つの戦略
| 戦略 | 動作 |
|---|---|
always | すべてを残します。既定です。 |
never | 何も残しません。 |
ratio | 一定の割合を残します。ルートで、トレースごとに一度、無作為に引きます。 |
deterministic | 一定の割合を残します。トレース id のハッシュで決めます。どのコレクターも、他のどれとも通信せずに同じ答えにたどり着きます。 |
tail | トレースが終わるまで判断を先送りし、その後は割合の設定にかかわらず、すべてのエラーとすべての遅い実行を残します。 |
割合を設定する
--sample、または MCPD_SAMPLE は 0.0 から 1.0 の
割合を取り、決定的戦略を設定します。その範囲の外の値は、あなたが渡した値を
示すメッセージとともに、起動時にデーモンを停止させます。範囲に丸めることは
しません。打ち間違えた割合が黙って 1.0 になれば、それが分かるのは一か月後、
ストレージの請求書によってだからです。
10 本に 1 本のトレースを残す
mcpd --sample 0.1
どの SDK も同じ割合を読みます。クライアント設定の sample、
または環境変数の MCPD_SAMPLE です。クライアント側の
サンプリングのほうが安く済みます。サンプリングされなかったスパンはキューに
入らず、送出もされないので、帯域をまったく使いません。デーモン側の
サンプリングは往復分の費用がかかりますが、割合を一か所にまとめられます。
十数個のサービスが 1 つのインスタンスに送っている場合は、そちらが望ましい
でしょう。
5 つの戦略のうち、デーモンから届くのは always と
deterministic です。--sample と、設定ファイルの
プロジェクトの sample は、どちらも deterministic 戦略を設定
します。never、ratio、tail は
ポリシーの型の中に実装されていますが、このビルドではフラグからも設定
ファイルからも届きません。
ハッシュで多数のインスタンスの判断がそろう理由
ロードバランサーの後ろで mcpd を複数動かすと、1 つのトレースのスパンが 同じインスタンスに全部届くとはかぎりません。インスタンスごと、あるいは スパンごとにコインを投げるサンプラーは、トレースの一部を残して残りを 捨てます。最初の規則は、まさにその失敗を防ぐためにあります。
決定的戦略は、トレース id を 64 ビットの範囲全体に写し、その値が割合の示す しきい値を下回るときにトレースを残します。どのインスタンスも同じ id から 同じ数を計算するので、共有状態も、調整のためのプロトコルも、ずれる余地も ないまま、全員の判断が一致します。同じ id はリトライでも同じ答えを返すので、 再送されたバッチが半分だけ届くこともありません。
この写像を正直なものにしている細部が、2 つあります。1 つは、id の 末尾 16 桁の 16 進を読むことです。OpenTelemetry はトレース id が 乱数であることを求めていますが、先頭のバイトをタイムスタンプから作る生成側 もあり、そこを読むサンプラーは、サンプリングではなく時刻との相関を取って しまいます。もう 1 つは、id がそもそも 16 進でない場合の動きです。すべてを 残す動作に退化するのではなく、仕上げの撹拌を伴う本物のハッシュに フォールバックするので、実際の割合は、あなたが指定した割合の近くに とどまります。
SDK では上流の判断が優先されます
SDK が受け取った W3C の traceparent からトレースを続ける場合、
ID と一緒に sampled フラグを引き継ぎ、自分の割合は適用しません。これは
両方向に効きます。フラグが立っていればローカルの割合に関係なくトレースは
残され、フラグが下りていればローカルの割合に関係なく捨てられます。
このビルドのデーモンはこれを行いません。OTLP で届いたスパンについて、
デーモンはその trace-flags バイトを属性 mcpd.otlp.flags に
残しますが、デーモンのサンプラーはそれを読みません。割合を設定したデーモンは、
フラグが何を示していても、すべてのトレースにその割合を適用します。
そうしない場合の結果は、字面から受ける印象より深刻です。各サービスが自分の 割合を適用すれば、4 つのサービスをまたぐトレースは、2 つで残されて 2 つで 捨てられます。最後に手元に残るのは、分散トレースではありません。分散トレース の形をした穴の集まりです。
テールサンプリングは、読む価値のある実行を残します
ヘッドサンプラーは、何も分からないうちに判断します。安上がりですが、目が 見えていません。10 分の 1 なら、失敗も 10 分の 1 しか残しません。あなたが実際に 開くトレースはめったに起きないほうであり、めったに起きないものこそ、割合に よるサンプラーが最も苦手とするものです。
レイテンシの記録は、こういう見た目になります。平らな基準線と、いくつかの 逸脱です。量があるのは基準線のほうで、答えがあるのは逸脱のほうです。テール サンプリングはトレースが終わるまで保持し、その中で何かがエラーになって いれば残し、あなたが設定したしきい値より長く走っていれば残し、それ以外には 先ほどと同じ決定的ハッシュを適用します。エラーを残す動作は既定で有効です。 いちばん必要になるトレースはめったに起きないものであり、割合によるサンプラー は、それをいちばん捨てやすいからです。
代価はバッファリングです。テールサンプラーは、判断できるようになるまで、 終わっていないトレースをすべてメモリーに保持しなければなりません。だから これは既定ではなく、意図して選ぶものです。上に書いたとおり、テール戦略は ポリシーの型の中に実装されており、このビルドではフラグからも設定ファイル からも届きません。デーモンの取り込み経路が適用するのは、ヘッドの判断です。
何が捨てられたかを知る
サンプラーが捨てたスパンは、そのつど
mcpd_traces_sampled_out_total を増やします。この値は
/metrics に Prometheus のテキスト形式で出ています。割合は、
チームが読む場所に書いておいてください。サンプリング率が文書化されていれば、
トレースが無いことには説明が付きます。説明の無いトレース欠落はサポート依頼
になり、それを書くのは、たいていあなた自身です。