カミナシ エンジニアブログ

株式会社カミナシのエンジニアが色々書くブログです

Prometheusのメトリックタイプを徹底解説!使い分けでオブザーバビリティを高めよう

カミナシの認証認可ユニットでソフトウェアエンジニアをしているトモ=ロウです。

早速ですが皆さんPrometheusはご存知ですか?

Prometheusは、システムメトリクスの監視に特化したオープンソースのモニタリングシステムです。時系列データを収集・保存し、強力なクエリ言語PromQLを使って分析することで、システムの健全性を詳細に可視化できます。プル型(Prometheusサーバが監視対象のシステムからメトリックをポーリングする形式)を採用している点が特徴的です。

私のチームではPrometheusを使ってメトリクスの収集・分析を行なっています。Prometheusの利用を検討する際に多くの方が最初に感じるのは「メトリックタイプがよく分からん…」ではないでしょうか?少なくとも私はそうでした。公式ドキュメントを読んでも、一部の概念が難解に感じられたり、具体的なユースケースがイメージしづらかったりしました。

そこで、今回はその際に学んだことを皆さんにも共有し、同じような悩みを抱える方々の助けになればと思い、この記事を公開することにしました!

今回は、Prometheusが提供する4つのメトリックタイプ

について、それぞれの概念から具体的なユースケース、動作の仕組みを解説します。公式ドキュメントだけでは掴みにくい部分も、実際のデータ構造のイメージを交えながら掘り下げていきます。

本記事では必要に応じて各メトリックの具体的なデータ構造を記載しますが、それらは実際に Prometheus が扱うデータ構造そのものではなく、重要な値に絞って JavaScript のオブジェクト形式で記載します。


1. カウントするならこれ!Counter(カウンター)

Counterは、その名の通り何かを「数える」ことに特化したメトリックタイプです。例えば、ウェブサーバーへのリクエスト数や、アプリケーションで発生したエラー数など、時間が経つにつれて単調に増加していく値を計測するのに最適です。

