データベース

Prometheus

概要

Prometheusは、時系列データの収集、保存、クエリに特化したオープンソースの監視・アラートシステムです。Cloud Native Computing Foundation(CNCF)の卒業プロジェクトとして、Kubernetesエコシステムでの標準的な監視ソリューションとして確立されています。高性能な時系列データベースとして、システム・アプリケーション監視、メトリクス収集、リアルタイムアラートを統合的に提供します。

詳細

Prometheusは2012年にSoundCloudで開発が開始され、Googleの内部監視システム「Borgmon」にインスパイアされて設計されました。時系列データに最適化された独自のデータベースエンジンを持ち、Pull型のメトリクス収集、強力なクエリ言語PromQL、柔軟なアラート機能を特徴とします。

2024年11月にリリースされたPrometheus 3.0では、7年ぶりのメジャーアップデートとして、完全に新しいWebUI、UTF-8フルサポート、OpenTelemetry統合、Remote Write 2.0など大幅な機能強化が行われました。

Prometheusの主な特徴:

  • 高性能な時系列データベース(TSDB)
  • Pull型メトリクス収集アーキテクチャ
  • 強力なクエリ言語「PromQL」
  • 多次元データモデル(メトリクス名 + ラベル)
  • 自動サービスディスカバリー
  • アラート・通知機能(Alertmanager連携)
  • Grafana等の可視化ツールとの統合
  • Kubernetes・Docker・AWS等の豊富な統合
  • Agent Mode(軽量なメトリクス収集専用モード)
  • Native Histogram対応
  • OpenTelemetry(OTLP)統合

メリット・デメリット

メリット

  • クラウドネイティブ: Kubernetesでの標準的な監視ソリューション
  • 高性能: 独自TSDBによる高速なデータ取り込みと検索
  • 柔軟性: PromQLによる強力で柔軟なクエリ機能
  • 運用性: Pull型アーキテクチャによる堅牢なメトリクス収集
  • エコシステム: Grafana、Alertmanager等の豊富な連携ツール
  • 自動化: サービスディスカバリーによる動的環境での自動監視
  • 軽量: 単一バイナリでの簡単デプロイ
  • 拡張性: Exporter・クライアントライブラリによる豊富な監視対象

デメリット

  • 長期保存: 単体では長期データ保存に制限(Remote Storage必要)
  • 水平スケーリング: フェデレーション・シャーディングが複雑
  • HA構成: 高可用性構成の設計・運用が複雑
  • 学習コスト: PromQLの習得コストが高い
  • データ型: 数値データ中心(文字列・ログデータは不向き)
  • セキュリティ: 認証・認可機能が基本的(外部プロキシ必要)

主要リンク

書き方の例

インストール・セットアップ

# Docker での実行(推奨)
docker run -d --name prometheus \
  -p 9090:9090 \
  -v ./prometheus.yml:/etc/prometheus/prometheus.yml \
  prom/prometheus:latest

# バイナリでの実行
wget https://github.com/prometheus/prometheus/releases/download/v3.0.0/prometheus-3.0.0.linux-amd64.tar.gz
tar xvf prometheus-3.0.0.linux-amd64.tar.gz
cd prometheus-3.0.0.linux-amd64
./prometheus --config.file=prometheus.yml

# Kubernetes での実行(Helm)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install prometheus prometheus-community/kube-prometheus-stack

# Agent Mode での実行(軽量)
./prometheus --agent --config.file=prometheus.yml --storage.agent.path=./agent-data

# systemd サービス登録
sudo useradd --no-create-home --shell /bin/false prometheus
sudo mkdir /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus
sudo cp prometheus promtool /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool

cat > /etc/systemd/system/prometheus.service << EOF
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
  --config.file /etc/prometheus/prometheus.yml \
  --storage.tsdb.path /var/lib/prometheus/ \
  --web.console.templates=/etc/prometheus/consoles \
  --web.console.libraries=/etc/prometheus/console_libraries \
  --web.listen-address=0.0.0.0:9090 \
  --web.enable-lifecycle

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable prometheus
sudo systemctl start prometheus

基本設定ファイル

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "rules/*.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager:9093

