ヒストグラムとサマリー

このドキュメントは、ネイティブヒストグラム(Prometheus v2.40で実験的機能として追加)よりも前に作成されたものです。ネイティブヒストグラムが安定機能に近づいたら、このドキュメントは徹底的に更新されます。

ヒストグラムとサマリーは、より複雑なメトリックタイプです。単一のヒストグラムまたはサマリーは多数の時系列を作成するだけでなく、これらのメトリックタイプを正しく使用することもより困難です。このセクションでは、ユースケースに適したメトリックタイプを選択して設定する方法を説明します。

ライブラリのサポート

まず、ヒストグラムサマリーのライブラリサポートを確認してください。

一部のライブラリは、2つのタイプのいずれかのみをサポートしているか、またはサマリーを限定的な方法(分位数計算がないなど)でしかサポートしていません。

観測値のカウントと合計

ヒストグラムとサマリーはどちらも、通常はリクエストの所要時間やレスポンスサイズなどの観測値をサンプリングします。これらは観測値の数観測値の合計を追跡し、観測値の平均を計算できるようにします。観測値の数(Prometheusでは_countサフィックスの付いた時系列として表示される)は、本質的にカウンターです(上記で説明したように、増加するだけです)。観測値の合計(_sumサフィックスの付いた時系列として表示される)は、負の観測値がない限り、カウンターのように動作します。もちろん、リクエストの所要時間やレスポンスサイズが負になることはありません。原則として、サマリーとヒストグラムを使用して負の値(例:摂氏での温度)を観測することもできます。その場合、観測値の合計は減少する可能性があるため、rate()を適用できなくなります。rate()を適用する必要があり、負の観測値を避けることができないまれなケースでは、2つの別々のサマリー(1つは正の値用、もう1つは負の値用(後者は符号を反転させる))を使用し、後で適切なPromQL式で結果を結合することができます。

ヒストグラムまたはサマリーhttp_request_duration_secondsを使用して、過去5分間の平均リクエスト所要時間を計算するには、次の式を使用します。

  rate(http_request_duration_seconds_sum[5m])
/
  rate(http_request_duration_seconds_count[5m])

Apdexスコア

ヒストグラム(サマリーではない)の直接的な用途は、特定の観測値のバケットに入る観測値をカウントすることです。

「300ms以内に95%のリクエストを提供する」というSLOがあるかもしれません。その場合、ヒストグラムに上限0.3秒のバケットを設定します。そうすれば、300ms以内に提供されたリクエストの相対量を直接表現でき、値が0.95を下回った場合に簡単にアラートを発することができます。次の式は、過去5分間のリクエストのジョブごとの相対量を計算します。リクエストの所要時間は、http_request_duration_secondsという名前のヒストグラムで収集されました。

  sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job)
/
  sum(rate(http_request_duration_seconds_count[5m])) by (job)

広く知られているApdexスコアを同様の方法で近似できます。目標リクエスト時間を示す上限を持つバケットと、許容リクエスト時間(通常は目標リクエスト時間の4倍)を示す上限を持つ別のバケットを設定します。例: 目標リクエスト時間は300msです。許容リクエスト時間は1.2秒です。次の式は、過去5分間の各ジョブのApdexスコアを生成します。

(
  sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job)
+
  sum(rate(http_request_duration_seconds_bucket{le="1.2"}[5m])) by (job)
) / 2 / sum(rate(http_request_duration_seconds_count[5m])) by (job)

両方のバケットの合計を割っていることに注意してください。その理由は、ヒストグラムのバケットは累積的であるためです。le="0.3"バケットはle="1.2"バケットにも含まれているため、2で割ることでそれを補正します。

計算は従来のApdexスコアと正確には一致しません。なぜなら、計算の「満足」、「許容」部分にエラーが含まれるからです。

分位数

サマリーとヒストグラムの両方を使用して、いわゆるφ分位数(0 ≤ φ ≤ 1)を計算できます。φ分位数は、N個の観測値の中でφ*N番目の順位を持つ観測値です。φ分位数の例: 0.5分位数は中央値として知られています。0.95分位数は95パーセンタイルです。

サマリーとヒストグラムの根本的な違いは、サマリーはクライアント側でストリーミングφ分位数を計算して直接公開するのに対し、ヒストグラムはバケット化された観測値のカウントを公開し、ヒストグラムのバケットから分位数の計算はサーバー側でhistogram_quantile()関数を使用して行われることです。

これらの2つのアプローチには、いくつかの異なる影響があります。

ヒストグラムサマリー
必要な設定観測値の期待される範囲に適したバケットを選択してください。希望するφ分位数とスライディングウィンドウを選択してください。後から他のφ分位数やスライディングウィンドウを計算することはできません。
クライアントのパフォーマンス観測値は、カウンターをインクリメントするだけで済むため、非常に軽量です。ストリーミング分位数計算のため、観測値は高コストです。
サーバーのパフォーマンスサーバーは分位数を計算する必要があります。実行時計算が長すぎる場合(例: 大きなダッシュボードの場合)は、記録ルールを使用できます。サーバー側のコストは低いです。
時系列の数(_sumおよび_count時系列に加えて)設定されたバケットごとに1つの時系列。設定された分位数ごとに1つの時系列。
分位数の誤差(詳細は後述)誤差は、関連するバケットの幅によって、観測値の次元で制限されます。誤差は、設定可能な値によって、φの次元で制限されます。
φ分位数およびスライディング時間ウィンドウの指定Prometheus式によるアドホック。クライアントによって事前設定されます。
集計Prometheus式によるアドホック。一般的に集計不可

