- 技術 💻
Prometheus Grafana入門|監視構築とPromQL一覧
Prometheus と Grafana とは、Prometheus がメトリクス(数値の時系列データ)を集めて保存・計算し、Grafana がそれをダッシュボードで見やすく表示する、監視の定番の組み合わせです。 Prometheus は対象のHTTPエンドポイント /metrics を定期的に取りに行く「Pull型」で、クエリ言語 PromQL で「1秒あたりのリクエスト数」「p95レイテンシ」などを計算できます。
この記事では、Prometheus 3.14.0 と node_exporter を実際に起動し、PromQL を HTTP API で実行した結果をそのまま掲載しています。Python の prometheus_client で自作メトリクスを公開してスクレイプし、アラートルールを promtool で検証・単体テストするところまで、コピペで試せる形でまとめました。Grafana は本環境で起動していないため、設定は公式ドキュメントに沿って記載し、未実行であることを明記しています。
PromQL早見表(実行結果つき)
| やりたいこと | PromQL | 実行結果の例 |
|---|---|---|
| 監視対象が生きているか | up |
1(正常)/0(取得失敗) |
| 1秒あたりの増加量(平均) | rate(app_requests_total[1m]) |
1.018 |
| 直近2点での瞬間の増加量 | irate(app_requests_total[1m]) |
1.2 |
| 期間内の増加数 | increase(app_requests_total[1m]) |
61.09 |
| ラベルごとに合計 | sum by (path) (rate(app_requests_total[1m])) |
/items 1.07 ほか |
| 上位N件 | topk(2, sum by (path) (rate(app_requests_total[1m]))) |
2系列 |
| エラー率 | sum(rate(app_requests_total{status=~"5.."}[1m])) / sum(rate(app_requests_total[1m])) |
0.045 |
| p95レイテンシ | histogram_quantile(0.95, sum by (le) (rate(app_request_duration_seconds_bucket[1m]))) |
0.877 秒 |
| 平均レイテンシ | sum(rate(..._sum[1m])) / sum(rate(..._count[1m])) |
0.311 秒 |
| CPU使用率(%) | 100 * (1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[1m]))) |
40.99 |
| メモリ使用率(%) | 100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) |
22.12 |
| ディスク使用率(%) | 100 * (1 - node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) |
96.38 |
| CPUコア数 | count(count by (cpu) (node_cpu_seconds_total)) |
2 |
| 期間内の最大値 | max_over_time(app_task_queue_length[1m]) |
19 |
| 系列が無いことを検知 | absent(up{job="batch"}) |
1(存在しない) |
| 24時間後の空き容量予測 | predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[1m], 3600*24) |
予測値(バイト) |
※数値は検証用コンテナでの一例です。本番では範囲を [5m] 程度にするのが一般的です(後述)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Prometheus Grafana入門|監視構築とPromQL一覧」
構成図と用語表
Prometheus は「取りに行って(scrape)、貯めて(TSDB)、計算して(PromQL)、条件を満たしたら知らせる(Alertmanager)」という流れで動きます。
flowchart LR
NE[node_exporter<br/>:7806/metrics<br/>OSのCPU・メモリ・ディスク] -->|scrape 5s| P
APP[自作アプリ<br/>prometheus_client<br/>:7807/metrics] -->|scrape 5s| P
subgraph P[Prometheus :7805]
TSDB[(TSDB<br/>時系列DB)]
RULE[ルール評価<br/>recording / alerting]
API[HTTP API<br/>/api/v1/query]
end
RULE -->|発火| AM[Alertmanager<br/>メール・チャット通知]
API -->|PromQL| G[Grafana :3000<br/>ダッシュボード]
| 用語 | 意味 |
|---|---|
| メトリクス | 名前とラベルを持つ数値の時系列。例:app_requests_total{path="/items"} |
| ラベル | 系列を分ける key=value。sum by (path) などで集計の軸になる |
| サンプル | ある時刻の1つの値 |
| Exporter | 既存ソフトの状態を /metrics 形式で公開するプログラム。例:node_exporter |
| スクレイプ(scrape) | Prometheus が /metrics を取りに行くこと |
| ジョブ/インスタンス | job はスクレイプ設定の単位、instance は接続先の host:port |
| TSDB | Prometheus 内蔵の時系列データベース |
| PromQL | Prometheus のクエリ言語 |
| Instant vector | ある時刻の値の集合。up など |
| Range vector | 時間幅つきの値の集合。[5m] を付けたもの。rate() などに渡す |
| Recording rule | よく使う式を事前計算して別名で保存するルール |
| Alerting rule | 条件を満たすとアラートを出すルール |
| Alertmanager | アラートのまとめ・抑制・通知を担う別プロセス |
| Grafana | 可視化ツール。Prometheus をデータソースとして登録して使う |
メトリクスの4つの型
| 型 | 性質 | 例 | よく使う関数 |
|---|---|---|---|
| Counter | 増える一方(再起動で0に戻る) | リクエスト数、エラー数 | rate irate increase |
| Gauge | 上下する | キュー長、メモリ使用量 | そのまま、avg_over_time max_over_time |
| Histogram | バケットごとの件数 | レイテンシ | histogram_quantile |
| Summary | クライアント側で分位数を計算 | レイテンシ | quantile ラベルで参照(集約は不可) |
Counter はそのまま見ても「累計」なので、必ず rate() か increase() を通して見るのが基本です。
バージョンの確認(2026年10月時点)
公式のリリース情報で確認した内容です。
| ソフト | 最新 | メモ |
|---|---|---|
| Prometheus | 3.14.0(2026-08-17) | 3.13 系が LTS(長期サポート)。本記事は 3.14.0 で検証 |
| Prometheus(Ubuntu 24.04 の apt) | 2.45.3 | apt install prometheus で入る版。3.x とは差がある(後述) |
| node_exporter(apt) | 1.7.0 | 本記事の検証版 |
| prometheus_client(Python) | 0.26.0 | pip install prometheus-client |
| Grafana | 13 系(13.0 は 2026-04-21 公開、13.1 も公開済み) | 本記事では未実行 |
apt で入る Prometheus は 2.45 系のため、3.x の機能を使いたい場合は公式サイトのバイナリを使います。
# 公式バイナリ(Linux x86_64)
curl -LO https://github.com/prometheus/prometheus/releases/download/v3.14.0/prometheus-3.14.0.linux-amd64.tar.gz
tar xzf prometheus-3.14.0.linux-amd64.tar.gz
./prometheus-3.14.0.linux-amd64/prometheus --version
prometheus, version 3.14.0 (branch: HEAD, revision: d7598b7141418fa35be2b5ec5d0fefb634199610)
# node_exporter は apt で入れた
sudo apt-get install -y prometheus-node-exporter
prometheus-node-exporter --version
node_exporter, version 1.7.0 (branch: debian/sid, revision: 1.7.0-1ubuntu0.3)
Prometheus 2.x と 3.x の違い
3.0 で既定の動作がいくつか変わったため、2.x 向けの記事のクエリや設定をそのまま使うと結果が少し変わることがあります。 公式の移行ガイドの要点です。
| 項目 | 2.x | 3.x |
|---|---|---|
| Web UI | 従来のUI | 新しいUIに刷新 |
| 機能フラグ | promql-at-modifier promql-negative-offset などをフラグで有効化 |
既定で有効になり、フラグは削除 |
正規表現の . |
改行にマッチしない | 改行にもマッチ |
範囲セレクタ [5m] |
両端を含む | 左端を含まない(左開・右閉区間) |
holt_winters |
使える | double_exponential_smoothing に改名(実験的機能フラグが必要) |
ヒストグラムの le、サマリーの quantile |
公開されたままの表記 | 取り込み時に浮動小数点表記へ正規化(1 → 1.0) |
| メトリクス名・ラベル名 | 英数字と _ : |
UTF-8 を許容 |
| Content-Type のない応答 | テキスト形式として扱う | スクレイプ失敗。fallback_scrape_protocol で指定 |
| Alertmanager | v1 API も可 | v2 API が必須(Alertmanager 0.16.0 以上) |
| ログ | go-kit/log | log/slog(ログの形式が変わる) |
| ダウングレード | — | 2.55 以上へのみ可能 |
範囲セレクタの変更により、[1m] のような短い範囲ではサンプル数が1つ減ることがあります。 スクレイプ間隔の4倍以上の範囲を指定しておくと安定します(例:間隔15秒なら [1m] 以上)。
手順1:自作メトリクスを公開する(Python)
アプリの中で数値を記録し、/metrics で公開するには公式クライアントライブラリを使います。 注文・会員・商品の3つのパスへリクエストが来る架空のAPIを想定しました。
pip install prometheus-client==0.26.0
# app.py
import random
import time
from prometheus_client import Counter, Gauge, Histogram, start_http_server
REQUESTS = Counter(
"app_requests_total", "処理したリクエスト数", ["method", "path", "status"]
)
LATENCY = Histogram(
"app_request_duration_seconds", "リクエスト処理時間(秒)", ["path"],
buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5),
)
QUEUE = Gauge("app_task_queue_length", "未処理タスク数")
if __name__ == "__main__":
start_http_server(7807) # http://localhost:7807/metrics
print("metrics on :7807", flush=True)
while True:
path = random.choice(["/orders", "/members", "/items"])
with LATENCY.labels(path=path).time():
time.sleep(random.uniform(0.01, 0.6))
status = "500" if random.random() < 0.05 else "200"
REQUESTS.labels(method="GET", path=path, status=status).inc()
QUEUE.set(random.randint(0, 20))
起動して数秒後の /metrics の中身(抜粋)です。
python app.py &
curl -s localhost:7807/metrics | grep '^app_'
# HELP app_requests_total 処理したリクエスト数
# TYPE app_requests_total counter
app_requests_total{method="GET",path="/items",status="200"} 7.0
app_requests_total{method="GET",path="/items",status="500"} 2.0
app_requests_total{method="GET",path="/orders",status="200"} 3.0
app_requests_total{method="GET",path="/members",status="200"} 4.0
# HELP app_requests_created 処理したリクエスト数
# TYPE app_requests_created gauge
app_requests_created{method="GET",path="/items",status="200"} 1.7908600727136035e+09
...
# HELP app_request_duration_seconds リクエスト処理時間(秒)
# TYPE app_request_duration_seconds histogram
app_request_duration_seconds_bucket{le="0.05",path="/items"} 0.0
app_request_duration_seconds_bucket{le="0.1",path="/items"} 0.0
app_request_duration_seconds_bucket{le="0.25",path="/items"} 3.0
app_request_duration_seconds_bucket{le="0.5",path="/items"} 9.0
app_request_duration_seconds_bucket{le="1.0",path="/items"} 9.0
app_request_duration_seconds_bucket{le="2.5",path="/items"} 9.0
app_request_duration_seconds_bucket{le="+Inf",path="/items"} 9.0
app_request_duration_seconds_count{path="/items"} 9.0
app_request_duration_seconds_sum{path="/items"} 2.7336251809998657
ポイントは3つです。
- Counter は名前の末尾に
_totalが付き、作成時刻を表す_createdも自動で出力される - Histogram は
_bucket(le=その値以下の件数、累積)・_count・_sumの3種類に展開される - ラベルに入れる値は種類が限られるもの(パス、ステータス)にする。会員IDや注文IDのような値を入れると系列数が増え続け、メモリを大きく使う
| ラベルにしてよい | ラベルを避けたい |
|---|---|
| HTTPメソッド、パス(テンプレート化したもの)、ステータスコード | ユーザーID、注文ID、セッションID |
| 環境名(dev/stg)、リージョン | タイムスタンプ、メールアドレス |
| ジョブ名・キュー名 | 生のURL(クエリ文字列つき) |
手順2:Prometheus を設定して起動する
スクレイプ先は prometheus.yml の scrape_configs に書きます。 今回は検証のため間隔を5秒にしました(本番では15〜60秒が一般的です)。
# prometheus.yml
global:
scrape_interval: 5s
evaluation_interval: 5s
rule_files:
- "rules.yml"
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:7805"]
- job_name: "node"
static_configs:
- targets: ["localhost:7806"]
- job_name: "app"
static_configs:
- targets: ["localhost:7807"]
labels:
env: "dev"
prometheus-node-exporter --web.listen-address=:7806 &
./prometheus-3.14.0.linux-amd64/prometheus \
--config.file=prometheus.yml \
--storage.tsdb.path=./data \
--web.listen-address=:7805 &
起動前に設定ファイルを検査できます。
promtool check config prometheus.yml
Checking prometheus.yml
SUCCESS: 1 rule files found
SUCCESS: prometheus.yml is valid prometheus config file syntax
Checking rules.yml
SUCCESS: 7 rules found
| よく使う起動オプション | 意味 |
|---|---|
--config.file |
設定ファイルのパス |
--storage.tsdb.path |
データの保存先 |
--storage.tsdb.retention.time=15d |
保存期間(既定15日) |
--web.listen-address=:9090 |
待ち受けポート(既定9090) |
--web.enable-lifecycle |
POST /-/reload で設定の再読み込みを許可 |
手順3:PromQL を HTTP API で実行する
PromQL は Web UI だけでなく、/api/v1/query に送ると JSON で結果が返ります。 スクリプトやテストから使えるので覚えておくと便利です。
curl -s --get 'http://localhost:7805/api/v1/query' \
--data-urlencode 'query=sum(rate(app_requests_total[1m]))'
{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1790860193.241,"3.218181818181818"]}]}}
value は [UNIX時刻, "値(文字列)"] です。以降は見やすいように、各系列のラベルと値だけを表示する小さなスクリプトを通しています。
# q.sh:PromQL を渡すと「ラベル 値」で表示する
q() { echo "### $1"; curl -s --get 'http://localhost:7805/api/v1/query' --data-urlencode "query=$1" | python3 -c '
import json,sys
d=json.load(sys.stdin)
for r in d["data"]["result"]:
print(r["metric"], r["value"][1])
'; }
for e in "$@"; do q "$e"; done
up:監視対象の死活
### up
{'__name__': 'up', 'env': 'dev', 'instance': 'localhost:7807', 'job': 'app'} 1
{'__name__': 'up', 'instance': 'localhost:7805', 'job': 'prometheus'} 1
{'__name__': 'up', 'instance': 'localhost:7806', 'job': 'node'} 1
up はスクレイプのたびに Prometheus が自動で作る系列です。 1なら取得成功、0なら失敗です。
rate・irate・increase:Counter の読み方
### rate(app_requests_total{path="/orders",status="200"}[1m])
{... 'path': '/orders', 'status': '200'} 1.018181818181818
### irate(app_requests_total{path="/orders",status="200"}[1m])
{... 'path': '/orders', 'status': '200'} 1.2
### increase(app_requests_total[1m])
{... 'path': '/items', 'status': '200'} 62.18181818181818
{... 'path': '/items', 'status': '500'} 2.1818181818181817
{... 'path': '/orders', 'status': '200'} 61.090909090909086
...
| 関数 | 計算 | 向いている用途 |
|---|---|---|
rate(x[5m]) |
範囲内の平均の毎秒増加量 | アラート、ダッシュボードの基本 |
irate(x[5m]) |
範囲内の最後の2点から毎秒増加量 | 変化の速いグラフを細かく見る |
increase(x[5m]) |
範囲内の増加数(= rate × 秒数) | 「直近5分で何件」 |
increase の結果が 61.09 のように小数になるのは、範囲の端を外挿(推定で補う)しているためです。整数にならなくても正常です。また、rate はカウンターのリセット(再起動で0に戻る)を自動で補正します。
sum by・topk:ラベルで集計する
### sum by (path) (rate(app_requests_total[1m]))
{'path': '/items'} 1.0727272727272728
{'path': '/orders'} 1.0363636363636362
{'path': '/members'} 1.109090909090909
### sum by (status) (increase(app_requests_total[1m]))
{'status': '200'} 184.36363636363635
{'status': '500'} 8.727272727272727
### topk(2, sum by (path) (rate(app_requests_total[1m])))
{'path': '/members'} 1.109090909090909
{'path': '/items'} 1.0727272727272728
順番は「rate してから sum」です。 rate(sum(...)) は書けず、sum してから rate するとカウンターのリセットを正しく扱えません。
| 集約演算子 | 意味 |
|---|---|
sum by (l) / sum without (l) |
指定ラベルで残す/指定ラベルを消して合計 |
avg min max count |
平均・最小・最大・件数 |
topk(n, x) / bottomk(n, x) |
上位・下位 n 件 |
count_values("v", x) |
値ごとの件数 |
quantile(0.9, x) |
系列をまたいだ分位数 |
histogram_quantile:p95・p50 レイテンシ
### histogram_quantile(0.95, sum by (le) (rate(app_request_duration_seconds_bucket[1m])))
{} 0.8770833333333332
### histogram_quantile(0.5, sum by (le, path) (rate(app_request_duration_seconds_bucket[1m])))
{'path': '/items'} 0.328125
{'path': '/orders'} 0.3317307692307693
{'path': '/members'} 0.26875000000000004
### sum(rate(app_request_duration_seconds_sum[1m])) / sum(rate(app_request_duration_seconds_count[1m]))
{} 0.31078916696044273
ここに分かりやすい落とし穴があります。 アプリは最大0.6秒しか待たないのに、p95 が 0.877 秒と出ました。これは histogram_quantile がバケットの中を直線で補間するためです。0.5〜1.0 秒のバケットに入った件数から「このあたりだろう」と推定するので、バケットの幅が広いと誤差が大きくなります。
| 状況 | 対処 |
|---|---|
| p95 が実測より大きく出る | 気にしたいしきい値の近くにバケットを細かく置く(例:0.5, 0.6, 0.75, 1.0) |
sum by (le) を忘れた |
系列ごとの値になる。分位数を計算する前に必ず le を残して合計する |
| パスごとに見たい | sum by (le, path) と le に加えて残す |
| 平均が欲しい | _sum の rate ÷ _count の rate |
CPU・メモリ・ディスク使用率(node_exporter)
### 100 * (1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[1m])))
{'instance': 'localhost:7806'} 40.99090909090912
### 100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)
{'instance': 'localhost:7806', 'job': 'node'} 22.121269456173177
### 100 * (1 - node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})
{'device': '/dev/vda', 'fstype': 'ext4', 'instance': 'localhost:7806', 'job': 'node', 'mountpoint': '/'} 96.38444091754714
### count(count by (cpu) (node_cpu_seconds_total))
{} 2
| 指標 | 式の考え方 |
|---|---|
| CPU使用率 | 「idle の割合」を1から引く。コアごとの値を avg by (instance) で平均 |
| メモリ使用率 | MemFree ではなく MemAvailable を使う(キャッシュ分を考慮した実際に使える量) |
| ディスク使用率 | avail(一般ユーザーが使える空き)÷ size。tmpfs などは fstype!~"tmpfs|overlay" で除外 |
| ネットワーク受信量 | rate(node_network_receive_bytes_total{device!="lo"}[5m]) |
| ロードアベレージ | node_load1 をコア数で割る |
Gauge・存在チェック・予測
### app_task_queue_length
{'__name__': 'app_task_queue_length', 'env': 'dev', 'instance': 'localhost:7807', 'job': 'app'} 5
### max_over_time(app_task_queue_length[1m])
{'env': 'dev', 'instance': 'localhost:7807', 'job': 'app'} 19
### absent(up{job="batch"})
{'job': 'batch'} 1
absent() は「その系列が存在しない」ときに 1 を返します。 設定漏れでスクレイプ自体が行われていないと up == 0 では検知できないため、重要なジョブには absent のアラートも用意すると安心です。
predict_linear() は直近の傾きから将来を予測します。検証では範囲が1分と短く、一時ファイルの増減で予測値が負の値になりました。予測系の関数には [6h] など長めの範囲を渡すと安定します。
PromQL 逆引き一覧
| やりたいこと | PromQL |
|---|---|
| ラベルの完全一致/否定 | x{job="app"} / x{job!="app"} |
| 正規表現一致/否定 | x{status=~"5.."} / x{path!~"/health.*"} |
| 1時間前と比べる | rate(x[5m]) / rate(x[5m] offset 1h) |
| 期間の平均・最大・最小 | avg_over_time(g[10m]) max_over_time min_over_time |
| 変化した回数 | changes(g[1h]) |
| Gauge の変化量 | delta(g[1h]) / deriv(g[1h]) |
| 小数を丸める | round(x, 0.1) |
| ラベルを付け替える | label_replace(x, "svc", "$1", "job", "(.*)") |
| 2つの式を結合(ラベルを合わせる) | a / on (instance) b / a * on (instance) group_left(nodename) node_uname_info |
| 値で絞る | x > 0.8(条件に合う系列だけ残る) |
| 0/1 にしたい | x > bool 0.8 |
| 一定時間の可用性(%) | avg_over_time(up[1d]) * 100 |
| サブクエリ | max_over_time(rate(x[5m])[1h:1m]) |
手順4:アラートルールを書いて promtool で検証する
アラートルールは「式」「継続時間(for)」「ラベル」「注釈」の4つで書きます。 以下はそのまま使える基本セットです。
# rules.yml
groups:
- name: node-alerts
rules:
- alert: InstanceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} が応答しません"
description: "job={{ $labels.job }} のスクレイプが1分以上失敗しています。"
- alert: HighCpuUsage
expr: 100 * (1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))) > 80
for: 10m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} のCPU使用率が80%超"
description: "現在値: {{ $value | printf \"%.1f\" }}%"
- alert: HighMemoryUsage
expr: 100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 90
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} のメモリ使用率が90%超"
- alert: DiskSpaceLow
expr: 100 * (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) > 85
for: 15m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} {{ $labels.mountpoint }} の空きが15%未満"
- name: app-alerts
rules:
- record: job:app_requests:rate5m
expr: sum by (job) (rate(app_requests_total[5m]))
- alert: HighErrorRate
expr: |
sum(rate(app_requests_total{status=~"5.."}[5m]))
/ sum(rate(app_requests_total[5m])) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "5xxエラー率が5%を超えています"
- alert: SlowRequestsP95
expr: histogram_quantile(0.95, sum by (le) (rate(app_request_duration_seconds_bucket[5m]))) > 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "p95レイテンシが0.5秒を超えています"
promtool check rules rules.yml
Checking rules.yml
SUCCESS: 7 rules found
括弧を閉じ忘れたルールを検査すると、行・列つきで教えてくれます。
# bad_rules.yml(直す前)
expr: rate(node_cpu_seconds_total{mode="idle"}[5m] > 0.8
Checking bad_rules.yml
FAILED:
bad_rules.yml: 5:15: group "bad", rule 1, "HighCpu": could not parse expression: 1:51: parse error: unclosed left parenthesis
# 直した後
expr: rate(node_cpu_seconds_total{mode="idle"}[5m]) > 0.8
アラートの状態を確認する
起動中の Prometheus に GET /api/v1/alerts で問い合わせた結果(状態・名前・値だけを抜粋)です。
pending HighCpuUsage 81.1
pending DiskSpaceLow /opt/claude-code 90.9
pending DiskSpaceLow / 96.4
pending DiskSpaceLow /var/lib/docker 96.4
pending SlowRequestsP95 0.9
条件を満たした直後は pending になり、for の時間ずっと続くと firing になって Alertmanager に送られます。 一瞬の値の跳ね上がりで通知が飛ばないようにするのが for の役割です。検証用コンテナはディスクが埋まり気味のため DiskSpaceLow が、補間の影響で p95 が高めに出るため SlowRequestsP95 が pending になっています。
| 状態 | 意味 |
|---|---|
| inactive | 条件を満たしていない |
| pending | 条件を満たしたが for の時間に達していない |
| firing | for を超えて継続。Alertmanager へ送信 |
promtool test rules でアラートを単体テストする
「up が0になったら3分後に InstanceDown が出るか」を、本物のサーバーなしで確かめられます。
# test_rules.yml
rule_files:
- rules.yml
evaluation_interval: 1m
tests:
- interval: 1m
input_series:
- series: 'up{job="app", instance="localhost:7807"}'
values: '1 1 0 0 0 0'
alert_rule_test:
- eval_time: 3m
alertname: InstanceDown
exp_alerts:
- exp_labels:
severity: critical
job: app
instance: localhost:7807
exp_annotations:
summary: "localhost:7807 が応答しません"
description: "job=app のスクレイプが1分以上失敗しています。"
promtool test rules test_rules.yml
SUCCESS
2分の時点で up が0になり、for: 1m を経た3分の時点で発火することを確認できました。ルールを変更したら check と test を CI で回すと、通知漏れを防げます。
手順5:Grafana でダッシュボードにする(未実行)
この節は本記事の検証環境では起動していません。Grafana 公式ドキュメントに沿った設定例です。
インストール(Ubuntu/Debian、公式APTリポジトリ)
sudo apt-get install -y apt-transport-https software-properties-common wget
sudo mkdir -p /etc/apt/keyrings/
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt-get update
sudo apt-get install -y grafana
sudo systemctl enable --now grafana-server
ブラウザで http://localhost:3000 を開き、初期ユーザー admin/パスワード admin でログインし、パスワードを変更します。
データソースをファイルで登録する(プロビジョニング)
画面で登録してもよいですが、YAML で管理すると環境を作り直しても同じ状態にできます。
# /etc/grafana/provisioning/datasources/prometheus.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://localhost:7805
isDefault: true
jsonData:
timeInterval: 5s # Prometheus の scrape_interval に合わせる
パネルの作り方のコツ
| パネル | クエリ例 | 設定のポイント |
|---|---|---|
| リクエスト数(時系列) | sum by (path) (rate(app_requests_total[$__rate_interval])) |
$__rate_interval を使うと範囲を自動調整 |
| エラー率(Stat) | エラー率の式 × 100 | Unit を Percent (0-100) |
| p95(時系列) | histogram_quantile(0.95, sum by (le) (rate(..._bucket[$__rate_interval]))) |
Unit を seconds |
| CPU・メモリ(Gauge) | 早見表の式 | しきい値 70/85 で色分け |
| 対象の死活(Stat) | up |
Value mappings で 1=UP、0=DOWN |
Grafana では [5m] と固定せず $__rate_interval を使うのが公式の推奨です。表示期間を広げても、スクレイプ間隔に対して範囲が短すぎる状態を防げます。node_exporter 用には、Grafana のダッシュボード公開サイトで配布されている既製ダッシュボード(「Node Exporter Full」など)をIDでインポートする方法も手早いです。
Grafana 13 では新しいダッシュボード形式(スキーマ v2)と動的ダッシュボードが標準になり、既存のダッシュボードは開いたときに自動で移行されます。
よくあるつまずきと対処
| 症状 | 原因 | 対処 |
|---|---|---|
Targets 画面で up=0、connection refused |
Exporter が起動していない/ポート違い | curl host:port/metrics で単体確認 |
rate() の結果が空 |
範囲内にサンプルが2つ未満 | 範囲をスクレイプ間隔の4倍以上に |
| グラフがギザギザ | irate を長い期間表示に使っている |
全体傾向は rate を使う |
| p95 が実測とずれる | バケットが粗い | しきい値付近にバケットを追加 |
| メモリ使用量が増え続ける | ラベルにIDなどを入れて系列が爆発 | ラベル値を有限にする |
| 3.x にしてからスクレイプが失敗 | 応答に Content-Type がない | fallback_scrape_protocol: PrometheusText0.0.4 を指定 |
| 2.x と結果が少し違う | 範囲セレクタが左開区間に変更 | 範囲を長めにとる |
よくある質問
Q. Prometheus だけでも監視できますか?Grafana は必須ですか? A. Prometheus 単体でも Web UI でグラフ表示やアラートができます。 Grafana は複数の指標を1画面にまとめたり、チームで共有したりするときに便利です。まず Prometheus で PromQL を確かめ、よく見る式を Grafana に並べる流れがおすすめです。
Q. rate と increase はどちらを使えばよいですか? A. アラートやグラフは「毎秒」で比べやすい rate、「直近1時間で何件」と件数で知りたいときは increase を使います。increase は rate に秒数を掛けたものなので、計算の中身は同じです。
Q. apt で入る Prometheus 2.45 のままでも学べますか? A. PromQL の基本(rate、sum by、histogram_quantile など)は同じように使えます。 ただし UI や一部の既定動作が 3.x と異なるため、新しく構築するなら公式バイナリの 3.x(長く使うなら LTS の 3.13 系)を選ぶとドキュメントと一致します。
Q. アプリのメトリクスはどの言語でも出せますか? A. Go・Java・Python・Ruby・.NET・Rust など主要な言語に公式またはコミュニティのクライアントがあります。 OpenTelemetry で計測して Prometheus 形式で公開する方法もあります。
まとめ
- Prometheus は Pull 型でメトリクスを集め、PromQL で計算する。Grafana はそれを見やすく並べる役
- Counter は rate/increase、Gauge はそのまま、Histogram は histogram_quantile で読む
- 「rate してから sum」、分位数は
sum by (le)を残すのが PromQL の基本形 - histogram_quantile はバケット内を補間するので、しきい値付近にバケットを置くと精度が上がる
- アラートは
forで pending→firing を制御し、promtool check / test で事前に検証できる - apt の Prometheus は 2.45、最新は 3.14.0。3.x は範囲セレクタや正規表現の既定動作が変わっている
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Prometheus Grafana入門|監視構築とPromQL一覧」
株式会社RJCでは、PrometheusとGrafanaによる監視の知識を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。