scrape_configs:
  # Prometheus自身の監視
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # Node Exporter(システムメトリクス)
  - job_name: 'node'
    static_configs:
      - targets: ['localhost:9100']

  # アプリケーション監視
  - job_name: 'myapp'
    static_configs:
      - targets: ['localhost:8080']
    metrics_path: /metrics
    scrape_interval: 10s

  # 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: (.+)

  # Docker Swarm自動発見
  - job_name: 'dockerswarm'
    dockerswarm_sd_configs:
      - host: unix:///var/run/docker.sock
        role: tasks
    relabel_configs:
      - source_labels: [__meta_dockerswarm_task_desired_state]
        regex: running
        action: keep

# OpenTelemetry統合設定(Prometheus 3.0)
otlp:
  promote_resource_attributes:
    - service.name
    - service.instance.id
    - service.namespace
    - service.version
    - k8s.cluster.name
    - k8s.namespace.name
    - k8s.pod.name

# ストレージ設定
storage:
  tsdb:
    retention.time: 15d
    retention.size: 10GB
    out_of_order_time_window: 30m  # アウト・オブ・オーダーサンプル許可

PromQLクエリ

# 基本的なインスタントベクター
up

# ラベルマッチング
up{job="prometheus"}

# 時間範囲指定(Range Vector)
rate(http_requests_total[5m])

# 集約関数
sum(rate(http_requests_total[5m])) by (instance)

# CPU使用率計算
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# メモリ使用率
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100

# トップKクエリ
topk(5, rate(http_requests_total[5m]))

# 四則演算
rate(http_requests_total[5m]) * 60

# 条件分岐
up == 1

# 正規表現マッチング
http_requests_total{job=~"api.*"}

# 複数メトリクスの結合
rate(http_requests_total[5m]) / rate(http_request_duration_seconds_count[5m])

# しきい値超過アラート用
avg_over_time(cpu_usage[5m]) > 80

# パーセンタイル計算
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

# 時系列の存在確認
absent(up{job="critical-service"})

# 予測クエリ
predict_linear(node_filesystem_free_bytes[1h], 4*3600) < 0

アラートルール

# rules/alerts.yml
groups:
  - name: system
    rules:
      - alert: HighCPUUsage
        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High CPU usage detected"
          description: "CPU usage is above 80% for instance {{ $labels.instance }}"

      - alert: HighMemoryUsage
        expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "High memory usage detected"
          description: "Memory usage is above 90% for instance {{ $labels.instance }}"

      - alert: DiskSpaceLow
        expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 < 10
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Low disk space"
          description: "Disk space is below 10% for {{ $labels.mountpoint }} on {{ $labels.instance }}"

      - alert: ServiceDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Service is down"
          description: "Service {{ $labels.job }} on {{ $labels.instance }} is down"

      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "High error rate"
          description: "Error rate is above 5% for {{ $labels.job }}"

  - name: application
    rules:
      - alert: SlowResponse
        expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 0.5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Slow response time"
          description: "95th percentile response time is above 500ms"

      - alert: HighRequestRate
        expr: rate(http_requests_total[5m]) > 1000
        for: 2m
        labels:
          severity: info
        annotations:
          summary: "High request rate"
          description: "Request rate is above 1000 req/s"

      # keep_firing_for機能(Prometheus 3.0新機能)
      - alert: TransientIssue
        expr: error_rate > 0.1
        for: 1m
        keep_firing_for: 5m  # 条件が満たされなくなっても5分間アラートを継続
        labels:
          severity: warning
        annotations:
          summary: "Transient issue detected"

Recording Rules

# rules/recording.yml
groups:
  - name: cpu_recording_rules
    interval: 30s
    rules:
      - record: instance:cpu_usage_rate
        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

      - record: job:cpu_usage_rate:avg
        expr: avg by(job) (instance:cpu_usage_rate)

  - name: http_recording_rules
    interval: 15s
    rules:
      - record: job:http_requests_rate
        expr: sum by(job) (rate(http_requests_total[5m]))

      - record: job:http_error_rate
        expr: sum by(job) (rate(http_requests_total{status=~"5.."}[5m])) / sum by(job) (rate(http_requests_total[5m]))

      - record: instance:http_latency_p95
        expr: histogram_quantile(0.95, sum by(instance, le) (rate(http_request_duration_seconds_bucket[5m])))

HTTP API使用例

# メトリクス一覧取得
curl http://localhost:9090/api/v1/label/__name__/values

# インスタントクエリ
curl -G http://localhost:9090/api/v1/query \
  --data-urlencode 'query=up'

