「平均値」という名の落とし穴
DevOpsエンジニアになりたての頃、Grafanaダッシュボードに表示された「平均レイテンシ 100ms」という数字を喜んで眺めていたことがあります。すべてが正常(グリーン)で、システムは極めてスムーズに動いていると確信していました。しかし突然、サポートチームのグループチャットが炎上しました。「決済が遅すぎる、ずっと読み込み中のままだ!」と。
その時、私は致命的なミスに気づきました。平均100msという数字は、実は90人のユーザーには極めて速いレスポンス(10ms)を返し、残りの10人が2秒も待たされている結果だったのです。その10人こそが、今まさに怒っている顧客でした。up や rate といった基本的なコマンドだけを使っていると、この「無意味で美しい数字」の罠に永遠にハマることになります。
真実を見極めるには、PromQLのより強力な「武器」が必要です。システムの内側を鋭く分析する方法を紐解いていきましょう。
なぜ基本的なクエリではエラーを見逃してしまうのか?
rate(http_requests_total[5m]) のようなコマンドは、現在のリクエストレートを教えてくれるだけです。外れ値(Outliers)については完全に盲目です。
実際、ジュニアエンジニアのダッシュボードが信頼性に欠ける原因には、主に3つの問題があります。
- 平均値(Average)の乱用: 平均値は危険なスパイク(急上昇)を隠してしまいます。10秒間のサーバー停止も、50分間の安定稼働にかき消されてしまうのです。
- 時間の経過に伴う変化の喪失: リアルタイム의 データだけを見ていると、過去24時間でエラー率が最も高かった瞬間を特定するのが困難です。
- データのノイズ: 何百ものマイクロサービスを運用している場合、無計画に
sumを使うと、どのインスタンスが「不調に陥っている」のかを特定できなくなります。
Histogram Quantile: 実際のユーザー体験を測定する (P95, P99)
これは、私がレイテンシを測定する際に最も優先しているテクニックです。平均を計算する代わりに、histogram_quantile を使うことで、99%のユーザーが実際にどの程度の速度を体験しているかを正確に把握できます。
例えば、http_request_duration_seconds_bucket というメトリクスがあるとします。P99(99%のリクエストがこの時間内、またはそれより早く完了したこと)を計算するには、次のように記述します:
histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
仕組みは非常にシンプルです:
rate(...[5m]): 5分間における各バケットの増加率を計算します。sum by (le):le(less than or equal)ラベルごとにデータを集計します。これはHistogramにおいて極めて重要なラベルです。0.99: 調査したいパーセンタイル(しきい値)です。
もし結果が 0.5 であれば、99%のユーザーが500ms以下のレスポンスを受け取っていることを意味します。平均が100msのままでも、この数字が2sに跳ね上がっていれば、特定のユーザー層に対して深刻な問題が発生していることが確実に分かります。
Subquery: クエリの中のクエリ
「過去1時間の間に、エラー率が5%を超えた瞬間はあったか?」と考えたことはありませんか?通常の rate では、現在の数字しか見えません。Subquery(サブクエリ)を使えば、過去のデータを動的な配列のようにスキャンできます。
基本的な構文: <query> [<range>:<resolution>]。
例えば、過去1時間以内に発生した(5分間の)最大エラー率を見つけるには:
max_over_time(
rate(http_requests_total{status=~"5.."}[5m])[1h:1m]
)
ここで、[1h:1m] は「過去1時間のデータをスキャンし、1分ごとにサンプルを取得する」という意味です。このテクニックは、アラート(alert)の設定に非常に役立ちます。メトリクスが一時的に少し上昇してすぐに戻るような「フラッピング」による誤アラートを防ぐことができます。
Aggregation 通り越して “without” でスマートに集計
sum by (instance, pod, region) と長く書き連ねる代わりに、without を使ってみましょう。これは、不要なラベルを除外し、それ以外のすべてのラベルを保持することができます。
sum without (instance, pod) (rate(http_requests_total[5m]))
このコマンドはリクエストを合計しますが、method や endpoint といったラベルは保持されます。これにより、クエリの柔軟性が大幅に向上します。システムに az や version といった新しいラベルが追加されても、クエリを修正することなく自動的に対応できます。
プロフェッショナルなダッシュボード最適化のコツ
ダッシュボードを「美しく」かつ「高速」にするために、私はいつも3つの黄金律を適用しています:
- Recording Rules の活用:
histogram_quantileのような計算はCPU負荷が非常に高いです。ブラウザを更新するたびに計算させるのではなく、Prometheusに事前に計算させて新しいメトリクスとして保存しておきましょう。 - P50 – P95 – P99 のコンボ: 常にこれら3つの数字を並べて表示してください。それぞれの間隔がシステムの安定性を物語ります。これらが大きく乖離している場合、システムの一部でボトルネックが発生しています。
- カーディナリティ (Cardinality) の制御:
user_idのように値が頻繁に変わるものをラベルに割り当ててはいけません。Prometheusのデータが肥大化し、すぐにリソース不足でダウンしてしまいます。
PromQLをマスターすることは、単に「チャートを見る人」から、システムを「読み解く」エンジニアへと進化するための大きな一歩です。P99やSubqueryを取り入れてから、私は夜中にSSHでサーバーに入って推測に頼る必要がなくなりました。今ではすべてが「語りかける数字」として明確に見えています。