表の最後の項目が重要です。SLOの「300ms以内に95%のリクエストを提供する」に戻りましょう。今回は、300ms以内に提供されたリクエストの割合を表示するのではなく、95パーセンタイル、つまり95%のリクエストが提供されたリクエスト所要時間を表示したいとします。これを行うには、0.95分位数と(例えば)5分間の減衰時間を持つサマリーを設定するか、300ms付近にいくつかのバケットを持つヒストグラムを設定します。例: {le="0.1"}{le="0.2"}{le="0.3"}{le="0.45"}。サービスが複数のインスタンスでレプリケートされている場合、各インスタンスからリクエスト所要時間を収集し、それらをすべて全体的な95パーセンタイルに集計したいと思うでしょう。しかし、サマリーから事前計算された分位数を平均化することは、ほとんど意味がありません。この特定のケースでは、分位数の平均化は統計的に無意味な値をもたらします。

avg(http_request_duration_seconds{quantile="0.95"}) // BAD!

ヒストグラムを使用すると、histogram_quantile()関数を使用して集計を完全に実行できます。

histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) // GOOD.

さらに、SLOが変更され、90パーセンタイルを表示したい場合、または過去5分ではなく過去10分を考慮したい場合、上記の式を調整するだけでよく、クライアントの設定を再構成する必要はありません。

分位数の推定誤差

クライアント側またはサーバー側で計算された分位数は、推定値です。その推定の誤差を理解することが重要です。

上記のヒストグラムの例を続けます。通常の所要時間がほとんどすべて220msに非常に近いと想像してください。つまり、「真の」ヒストグラムをプロットできた場合、220msに非常に鋭いピークが表示されるでしょう。上記のように構成されたPrometheusヒストグラムメトリックでは、ほとんどすべての観測値、したがって95パーセンタイルも、{le="0.3"}というラベルのバケット、つまり200msから300msのバケットに fall します。ヒストグラムの実装は、真の95パーセンタイルが200msから300msの間にあることを保証します。単一の値(間隔ではなく)を返すために、線形補間が適用され、この場合は295msになります。計算された分位数は、SLOに近づいているという印象を与えますが、実際には95パーセンタイルは220msをわずかに上回っており、SLOからはかなり快適な距離にあります。

思考実験の次のステップ: バックエンドルーティングの変更により、すべてのリクエスト所要時間に固定で100msが追加されます。 now、リクエスト所要時間の鋭いピークは320msになり、ほとんどすべての観測値は300msから450msのバケットに fall します。95パーセンタイルは442.5msと計算されますが、正しい値は320msに近いです。SLOをわずかに外れていますが、計算された95パーセンタイルははるかに悪く見えます。

サマリーであれば、少なくともクライアント側で適切なアルゴリズム(Goクライアントで使用されているアルゴリズムなど)を使用していれば、両方のケースで正しいパーセンタイル値を計算できていたはずです。残念ながら、複数のインスタンスから観測値を集計する必要がある場合、サマリーを使用することはできません。

幸いなことに、バケット境界を適切に選択したため、観測値の分布における非常に鋭いピークという、この人為的な例でも、ヒストグラムはSLO内または外にあるかどうかを正しく識別できました。また、分位数の実際の値がSLOに近いほど(または、私たちが実際に最も関心のある値)、計算された値はより正確になります。

ここで、実験をもう一度変更しましょう。新しいセットアップでは、リクエスト所要時間の分布は150msにピークがありますが、以前ほど鋭くはなく、観測値の90%のみを構成します。観測値の10%は、150msから450msの間の長いテールに均等に分散されています。この分布では、95パーセンタイルは偶然にもSLOの300msと一致します。ヒストグラムでは、95パーセンタイルの値がバケット境界の1つと一致するため、計算された値は正確です。バケット内の(人為的な)均等な分布は、バケット内の線形補間が想定しているものと正確に一致するため、わずかに異なる値でも正確になります。

サマリーによって報告される分位数の誤差は、ここでさらに興味深いものになります。サマリーにおける分位数の誤差は、φの次元で設定されます。この場合、0.95±0.01を設定した可能性があります。つまり、計算された値は94番目から96番目のパーセンタイルの間になります。上記の分布における94番目の分位数は270ms、96番目の分位数は330msです。サマリーによって報告される95パーセンタイルの計算値は、270msと330msの間の間隔のどこにでもなる可能性があり、残念ながら、SLO内とSLO外を明確に区別できるすべてがその間になってしまいます。

結論: サマリーを使用する場合、φの次元での誤差を制御します。ヒストグラムを使用する場合、観測値の次元での誤差(適切なバケットレイアウトの選択を通じて)を制御します。広範な分布では、φの小さな変化が観測値の大きな偏差を引き起こします。鋭い分布では、観測値の小さな間隔がφの大きな間隔をカバーします。

2つの経験則

  1. 集計が必要な場合は、ヒストグラムを選択してください。

  2. それ以外の場合は、観測される値の範囲と分布のアイデアがある場合はヒストグラムを選択してください。範囲と分布に関係なく正確な分位数が必要な場合は、サマリーを選択してください。

クライアントライブラリで必要なメトリックタイプがサポートされていない場合はどうすればよいですか?

実装してください! コードの貢献を歓迎します。一般的に、ヒストグラムはサマリーよりも緊急に必要とされると予想されます。ヒストグラムはクライアントライブラリでの実装も容易なため、迷った場合はまずヒストグラムを実装することをお勧めします。

このページの内容