# 範囲クエリ
curl -G http://localhost:9090/api/v1/query_range \
  --data-urlencode 'query=rate(http_requests_total[5m])' \
  --data-urlencode 'start=2024-01-01T00:00:00Z' \
  --data-urlencode 'end=2024-01-01T01:00:00Z' \
  --data-urlencode 'step=60s'

# ラベル値取得
curl http://localhost:9090/api/v1/label/job/values

# シリーズメタデータ
curl -G http://localhost:9090/api/v1/series \
  --data-urlencode 'match[]=up'

# アラート一覧
curl http://localhost:9090/api/v1/alerts

# ルール一覧
curl http://localhost:9090/api/v1/rules

# 設定情報
curl http://localhost:9090/api/v1/status/config

# ビルド情報
curl http://localhost:9090/api/v1/status/buildinfo

# TSDBステータス
curl http://localhost:9090/api/v1/status/tsdb

# 設定リロード(--web.enable-lifecycle必要)
curl -X POST http://localhost:9090/-/reload

# ヘルスチェック
curl http://localhost:9090/-/healthy

# レディネスチェック
curl http://localhost:9090/-/ready

クライアントライブラリ(Go)

package main

import (
    "log"
    "net/http"
    "time"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "github.com/prometheus/client_golang/prometheus/promauto"
)

var (
    // Counter - 累積値(リクエスト数、エラー数等)
    httpRequestsTotal = promauto.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total number of HTTP requests",
        },
        []string{"method", "endpoint", "status"},
    )

    // Gauge - 現在値(CPU使用率、メモリ使用量等)
    cpuUsage = promauto.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: "cpu_usage_percent",
            Help: "Current CPU usage percentage",
        },
        []string{"core"},
    )

    // Histogram - 分布(レスポンス時間、リクエストサイズ等)
    httpDuration = promauto.NewHistogramVec(
        prometheus.HistogramOpts{
            Name: "http_request_duration_seconds",
            Help: "Duration of HTTP requests",
            Buckets: prometheus.DefBuckets,
        },
        []string{"method", "endpoint"},
    )

    // Summary - 分位数(レスポンス時間の分位数等)
    httpSummary = promauto.NewSummaryVec(
        prometheus.SummaryOpts{
            Name: "http_request_duration_summary",
            Help: "Summary of HTTP request durations",
            Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
        },
        []string{"method"},
    )
)

func httpHandler(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    
    // Counter増加
    httpRequestsTotal.WithLabelValues(r.Method, r.URL.Path, "200").Inc()
    
    // 処理時間記録
    duration := time.Since(start).Seconds()
    httpDuration.WithLabelValues(r.Method, r.URL.Path).Observe(duration)
    httpSummary.WithLabelValues(r.Method).Observe(duration)
    
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("Hello World"))
}

