ヒストグラムとサマリー
注このドキュメントはネイティブヒストグラム(Prometheus v2.40で実験的機能として追加)より前のものです。ネイティブヒストグラムが安定した機能に近づいた時点で、このドキュメントは完全に更新されます。
ヒストグラムとサマリーはより複雑なメトリックタイプです。1つのヒストグラムまたはサマリーが多数の時系列を作成するだけでなく、これらのメトリックタイプを正しく使用することもより困難です。このセクションでは、ユースケースに適したメトリックタイプを選択して設定するのに役立ちます。
ライブラリのサポート
まず、ヒストグラムおよびサマリーのライブラリサポートを確認してください。
一部のライブラリは、2つのタイプのうち1つのみをサポートするか、または限られた方法でサマリーをサポートします(クォンタイル計算が不足しています)。
観測値の数と合計
ヒストグラムとサマリーはどちらも観測値をサンプリングします。通常はリクエストの期間や応答サイズです。それらは観測値の数と観測された値の合計を追跡し、観測された値の平均を計算できるようにします。観測値の数(Prometheusでは`_count`サフィックスを持つ時系列として表示される)は本質的にカウンターです(上記のように、常に増加します)。観測値の合計(`_sum`サフィックスを持つ時系列として表示される)も、負の観測値がない限りカウンターのように動作します。明らかに、リクエストの期間や応答サイズは決して負になりません。ただし、原則として、サマリーとヒストグラムを使用して負の値(例: セ氏温度)を観測できます。その場合、観測値の合計は減少する可能性があり、`rate()`を適用できなくなります。`rate()`を適用する必要があり、負の観測値を避けられないまれなケースでは、正の観測値用と負の観測値用(後者は符号を反転させる)の2つの個別のサマリーを使用し、後で適切なPromQL式で結果を結合することができます。
過去5分間の`http_request_duration_seconds`というヒストグラムまたはサマリーから平均リクエスト期間を計算するには、次の式を使用します。
rate(http_request_duration_seconds_sum[5m])
/
rate(http_request_duration_seconds_count[5m])
Apdex スコア
ヒストグラム (サマリーではない) の直接的な使用法は、特定の観測値のバケットに分類される観測値を数えることです。
リクエストの95%を300ms以内に処理するという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式によるアドホック。 | 一般的に集計不可です。 |
表の最後の項目が重要であることに注目してください。リクエストの95%を300ms以内に処理するというSLOに戻りましょう。今回は、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のバケットに分類されます。ヒストグラムの実装は、真の95パーセンタイルが200msから300msの間に存在することを保証します。単一の値を返すために(区間ではなく)、線形補間が適用され、この場合は295msが得られます。計算されたクォンタイルは、SLOを侵害する寸前であるかのような印象を与えますが、実際には95パーセンタイルは220msをわずかに超える程度であり、SLOとはかなり余裕のある距離です。
思考実験の次のステップ:バックエンドルーティングの変更により、すべてのリクエスト期間に100msが固定で追加されます。これにより、リクエスト期間の鋭いスパイクは320msになり、ほとんどすべての観測値が300msから450msのバケットに分類されます。95パーセンタイルは442.5msと計算されますが、正しい値は320msに近いものです。SLOをわずかに超えているだけなのに、計算された95パーセンタイルははるかに悪く見えます。
サマリーであれば、どちらのケースでも正しいパーセンタイル値を計算するのに問題はなかったでしょう。少なくとも、クライアント側で適切なアルゴリズム(Goクライアントが使用しているもののような)を使用していれば。残念ながら、複数のインスタンスからの観測値を集計する必要がある場合、サマリーは使用できません。
幸いなことに、観測値の分布に非常に鋭いスパイクがあるという不自然な例であっても、バケット境界の適切な選択により、ヒストグラムはSLOの内側か外側かを正しく識別できました。また、クォンタイルの実際の値がSLO(つまり、私たちが実際に最も関心のある値)に近づくほど、計算された値はより正確になります。
実験をもう一度変更してみましょう。新しい設定では、リクエスト期間の分布は150msにピークがありますが、以前ほど鋭くはなく、観測値の90%しか占めていません。観測値の10%は、150msから450msの長いテールに均等に分布しています。この分布では、95パーセンタイルはちょうど300msのSLOに一致します。ヒストグラムでは、95パーセンタイルの値がバケット境界の1つと一致するため、計算された値は正確です。わずかに異なる値でも、関連するバケット内の(不自然な)均等な分布が、バケット内の線形補間が想定しているものと正確に一致するため、正確なままです。
サマリーによって報告される分位点誤差は、さらに興味深いものになります。サマリーにおける分位点誤差は、φの次元で設定されます。このケースでは、0.95±0.01、つまり計算された値が94パーセンタイルと96パーセンタイルの間になるように設定したかもしれません。上記の分布の場合、94パーセンタイルは270ms、96パーセンタイルは330msです。サマリーによって報告される95パーセンタイルの計算値は、270msから330msの範囲のどこにでもなり得ます。これは残念ながら、SLO内であるか、SLO外であるかという明確な違いをすべて含んでしまいます。
結論として、サマリーを使用する場合はφの次元で誤差を制御し、ヒストグラムを使用する場合は観測値の次元で誤差を制御します(適切なバケットレイアウトを選択することで)。広い分布では、φのわずかな変化が観測値の大きな偏差につながり、鋭い分布では、観測値の狭い区間がφの広い区間をカバーします。
二つの経験則
-
集計が必要な場合は、ヒストグラムを選択してください。
-
そうでない場合は、観測される値の範囲と分布がわかっているならヒストグラムを選択します。値の範囲と分布にかかわらず正確なクォンタイルが必要ならサマリーを選択します。
必要なメトリックタイプがクライアントライブラリでサポートされていない場合、どうすればよいですか?
実装してください!コードの貢献は歓迎します。一般的に、ヒストグラムはサマリーよりも緊急に必要とされていると考えています。また、ヒストグラムはクライアントライブラリで実装するのが容易であるため、迷っている場合はまずヒストグラムを実装することをお勧めします。