この記事の要点
• Prometheusのアーキテクチャとメトリクス収集の仕組み
• PromQLの基本クエリとアラートルールの設定方法
• Alertmanagerとの連携によるインシデント通知
このチュートリアルで学ぶこと
- ✓ Prometheusのアーキテクチャと概念
- ✓ Dockerでのセットアップ
- ✓ Node Exporter / アプリからのメトリクス公開
- ✓ PromQLの基本
- ✓ 記録ルール (Recording Rules) とアラート
- ✓ Alertmanagerとの連携
前提条件
- Linux / コマンドラインの基礎
- Docker と Docker Compose の基礎
- HTTP / JSON の基本
- 監視・オブザーバビリティの基本概念
基本概念
ポイント: PrometheusはPull型モデルを採用しており、監視対象のサービスにPrometheus自身がアクセスしてメトリクスを取得します。Push型と異なり、監視対象側にエージェントを常駐させる必要がありません。
Prometheusは以下の特徴を持つオープンソース監視システムです。
- Pull型モデル (Prometheus がターゲットをスクレイプ)
- 多次元データモデル (メトリクス名 + ラベル)
- PromQL という強力なクエリ言語
- ローカル時系列DB (TSDB)
- サービスディスカバリ対応
メトリクスの種類
- Counter: 単調増加 (リクエスト総数, エラー総数)
- Gauge: 増減する値 (CPU使用率, メモリ, キュー長)
- Histogram: レイテンシ等の分布 (bucket)
- Summary: Histogramと類似、クライアント側で計算
プロジェクトのセットアップ
mkdir prometheus-demo && cd prometheus-demo
touch docker-compose.yml prometheus.yml alert.rules.yml alertmanager.yml
docker-compose.yml
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--web.enable-lifecycle"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./alert.rules.yml:/etc/prometheus/alert.rules.yml
- prom_data:/prometheus
ports:
- "9090:9090"
node_exporter:
image: prom/node-exporter:latest
container_name: node_exporter
ports:
- "9100:9100"
alertmanager:
image: prom/alertmanager:latest
container_name: alertmanager
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
ports:
- "9093:9093"
volumes:
prom_data:
Step 1: 基本的な prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:9093"]
rule_files:
- "alert.rules.yml"
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: "node"
static_configs:
- targets: ["node_exporter:9100"]
- job_name: "app"
metrics_path: "/metrics"
static_configs:
- targets: ["host.docker.internal:3000"]
起動:
docker compose up -d
# http://localhost:9090 でPrometheusのUI
# http://localhost:9100/metrics でNode Exporterの出力
Step 2: PromQL の基本
PromQLはPrometheus独自のクエリ言語で、メトリクスの集計・フィルタリング・演算を柔軟に行えます。
# 現在のCPU使用率 (各ノード)
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# メモリ使用率
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
# リクエストの1分間スループット
rate(http_requests_total[1m])
# ステータスコード別の5xx比率
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))
# 上位5インスタンスのCPU
topk(5, 100 - rate(node_cpu_seconds_total{mode="idle"}[5m]) * 100)
Step 3: Node.jsアプリからメトリクスを公開
mkdir app && cd app
npm init -y
npm install express prom-client
// app/server.js
const express = require("express");
const client = require("prom-client");
const app = express();
const register = new client.Registry();
client.collectDefaultMetrics({ register });
const httpRequests = new client.Counter({
name: "http_requests_total",
help: "Total HTTP requests",
labelNames: ["method", "route", "status"],
});
register.registerMetric(httpRequests);
const httpDuration = new client.Histogram({
name: "http_request_duration_seconds",
help: "HTTP request latency",
labelNames: ["method", "route", "status"],
buckets: [0.05, 0.1, 0.3, 0.5, 1, 2, 5],
});
register.registerMetric(httpDuration);
app.use((req, res, next) => {
const end = httpDuration.startTimer({ method: req.method, route: req.path });
res.on("finish", () => {
httpRequests.inc({
method: req.method,
route: req.path,
status: String(res.statusCode),
});
end({ status: String(res.statusCode) });
});
next();
});
app.get("/", (req, res) => res.send("hello"));
app.get("/slow", async (req, res) => {
await new Promise((r) => setTimeout(r, 500));
res.send("done");
});
app.get("/metrics", async (req, res) => {
res.set("Content-Type", register.contentType);
res.end(await register.metrics());
});
app.listen(3000, () => console.log("http://localhost:3000"));
起動後 http://localhost:3000/metrics でメトリクス出力を確認できます。
Step 4: 記録ルール
# alert.rules.yml 内で記録ルールも定義可能
groups:
- name: recording.rules
interval: 30s
rules:
- record: job:http_requests:rate5m
expr: sum by (job) (rate(http_requests_total[5m]))
- record: job:http_errors:ratio5m
expr: |
sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))
/
sum by (job) (rate(http_requests_total[5m]))
実践メモ: 記録ルールで頻繁に使うクエリを事前計算しておくと、ダッシュボードの表示が高速になり、Prometheusの負荷も軽減されます。
Step 5: アラートルール
# alert.rules.yml (続き)
- name: alerts
rules:
- alert: HighErrorRate
expr: job:http_errors:ratio5m > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.job }} の 5xx 率が高い"
description: "5分間の5xx比率が {{ $value | humanizePercentage }} を超えました"
- alert: InstanceDown
expr: up == 0
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} がダウンしています"
- alert: HighCPU
expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 10m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} のCPU使用率が85%超"
設定再読み込み:
curl -X POST http://localhost:9090/-/reload
Step 6: Alertmanager 設定
# alertmanager.yml
route:
receiver: "default"
group_by: ["alertname", "severity"]
group_wait: 30s
group_interval: 5m
repeat_interval: 1h
routes:
- matchers:
- severity="critical"
receiver: "critical-receiver"
receivers:
- name: "default"
webhook_configs:
- url: "http://example.com/webhook"
send_resolved: true
- name: "critical-receiver"
webhook_configs:
- url: "http://example.com/critical"
send_resolved: true
inhibit_rules:
- source_matchers:
- severity="critical"
target_matchers:
- severity="warning"
equal: ["alertname", "instance"]
完成: ダッシュボード用クエリ集
# サービスごとのQPS
sum by (job) (rate(http_requests_total[1m]))
# P95 レイテンシ
histogram_quantile(
0.95,
sum by (le, route) (rate(http_request_duration_seconds_bucket[5m]))
)
# メモリ上位コンテナ (kube state想定)
topk(10, container_memory_working_set_bytes{namespace!=""})
# ディスク残量 (10%未満)
(node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 < 10
# 過去24hで一度でもdownしたインスタンス
max_over_time(up[24h]) == 0
注意: ハイカーディナリティ(ラベルの組み合わせ爆発)はPrometheusの最大の落とし穴です。ユーザーIDやリクエストIDなどの高基数値をラベルにしないことが鉄則です。
よくあるエラーと対処
-
“context deadline exceeded”
- scrape_timeout を緩める、ターゲットの /metrics 応答を確認
-
host.docker.internal が解決できない (Linux)
- extra_hosts: “host.docker.internal:host-gateway” を追加
-
TSDBの肥大化
- —storage.tsdb.retention.time でリテンションを設定
- 長期保存はリモートストレージ (Thanos, Mimir等)
-
アラートが飛ばない
- /alerts ページで PENDING → FIRING を確認
- Alertmanager のログ、ネットワーク到達性を確認
-
ハイカーディナリティ
- ラベルにユーザーIDやリクエストIDを入れない
- ラベルの組み合わせ爆発に注意
ベストプラクティス
ポイント: 4 Golden Signals(Latency, Traffic, Errors, Saturation)を軸に監視設計を行うことで、サービスの健全性を包括的に把握できます。
- 4 Golden Signals (Latency, Traffic, Errors, Saturation) を軸に設計
- ラベル数は厳選しカーディナリティを抑制
- アラートはアクションに繋がるものだけ (アラート疲れを防ぐ)
- 記録ルールで高頻度クエリを事前計算
- ダッシュボードはGrafanaで可視化
- メトリクス、ログ、トレースの3本柱で相補的に監視
次のステップ
- Grafana でのダッシュボード作成
- Thanos / Mimir による長期保存とマルチクラスタ
- kube-prometheus-stack (Helm) でKubernetesを監視
- OpenTelemetry との統合
- SLI/SLO 設計と Error Budget
まとめ
PrometheusはPullベースで扱いやすく、PromQLの表現力が高く、エコシステムも充実しています。 Node Exporter とアプリのインストゥルメント、適切なアラート設計を組み合わせることで、 本番環境での異常を早期に検知できる監視基盤を構築できます。
FAQ
Q. Pull型とPush型、Prometheusはどちら?
A. 基本はPull型です。Prometheus自身がターゲットに /metrics をスクレイプしに行きます。
バッチジョブ等のPushが必要な場合は Pushgateway を経由させます。
Q. 長期保存はどうする? A. Prometheus単体では長期保存に不向きです。Thanos / Mimir / Cortex などのリモートストレージ統合を 検討してください。
Q. Kubernetesで使うには? A. kube-prometheus-stack (Helmチャート) を使うと Prometheus, Alertmanager, Grafana, Node Exporter, kube-state-metrics などがワンセットで導入できます。
Q. ハイカーディナリティとは? A. ラベルの組み合わせ数が爆発的に増える状態です。ユーザーIDやリクエストIDなど高基数の値を ラベルにしないことが基本的な対策です。
チートシート
# Prometheus 2.x / PromQL
# https://prometheus.io/docs/prometheus/latest/querying/basics/
rate(metric[5m]) # 単位時間あたりの増加率 (Counter)
increase(metric[1h]) # 区間での増分
sum by (label) (expr) # ラベルでまとめて合計
avg / max / min / count # 集計関数
topk(n, expr) / bottomk(n, expr) # 上位/下位
histogram_quantile(0.95, sum by (le)(...)) # P95レイテンシ
up == 0 # ダウン中のインスタンス
absent(metric) # メトリクスが欠損
参考リソース
補足: SLI/SLOの設計例
SREの実践では、Error Budgetをベースに運用判断を行うことが一般的です。 PromQLで以下のようにSLIを定義できます。
# 可用性SLI (成功率)
sum(rate(http_requests_total{status!~"5.."}[30d]))
/
sum(rate(http_requests_total[30d]))
# レイテンシSLI (300ms以内の比率)
sum(rate(http_request_duration_seconds_bucket{le="0.3"}[30d]))
/
sum(rate(http_request_duration_seconds_count[30d]))
SLOを99.9% と置いた場合、Error Budget は 0.1% (30日で約43分) となります。 Budgetを消費しすぎたらリリース凍結、余裕があれば攻めた改善に回すといった運用判断が行えます。
バーンレートアラート
急激にBudgetを消費したときだけ通知する仕組みです。
- alert: HighErrorBurnRate
expr: |
(
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
) > (14.4 * 0.001)
for: 2m
labels:
severity: critical
annotations:
summary: "エラーバーンレートが高い (2%ウィンドウ)"
14.4 は「30日で2%のBudgetを5分ウィンドウで消費する速度」の係数で、
Google SREブックで紹介されている典型的な値です。