- Authors
- Name
- はじめに
- 1. コアコンセプト
- 2. アーキテクチャ
- 3. インストールと設定
- 4. PromQL実践
- 5. Alertmanagerアラート設定
- 6. Grafanaダッシュボード連携
- 7. 大規模運用のベストプラクティス
- 8. 運用チェックリスト
- 9. よくあるミス
- 10. まとめ
はじめに
現代のインフラはマイクロサービス、コンテナ、サーバーレスに分散され、数百のコンポーネントが同時に稼働している。このような環境で「今システムは正常か?」という質問に答えるためには、体系的なメトリクス収集とモニタリングが不可欠だ。PrometheusはCNCF卒業プロジェクトとしてKubernetesエコシステムの事実上の標準モニタリングシステムであり、プル(pull)ベースのメトリクス収集、強力なクエリ言語PromQL、柔軟なアラートシステムを提供する。
この記事では、Prometheusのコアコンセプトからアーキテクチャ、インストールと設定、実践的なPromQLクエリ、Alertmanagerアラート構成、Grafanaダッシュボード連携、大規模環境の運用ベストプラクティス、運用チェックリスト、よくあるミスまでを一つの記事で総まとめする。本番環境ですぐに適用できる実践的なガイドだ。
1. コアコンセプト
プルベースモデル
Prometheusは監視対象がメトリクスをpushする方式ではなく、Prometheusサーバーが定期的に対象の/metricsエンドポイントをスクレイプ(scrape) するプルベースモデルを使用する。この方式の利点は以下の通りだ。
- 監視対象がPrometheusの存在を知る必要がない
- サービスディスカバリと組み合わせることで動的環境に自然に対応する
- 対象がダウンするとスクレイプ失敗で即座に検知される
時系列データ(Time Series)
Prometheusはすべてのデータを時系列(time series) として保存する。各時系列はメトリクス名とキーバリューペアのラベルの組み合わせで一意に識別される。
http_requests_total{method="GET", handler="/api/users", status="200"}
メトリクスタイプ
| タイプ | 説明 | 使用例 | PromQLでの使用 |
|---|---|---|---|
| Counter | 単調増加する累積値 | リクエスト数、エラー数 | rate()、increase() |
| Gauge | 増減可能な現在の値 | CPU使用率、メモリ、温度 | 直接使用、avg_over_time() |
| Histogram | バケットごとに値の分布を測定 | レスポンスタイム、リクエストサイズ | histogram_quantile() |
| Summary | クライアント側でquantileを計算 | レスポンスタイム(集約不可) | 直接使用(非推奨) |
Histogram vs Summary: Histogramはサーバー側でhistogram_quantile()関数によりquantileを計算できるため、複数インスタンスのデータを集約可能だ。Summaryはクライアント側で計算するため、インスタンス間の集約が数学的に不可能だ。ほとんどの場合、Histogramを推奨する。
2. アーキテクチャ
Prometheusエコシステムは複数のコンポーネントが有機的に連携する。
graph TB
subgraph Targets
A[Application /metrics]
B[Node Exporter]
C[cAdvisor]
D[Custom Exporter]
end
subgraph "Prometheus Server"
E[Retrieval<br/>スクレイプエンジン]
F[TSDB<br/>時系列データベース]
G[HTTP Server<br/>PromQL API]
end
H[Service Discovery<br/>Kubernetes / Consul / DNS]
I[Pushgateway<br/>短期ジョブ用]
J[Alertmanager<br/>アラートルーティング/グルーピング]
K[Grafana<br/>ダッシュボード]
H --> E
A --> E
B --> E
C --> E
D --> E
I --> E
E --> F
F --> G
G --> K
G --> J
J --> L[Slack / PagerDuty / Email]
| コンポーネント | 役割 |
|---|---|
| Prometheus Server | メトリクスのスクレイプ、TSDBへの保存、PromQLクエリエンジン |
| Exporters | 対象システムのメトリクスをPrometheus形式で公開(node_exporter、mysqld_exporterなど) |
| Pushgateway | スクレイプが困難な短期バッチジョブのメトリクスを中継 |
| Alertmanager | アラートルールに基づくアラートのルーティング、グルーピング、重複排除、抑制 |
| Service Discovery | Kubernetes、Consul、DNSなどからスクレイプ対象を動的に発見 |
3. インストールと設定
Docker Composeによるフルスタック構成
# docker-compose.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:v2.53.0
container_name: prometheus
ports:
- '9090:9090'
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus/rules/:/etc/prometheus/rules/
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
- '--web.enable-lifecycle'
- '--web.enable-admin-api'
restart: unless-stopped
alertmanager:
image: prom/alertmanager:v0.27.0
container_name: alertmanager
ports:
- '9093:9093'
volumes:
- ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml
restart: unless-stopped
grafana:
image: grafana/grafana:11.1.0
container_name: grafana
ports:
- '3000:3000'
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning/:/etc/grafana/provisioning/
restart: unless-stopped
node-exporter:
image: prom/node-exporter:v1.8.1
container_name: node-exporter
ports:
- '9100:9100'
restart: unless-stopped
volumes:
prometheus_data:
grafana_data:
prometheus.yml設定
# prometheus/prometheus.yml
global:
scrape_interval: 15s # デフォルトスクレイプ間隔
evaluation_interval: 15s # アラートルール評価間隔
scrape_timeout: 10s # スクレイプタイムアウト
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
rule_files:
- /etc/prometheus/rules/*.yml
scrape_configs:
# Prometheus自身のモニタリング
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Node Exporter
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
# アプリケーション(relabel_configsの活用)
- job_name: 'app'
metrics_path: /metrics
scheme: http
static_configs:
- targets: ['app:8080']
labels:
env: production
team: backend
# Kubernetesサービスディスカバリ(参考)
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: pod
4. PromQL実践
rate()とirate()
# 5分間の秒間リクエストレート(アラートに適切)
sum(rate(http_requests_total[5m])) by (service)
# 瞬間変化率(ダッシュボードの可視化に適切)
sum(irate(http_requests_total[$__rate_interval])) by (service)
rate()はアラートルールで、irate()はダッシュボードで使用する。rate()のrange windowはscrape_intervalの4倍以上に設定する必要がある。
histogram_quantile()
# 95パーセンタイルレスポンスタイム
histogram_quantile(0.95,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
)
# 50パーセンタイル(中央値)
histogram_quantile(0.50,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)
集約演算子(Aggregation Operators)
# サービス別エラー率トップ5
topk(5,
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service)
)
# ネームスペース別メモリ使用量
sum(container_memory_usage_bytes{container!=""}) by (namespace)
# CPU使用率が80%を超えるノード
node_cpu_seconds_total{mode="idle"} < 0.2
# predict_linearでディスク容量を予測(4時間後)
predict_linear(
node_filesystem_avail_bytes{mountpoint="/"}[6h], 4 * 3600
) < 0
便利な関数リファレンス
| 関数 | 用途 | 例 |
|---|---|---|
rate() | 時間範囲の秒間平均変化率 | rate(http_requests_total[5m]) |
irate() | 最新2点間の瞬間変化率 | irate(http_requests_total[5m]) |
increase() | 時間範囲の総増加量 | increase(http_requests_total[1h]) |
histogram_quantile() | ヒストグラムからの分位数計算 | histogram_quantile(0.99, ...) |
predict_linear() | 線形回帰による将来値の予測 | predict_linear(disk_free[6h], 3600*4) |
absent() | 時系列が存在しない場合1を返す | absent(up{job="app"}) |
changes() | 時間範囲内の値変更回数 | changes(process_start_time_seconds[1h]) |
5. Alertmanagerアラート設定
アラートルールの定義
# prometheus/rules/alerts.yml
groups:
- name: instance-alerts
rules:
- alert: InstanceDown
expr: up == 0
for: 3m
labels:
severity: critical
annotations:
summary: 'インスタンス {{ $labels.instance }} がダウンしました'
description: '{{ $labels.job }} ジョブの {{ $labels.instance }} が3分以上応答していません。'
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service)
> 0.05
for: 5m
labels:
severity: warning
annotations:
summary: '{{ $labels.service }} エラー率 {{ $value | humanizePercentage }} 超過'
description: 'サービス {{ $labels.service }} の5xxエラー率が5%を超過しました。'
- alert: HighMemoryUsage
expr: |
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 0.9
for: 10m
labels:
severity: warning
annotations:
summary: 'ノードメモリ使用率90%超過'
- alert: DiskSpaceRunningOut
expr: |
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 24*3600) < 0
for: 30m
labels:
severity: warning
annotations:
summary: '24時間以内にディスク容量が不足すると予測されます'
- alert: HighLatencyP95
expr: |
histogram_quantile(0.95,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
) > 1.0
for: 5m
labels:
severity: warning
annotations:
summary: '{{ $labels.service }} P95レスポンスタイム1秒超過'
Alertmanager設定
# alertmanager/alertmanager.yml
global:
resolve_timeout: 5m
slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
route:
receiver: 'default-slack'
group_by: ['alertname', 'service']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- match:
severity: critical
receiver: 'pagerduty-critical'
repeat_interval: 1h
- match:
severity: warning
receiver: 'slack-warnings'
repeat_interval: 4h
receivers:
- name: 'default-slack'
slack_configs:
- channel: '#alerts'
title: '{{ .GroupLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
send_resolved: true
- name: 'pagerduty-critical'
pagerduty_configs:
- service_key: 'YOUR_PAGERDUTY_SERVICE_KEY'
severity: critical
- name: 'slack-warnings'
slack_configs:
- channel: '#alerts-warning'
send_resolved: true
inhibit_rules:
- source_match:
severity: critical
target_match:
severity: warning
equal: ['alertname', 'instance']
6. Grafanaダッシュボード連携
データソースプロビジョニング
# grafana/provisioning/datasources/prometheus.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: true
jsonData:
timeInterval: '15s'
httpMethod: POST
ダッシュボードパネル例
{
"title": "Service Error Rate",
"type": "timeseries",
"datasource": "Prometheus",
"targets": [
{
"expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m])) by (service) / sum(rate(http_requests_total[5m])) by (service)",
"legendFormat": "{{ service }}"
}
],
"fieldConfig": {
"defaults": {
"unit": "percentunit",
"thresholds": {
"steps": [
{ "value": 0, "color": "green" },
{ "value": 0.01, "color": "yellow" },
{ "value": 0.05, "color": "red" }
]
}
}
}
}
推奨ダッシュボードパネル構成
| パネル | PromQL | 用途 |
|---|---|---|
| エラー率 | sum(rate(http_requests_total{status=~"5.."}[5m])) by (service) / sum(rate(http_requests_total[5m])) by (service) | サービス別エラー率の推移 |
| QPS | sum(rate(http_requests_total[5m])) by (service) | 秒間リクエスト数 |
| P95レイテンシ | histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)) | 95パーセンタイルレスポンスタイム |
| CPU使用率 | 1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) | ノード別CPU使用率 |
| メモリ使用量 | 1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes | ノード別メモリ使用率 |
| ディスク残量 | node_filesystem_avail_bytes{mountpoint="/"} | ルートファイルシステムの残り容量 |
7. 大規模運用のベストプラクティス
Federation(フェデレーション)
複数のPrometheusサーバーのメトリクスを上位Prometheusが収集する構造だ。チーム/クラスター別にローカルPrometheusを運用し、グローバルビューに必要なメトリクスのみをフェデレーションサーバーに転送する。
# グローバルPrometheusのscrape_configs
scrape_configs:
- job_name: 'federate'
honor_labels: true
metrics_path: '/federate'
params:
'match[]':
- '{job=~".+"}' # すべてのjobメトリクス
- '{__name__=~"job:.*"}' # Recording Ruleの結果のみ
static_configs:
- targets:
- 'prometheus-team-a:9090'
- 'prometheus-team-b:9090'
長期ストレージ:Thanos / Cortex / Mimir
PrometheusのローカルTSDBは基本的に15〜30日の保持に適している。長期保持とグローバルクエリが必要な場合は以下のソリューションを検討する。
| ソリューション | 特徴 | 適した環境 |
|---|---|---|
| Thanos | Sidecarパターン、オブジェクトストレージ、グローバルクエリ | 既存Prometheusへのサイドカー追加 |
| Cortex | マルチテナント、水平スケーリング、マイクロサービスアーキテクチャ | 大規模SaaS環境 |
| Grafana Mimir | Cortexフォーク、パフォーマンス改善、Grafanaエコシステム統合 | Grafanaスタック使用時 |
| VictoriaMetrics | 高パフォーマンス、低リソース、PromQL互換 | コスト最適化が重要な場合 |
カーディナリティ管理
高カーディナリティ(ラベル値の組み合わせの爆発的増加)はPrometheusのパフォーマンス低下の最も一般的な原因だ。
# relabel_configsで不要なラベルを削除
relabel_configs:
- action: labeldrop
regex: '(pod_template_hash|controller_revision_hash)'
# metric_relabel_configsで高カーディナリティメトリクスを削除
metric_relabel_configs:
- source_labels: [__name__]
regex: '(go_gc_.*|go_memstats_.*)'
action: drop
カーディナリティ確認PromQL:
# 時系列数が最も多いメトリクストップ10
topk(10, count by (__name__) ({__name__=~".+"}))
# 特定メトリクスのカーディナリティ確認
count(http_requests_total) by (service, method, status)
8. 運用チェックリスト
| 項目 | 推奨設定 | 備考 |
|---|---|---|
| 保持期間 | 15〜30日(ローカルTSDB) | 長期:Thanos/Mimirでオブジェクトストレージ |
| バックアップ | TSDBスナップショットAPI活用 | POST /api/v1/admin/tsdb/snapshot |
| HA構成 | 同一設定のPrometheus 2台 + Alertmanagerクラスタリング | Alertmanagerはgossipプロトコルで重複アラート防止 |
| セキュリティ | TLS + Basic AuthまたはOAuth2 Proxy | --web.config.fileでTLS/Auth設定 |
| リソース | 時系列100万件あたり約2GB RAM | WALとHead chunksのメモリを考慮 |
| スクレイプ間隔 | 15s(デフォルト)、重要メトリクスは10s | 短すぎると負荷増加、長すぎると解像度低下 |
| アラートテスト | amtool check-config、promtool check rules | CI/CDへの統合を推奨 |
| Recording Rules | よく使うクエリを事前計算 | ダッシュボードパフォーマンス向上、record:命名規則を遵守 |
| カーディナリティ監視 | prometheus_tsdb_head_seriesの推移を監視 | 急激な増加時は原因メトリクスを追跡 |
9. よくあるミス
rate()に短すぎるrange windowを使用 --
rate(metric[1m])の代わりにscrape_intervalの最低4倍(15sなら[1m]、30sなら[2m])を使用すべきだ。短すぎるとデータポイントの欠落で結果が0になる。アラートにirate()を使用 -- irate()は瞬間変化率であるためノイズに敏感だ。アラートルールでは必ずrate()を使用する。
for句なしでアラートを定義 --
for: 0sだと一度のスパイクでもアラートが発火する。最低3〜5分のfor期間を設定する。高カーディナリティラベルの使用 -- user_id、request_id、trace_idのような一意の値をラベルに入れると時系列が無限に増加する。このような値はログやトレースに保存する。
Summaryタイプの乱用 -- Summaryのquantileはインスタンス間の集約が不可能だ。複数インスタンスを運用する場合はHistogramを使用する。
Pushgatewayの乱用 -- Pushgatewayは短期バッチジョブ専用だ。長期実行サービスのメトリクスをPushgatewayに送ると、対象ダウンの検知が不可能になる。
Alertmanagerのルーティング優先順位を無視 -- ルートマッチングは上から下に進む。具体的なマッチを先に配置し、デフォルトレシーバーを最後に置く。
Recording Rulesの命名規則を未遵守 --
level:metric:operations形式(例:job:http_requests_total:rate5m)に従うことで、ダッシュボードやアラートでの識別が容易になる。TSDB保持期間とディスク容量の不一致 -- retentionを90日に設定してもディスクが30日分しか対応できない場合、PrometheusがOOMで停止する。
--storage.tsdb.retention.sizeでディスクベースの保持制限を併用する。モニタリングシステム自体をモニタリングしない -- Prometheus自身の
up、prometheus_tsdb_head_series、prometheus_engine_query_duration_secondsを必ず監視する。
10. まとめ
Prometheusはクラウドネイティブ環境の標準モニタリングシステムだ。重要なポイントをまとめると以下の通りだ。
- プルベースモデルで動的環境に自然に適応し、サービスディスカバリと統合する
- 4つのメトリクスタイプ(Counter、Gauge、Histogram、Summary)のうちHistogramを優先使用する
- PromQLはrate()、histogram_quantile()、predict_linear()などの強力な関数を提供する
- Alertmanagerはルーティング、グルーピング、抑制、重複排除でアラート疲労を軽減する
- Grafana連携でエラー率、QPS、レイテンシ、リソース使用量のダッシュボードを構成する
- 大規模環境ではThanos/Mimirで長期保存、Federationでグローバルビュー、カーディナリティ管理でパフォーマンスを維持する
- 運用チェックリストを定期的に確認し、よくあるミスを避ける
この記事で紹介した設定とクエリをベースに自分の環境に合わせて調整すれば、安定的でスケーラブルなモニタリングシステムを構築できる。