func main() {
    // CPUモニタリングのサンプル
    go func() {
        for {
            // 実際のCPU使用率取得ロジック
            cpuUsage.WithLabelValues("0").Set(45.2)
            cpuUsage.WithLabelValues("1").Set(67.8)
            time.Sleep(10 * time.Second)
        }
    }()

    http.HandleFunc("/", httpHandler)
    http.Handle("/metrics", promhttp.Handler())
    
    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Python クライアント

from prometheus_client import Counter, Gauge, Histogram, Summary, start_http_server
import time
import random

# メトリクス定義
REQUEST_COUNT = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint'])
CPU_USAGE = Gauge('cpu_usage_percent', 'CPU usage percentage', ['core'])
REQUEST_DURATION = Histogram('http_request_duration_seconds', 'HTTP request duration')
RESPONSE_SIZE = Summary('http_response_size_bytes', 'HTTP response size')

def process_request():
    """リクエスト処理のシミュレーション"""
    start_time = time.time()
    
    # ランダムな処理時間
    time.sleep(random.uniform(0.1, 0.5))
    
    # メトリクス更新
    REQUEST_COUNT.labels(method='GET', endpoint='/api').inc()
    REQUEST_DURATION.observe(time.time() - start_time)
    RESPONSE_SIZE.observe(random.randint(100, 1000))

def update_system_metrics():
    """システムメトリクスの更新"""
    while True:
        # CPU使用率のシミュレーション
        CPU_USAGE.labels(core='0').set(random.uniform(20, 80))
        CPU_USAGE.labels(core='1').set(random.uniform(20, 80))
        time.sleep(10)

if __name__ == '__main__':
    # メトリクスサーバー開始
    start_http_server(8000)
    
    # システムメトリクス更新スレッド開始
    import threading
    threading.Thread(target=update_system_metrics, daemon=True).start()
    
    # リクエスト処理シミュレーション
    while True:
        process_request()
        time.sleep(1)

Node Exporter設定

# Node Exporter インストール
wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
tar xvf node_exporter-1.7.0.linux-amd64.tar.gz
cd node_exporter-1.7.0.linux-amd64

# 実行
./node_exporter

# サービス登録
sudo useradd --no-create-home --shell /bin/false node_exporter
sudo cp node_exporter /usr/local/bin/
sudo chown node_exporter:node_exporter /usr/local/bin/node_exporter

cat > /etc/systemd/system/node_exporter.service << EOF
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target

[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter \
  --collector.systemd \
  --collector.processes \
  --web.listen-address=:9100

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable node_exporter
sudo systemctl start node_exporter

# カスタムメトリクス(textfile collector)
mkdir -p /var/lib/node_exporter/textfile_collector

# カスタムメトリクス作成スクリプト
cat > /usr/local/bin/custom_metrics.sh << 'EOF'
#!/bin/bash
echo "# HELP custom_disk_usage Custom disk usage metric
# TYPE custom_disk_usage gauge" > /var/lib/node_exporter/textfile_collector/custom.prom
df -h | grep '^/dev/' | awk '{print "custom_disk_usage{device=\""$1"\",mountpoint=\""$6"\"} " ($5+0)/100}' >> /var/lib/node_exporter/textfile_collector/custom.prom
EOF

chmod +x /usr/local/bin/custom_metrics.sh

# cron設定
echo "*/5 * * * * /usr/local/bin/custom_metrics.sh" | crontab -

Remote Storage設定

# Remote Write設定(長期保存用)
remote_write:
  - url: "https://cortex.example.com/api/v1/push"
    basic_auth:
      username: user
      password: pass
    queue_config:
      max_samples_per_send: 2000
      batch_send_deadline: 5s
      max_retries: 3
    metadata_config:
      send: true

  # Grafana Cloud
  - url: "https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push"
    basic_auth:
      username: "123456"
      password: "YOUR_API_KEY"

# Remote Read設定
remote_read:
  - url: "https://cortex.example.com/api/v1/read"
    basic_auth:
      username: user
      password: pass

Alertmanager設定

# alertmanager.yml
global:
  smtp_smarthost: 'smtp.gmail.com:587'
  smtp_from: '[email protected]'
  smtp_auth_username: '[email protected]'
  smtp_auth_password: 'password'

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'web.hook'
  routes:
    - match:
        severity: critical
      receiver: 'critical-alerts'
    - match:
        severity: warning
      receiver: 'warning-alerts'

receivers:
  - name: 'web.hook'
    webhook_configs:
      - url: 'http://localhost:5001/webhook'

  - name: 'critical-alerts'
    email_configs:
      - to: '[email protected]'
        subject: 'Critical Alert: {{ .GroupLabels.alertname }}'
        body: |
          {{ range .Alerts }}
          Alert: {{ .Annotations.summary }}
          Description: {{ .Annotations.description }}
          {{ end }}
    slack_configs:
      - api_url: 'YOUR_SLACK_WEBHOOK_URL'
        channel: '#critical-alerts'
        title: 'Critical Alert'
        text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'

  - name: 'warning-alerts'
    slack_configs:
      - api_url: 'YOUR_SLACK_WEBHOOK_URL'
        channel: '#alerts'
        title: 'Warning Alert'

運用・最適化

# データベースサイズ確認
du -sh /var/lib/prometheus/

# TSDBブロック情報
promtool tsdb list /var/lib/prometheus/

# 設定ファイル検証
promtool check config prometheus.yml

# ルールファイル検証
promtool check rules rules/*.yml

# メトリクス削除
curl -X POST http://localhost:9090/api/v1/admin/tsdb/delete_series \
  --data-urlencode 'match[]=up{job="old-job"}'

# スナップショット作成
curl -X POST http://localhost:9090/api/v1/admin/tsdb/snapshot

# バックアップ
tar czf prometheus-backup-$(date +%Y%m%d).tar.gz /var/lib/prometheus/

# パフォーマンス監視用クエリ
# メモリ使用量
process_resident_memory_bytes{job="prometheus"}

# 取り込み率
rate(prometheus_tsdb_symbol_table_size_bytes[5m])

# チャンク数
prometheus_tsdb_head_chunks

# WALサイズ
prometheus_tsdb_wal_storage_size_bytes