Skip to content
Published on

Prometheus運用完全ガイド — メトリクス収集・PromQL・アラート・ダッシュボード・ベストプラクティス

Authors
  • Name
    Twitter

はじめに

現代のインフラはマイクロサービス、コンテナ、サーバーレスに分散され、数百のコンポーネントが同時に稼働している。このような環境で「今システムは正常か?」という質問に答えるためには、体系的なメトリクス収集とモニタリングが不可欠だ。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 DiscoveryKubernetes、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)サービス別エラー率の推移
QPSsum(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日の保持に適している。長期保持とグローバルクエリが必要な場合は以下のソリューションを検討する。

ソリューション特徴適した環境
ThanosSidecarパターン、オブジェクトストレージ、グローバルクエリ既存Prometheusへのサイドカー追加
Cortexマルチテナント、水平スケーリング、マイクロサービスアーキテクチャ大規模SaaS環境
Grafana MimirCortexフォーク、パフォーマンス改善、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 RAMWALとHead chunksのメモリを考慮
スクレイプ間隔15s(デフォルト)、重要メトリクスは10s短すぎると負荷増加、長すぎると解像度低下
アラートテストamtool check-configpromtool check rulesCI/CDへの統合を推奨
Recording Rulesよく使うクエリを事前計算ダッシュボードパフォーマンス向上、record:命名規則を遵守
カーディナリティ監視prometheus_tsdb_head_seriesの推移を監視急激な増加時は原因メトリクスを追跡

9. よくあるミス

  1. rate()に短すぎるrange windowを使用 -- rate(metric[1m])の代わりにscrape_intervalの最低4倍(15sなら[1m]、30sなら[2m])を使用すべきだ。短すぎるとデータポイントの欠落で結果が0になる。

  2. アラートにirate()を使用 -- irate()は瞬間変化率であるためノイズに敏感だ。アラートルールでは必ずrate()を使用する。

  3. for句なしでアラートを定義 -- for: 0sだと一度のスパイクでもアラートが発火する。最低3〜5分のfor期間を設定する。

  4. 高カーディナリティラベルの使用 -- user_id、request_id、trace_idのような一意の値をラベルに入れると時系列が無限に増加する。このような値はログやトレースに保存する。

  5. Summaryタイプの乱用 -- Summaryのquantileはインスタンス間の集約が不可能だ。複数インスタンスを運用する場合はHistogramを使用する。

  6. Pushgatewayの乱用 -- Pushgatewayは短期バッチジョブ専用だ。長期実行サービスのメトリクスをPushgatewayに送ると、対象ダウンの検知が不可能になる。

  7. Alertmanagerのルーティング優先順位を無視 -- ルートマッチングは上から下に進む。具体的なマッチを先に配置し、デフォルトレシーバーを最後に置く。

  8. Recording Rulesの命名規則を未遵守 -- level:metric:operations形式(例:job:http_requests_total:rate5m)に従うことで、ダッシュボードやアラートでの識別が容易になる。

  9. TSDB保持期間とディスク容量の不一致 -- retentionを90日に設定してもディスクが30日分しか対応できない場合、PrometheusがOOMで停止する。--storage.tsdb.retention.sizeでディスクベースの保持制限を併用する。

  10. モニタリングシステム自体をモニタリングしない -- Prometheus自身のupprometheus_tsdb_head_seriesprometheus_engine_query_duration_secondsを必ず監視する。

10. まとめ

Prometheusはクラウドネイティブ環境の標準モニタリングシステムだ。重要なポイントをまとめると以下の通りだ。

  • プルベースモデルで動的環境に自然に適応し、サービスディスカバリと統合する
  • 4つのメトリクスタイプ(Counter、Gauge、Histogram、Summary)のうちHistogramを優先使用する
  • PromQLはrate()、histogram_quantile()、predict_linear()などの強力な関数を提供する
  • Alertmanagerはルーティング、グルーピング、抑制、重複排除でアラート疲労を軽減する
  • Grafana連携でエラー率、QPS、レイテンシ、リソース使用量のダッシュボードを構成する
  • 大規模環境ではThanos/Mimirで長期保存、Federationでグローバルビュー、カーディナリティ管理でパフォーマンスを維持する
  • 運用チェックリストを定期的に確認し、よくあるミスを避ける

この記事で紹介した設定とクエリをベースに自分の環境に合わせて調整すれば、安定的でスケーラブルなモニタリングシステムを構築できる。