Counterのデータ構造は非常にシンプルで、基本的には カウント値(countのみを持ちます。

// 1回目
{
  count: 1
}
// 2回目
{
  count: 15
}

このシンプルさゆえに、単一のデータポイントだけを見ても、その時点のカウント値しかわかりません。Counterを最大限に活用するには、複数のデータポイントを組み合わせて分析することが重要です。

カウンターがリセットされたらどうする?PromQLのrate関数が救世主!

ここで注意したいのが、サーバーの再起動などでCounterの値がリセットされる可能性がある点です。

// 1回目
{
  count: 1000 // 再起動前の値
}
// 2回目
{
  count: 5    // 再起動後の値(リセットされた)
}

もし2回目のデータポイントだけを見ると、「カウントが5しかない」と誤解してしまいますよね。このような問題は、PromQLの強力な関数であるrate関数を使うことで解決できます。

rate関数は、指定した時間窓における単位時間あたりの増加量を計算してくれる優れものです。Counterのリセットを自動的に考慮してくれるため、常に正確な増加量を得ることができます。

例えば、以下のような時系列データがあったとします。

1回目 2回目 3回目 4回目 5回目
100 130 170 10 50

3回目と4回目の間で値が大きく減少していることから、リセットが発生したことがわかります。rate関数はこれを検知し、あたかもリセットがなかったかのように値を補正して計算してくれます。

1回目 2回目 3回目 4回目 5回目
100 130 170 180 220

もし各データポイントの間隔が30秒であれば、rate関数によって得られる結果は(220 - 100) / (30 * 4) = 1となります。このrate関数は、他のメトリックタイプでも非常に重要になるので、ぜひ覚えておきましょう!


2. その瞬間の値を測るなら!Gauge(ゲージ)

Gaugeメトリックは、その瞬間の特定の値を計測したい場合に利用します。Counterが「積算値」を扱うのに対し、Gaugeは「現在の値」を表します。

例えば、同時処理されているリクエスト数や、現在のCPU使用率ディスクの空き容量など、上がったり下がったりする可能性のある値を継続的に観測するのに適しています。

// 同時リクエスト数
// 1回目
{
  value: 10
}
// 2回目
{
  value: 8
}

GaugeはCounterと異なり、1つのデータポイントだけで求める情報が得られることが多いのが特徴です。もちろん、複数のデータポイントから単位時間あたりの増減を計算することも可能です。


3. 分布を推定するなら!Histogram(ヒストグラム)

※ PrometheusのHistogramにはNative HistogramとClassic Histogramがありますが、Native Histogramが2025年6月現在でExperimental feature扱いのため、本記事ではClassic Histogramを扱います。

Histogramは、特定の時間窓における値の分布を把握したい場合に真価を発揮します。例えば、直近1分間のレスポンスタイムの平均値だけでなく、 パーセンタイル値(P95やP99など)を知りたい場合に非常に有用です。

Histogramの素晴らしい点は、少ないデータポイントからでも高い解像度で分布を推定できることにあります。

Histogramは、計測対象の合計値であるsum、計測された回数の合計値であるcount、そして特定の閾値(バケット)ごとに観測値を数えたbucketsというデータ構造を持ちます。

// レスポンスタイム(ミリ秒)
// 1回目
{
  sum: 8,   // 計測値の合計
  count: 1, // 計測回数
  buckets: {
    "le5": 0,    // 5ms以下の観測数
    "le7.5": 0,  // 7.5ms以下の観測数
    "le10": 1,   // 10ms以下の観測数
    "+Inf": 1,   // 無限大以下の観測数(つまり全観測数)
  }
}
// 2回目
{
  sum: 43,
  count: 5,
  buckets: {
    "le5": 3,
    "le7.5": 3,
    "le10": 4,
    "+Inf": 5,
  }
}

これらの値もCounterと同様にリセットを考慮する必要があるため、PromQLのrate関数と組み合わせて利用することが推奨されます。

上記の例で、1回目の観測と2回目の観測の間に処理されたリクエストの平均処理時間を計算してみましょう。

Δsum/Δcount で計算できるため、(43 - 8) / (5 - 1) = 35 / 4 = 8.75ミリ秒という結果が得られます。

また、bucketsの情報からは、1回目以降に

  • 5ms以下のリクエストが3件
  • 10msより大きいリクエストが1件

処理されたことがわかります。Histogramはこれらの情報を用いてパーセンタイル値を推定します。

精度の高い推定のために、線形補完ロジックを理解しよう!

パーセンタイル値の推定は、ヒストグラムのバケット情報から、各観測値が均等に分布していると仮定して線形補完を行うことで実現されます。この「線形補完」のロジックを理解することが、ヒストグラムの推定精度を理解する上で非常に重要です。

Prometheusは、与えられたバケットの閾値と、そのバケットに含まれる観測数(count)から、具体的なパーセンタイル値を推定します。具体的な計算ロジックを見てみましょう。

※以下の例は2025年6月17日時点での実装を参考にしています。

例として、先ほどのレスポンスタイムのデータで、1回目の観測と2回目の観測の間に処理された4つのリクエスト(Δcount = 4)のパーセンタイル値(例えばP50)を推定する場合を考えます。

  1. 求めるパーセンタイルの位置を特定: 4つのリクエストをレスポンスタイムの昇順で並べた際に、50パーセントに位置するリクエストは2番目のリクエストです。
  2. 該当するバケットを特定: 2番目のリクエストがどのバケットに含まれるかをbuckets情報から判断します。1回目と2回目のバケットの値の差分を取ると、以下となります。

    • le5: 3件
    • le7.5: 3件
    • le10: 3件
    • +Inf: 4件

    これらの情報から、P50(2番目のリクエスト)は、le5バケット(観測数3件)に含まれることがわかります。つまり、2番目のリクエストのレスポンスタイムは0msから5msの間に存在します。

  3. 線形補完の適用: 0msから5msの間に3つのリクエストが均等に分布していると仮定し、線形補完を行います。

    • 3つのリクエストをこの範囲に均等に割り振ると、それぞれの境界は 5ms / 3 = 1.6666...ms となります。
    • つまり、
      • 1番目のリクエストは 約1.66ms
      • 2番目のリクエストは 約3.33ms
      • 3番目のリクエストは 5ms
    • したがって、P50は3.3333…msと推定されるわけです。

この計算ロジックからもわかるように、バケットの閾値が粗すぎると、推定されるパーセンタイル値と実際の値との乖離が大きくなる可能性があります。

例えば、上記の例において、3つの5ms以下のリクエストの実際のレスポンスタイムが

  • 4.8ms
  • 4.9ms
  • 5.0ms

だった場合、本来のP50値は4.9msとなり、推定された3.3333…msから大きく外れてしまいます。ここに仮に le4 というより細かい閾値のバケットが存在した場合、推定値は 4.6666…ms のようになり、より精度の高い結果が得られるという仕組みです。

Prometheusでは histogram_quantile 関数を用いてこれらの推定値を取得できます。この関数の内部ロジックは、上記のような線形補完の考え方に基づいています。


4. クライアントサイドでパーセンタイル計算!Summary(サマリー)

SummaryメトリックもHistogramと同様に、値の分布(特にパーセンタイル値)を扱いたいケースで利用できます。しかし、Histogramとはいくつか大きな違いがあります。

主な違いは以下の2点です。

  • Summary は quantile の計算をメトリックを生成するクライアントサイド(アプリケーション側)で事前に計算する
  • Summaryは複数の時系列データを跨いだ集計ができない

特に後者の「複数の時系列データを跨いだ集計ができない」という点が、Summaryの利用シーンを制限する大きな要因となります。これは、異なるラベルが付与された時系列データ全体でのパーセンタイル計算が難しいことを意味します。

例えば、/lightweight/heavyweightという2つのエンドポイントに対して集計されるSummaryメトリックで、両エンドポイント横断のP95レスポンスタイムを計算したい場合を考えてみましょう。

avg(http_request_duration_seconds{quantile="0.95"})のようなクエリを使うことになりますが、その場合もし/lightweightのP95が5ms、/heavyweightのP95が500msだったとすると、結果は(5 + 500) / 2 = 252.5msとなります。これは、実際のリクエスト数の分布によっては、現実のP95値から大きく乖離してしまう可能性があります。

Summaryは、

  • 送信する時系列データの数を減らせる
  • パーセンタイル推定値の精度がHistogramと比較して高い(クライアントサイドで直接計算するため)

といったメリットも持っています。

しかし、現代のコンテナ環境で水平スケーリングが行われるシステムにおいては、個々のインスタンスで計算されたパーセンタイル値を単純に平均しても意味がないケースが多く、複数のインスタンスを横断して集計できるHistogramの方が使い勝手が良い場面が多いのではないかなと思います。


まとめ:メトリックタイプを理解して、より深い洞察を!

今回はPrometheusの4つのメトリックタイプ、Counter、Gauge、Histogram、Summaryについて解説しました。

メトリックタイプ 主な用途 特徴 注意点
Counter リクエスト数、エラー数など、単調増加する値 rate関数でリセットを考慮した増加量を算出できる 単一データポイントの評価は推奨しない(リセットの影響を受けるため)
Gauge CPU使用率、同時リクエスト数など、変動する現在の値 その時点の値を取得できる
Histogram レスポンスタイムなど、値の分布(平均、パーセンタイル) 少ないデータポイントから分布を推定可能、bucketsで詳細な分布情報を持つ 適切なbuckets設定が精度に直結。rate関数との組み合わせが重要
Summary レスポンスタイムなど、値の分布(パーセンタイル) クライアントサイドでパーセンタイル計算、データ量を削減可能 複数の時系列データを跨いだ集計が難しい。利用シーンは限定的

それぞれのメトリックタイプを適切に使い分けることで、システムの状態をより正確に、そして多角的に把握できるようになります。この記事が少しでも皆さんの役に立てば嬉しいです。

Prometheusのメトリックタイプについて、さらに詳しく知りたい方は、公式ドキュメントもぜひ参照してみてください。