PrometheusとGrafanaで本番監視スタックを構築

監視は信頼性の高いインフラの要です。このチュートリアルでは、メトリクス収集にPrometheus、可視化にGrafanaを使用した完全なオブザーバビリティスタックをDocker Composeで構築します。

構築するもの

  • 複数ターゲットからメトリクスを収集するPrometheusサーバー
  • リアルタイム可視化のGrafanaダッシュボード
  • システムメトリクス(CPU、メモリ、ディスク)用のNode Exporter
  • カスタムメトリクス付きサンプルPythonアプリ
  • Slack通知付きAlertmanager
  • 前提条件

  • DockerとDocker Composeがインストール済み
  • YAML設定の基本的な理解
  • Slack Webhook URL(オプション、アラート用)
  • ---

    ステップ1:プロジェクト構造

    プロジェクトディレクトリを作成します:

    mkdir monitoring-stack && cd monitoring-stack
    

    mkdir -p prometheus grafana/provisioning/datasources grafana/provisioning/dashboards alertmanager app

    最終構造:

    monitoring-stack/
    

    ├── docker-compose.yml

    ├── prometheus/

    │ ├── prometheus.yml

    │ └── alert.rules.yml

    ├── grafana/

    │ └── provisioning/

    │ ├── datasources/

    │ │ └── prometheus.yml

    │ └── dashboards/

    │ ├── dashboard.yml

    │ └── node-exporter.json

    ├── alertmanager/

    │ └── alertmanager.yml

    └── app/

    ├── app.py

    ├── requirements.txt

    └── Dockerfile

    ---

    ステップ2:Docker Compose設定

    docker-compose.ymlを作成します:

    version: '3.8'
    
    

    services:

    prometheus:

    image: prom/prometheus:v2.51.0

    container_name: prometheus

    ports:

    - "9090:9090"

    volumes:

    - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml

    - ./prometheus/alert.rules.yml:/etc/prometheus/alert.rules.yml

    - prometheus_data:/prometheus

    command:

    - '--config.file=/etc/prometheus/prometheus.yml'

    - '--storage.tsdb.path=/prometheus'

    - '--storage.tsdb.retention.time=30d'

    - '--web.enable-lifecycle'

    restart: unless-stopped

    networks:

    - monitoring

    grafana:

    image: grafana/grafana:10.3.1

    container_name: grafana

    ports:

    - "3000:3000"

    environment:

    - GF_SECURITY_ADMIN_USER=admin

    - GF_SECURITY_ADMIN_PASSWORD=changeme

    - GF_USERS_ALLOW_SIGN_UP=false

    volumes:

    - grafana_data:/var/lib/grafana

    - ./grafana/provisioning:/etc/grafana/provisioning

    restart: unless-stopped

    networks:

    - monitoring

    node-exporter:

    image: prom/node-exporter:v1.7.0

    container_name: node-exporter

    ports:

    - "9100:9100"

    volumes:

    - /proc:/host/proc:ro

    - /sys:/host/sys:ro

    - /:/rootfs:ro

    command:

    - '--path.procfs=/host/proc'

    - '--path.rootfs=/rootfs'

    - '--path.sysfs=/host/sys'

    - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'

    restart: unless-stopped

    networks:

    - monitoring

    alertmanager:

    image: prom/alertmanager:v0.27.0

    container_name: alertmanager

    ports:

    - "9093:9093"

    volumes:

    - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml

    restart: unless-stopped

    networks:

    - monitoring

    sample-app:

    build: ./app

    container_name: sample-app

    ports:

    - "8000:8000"

    restart: unless-stopped

    networks:

    - monitoring

    volumes:

    prometheus_data:

    grafana_data:

    networks:

    monitoring:

    driver: bridge

    ---

    ステップ3:Prometheus設定

    prometheus/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-exporter'

    static_configs:

    - targets: ['node-exporter:9100']

    - job_name: 'sample-app'

    static_configs:

    - targets: ['sample-app:8000']

    アラートルール prometheus/alert.rules.ymlを作成:

    groups:
    

    - name: system_alerts

    rules:

    - alert: HighCPUUsage

    expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80

    for: 5m

    labels:

    severity: warning

    annotations:

    summary: "{{ $labels.instance }}でCPU使用率が高い"

    description: "CPU使用率が5分以上80%を超えています(現在: {{ $value }}%)"

    - alert: TargetDown

    expr: up == 0

    for: 1m

    labels:

    severity: critical

    annotations:

    summary: "ターゲット{{ $labels.instance }}がダウンしています"

    ---

    ステップ4:カスタムメトリクス付きPythonアプリ

    app/app.pyを作成します:

    import time
    

    import random

    from flask import Flask, Response

    from prometheus_client import (

    Counter, Histogram, Gauge,

    generate_latest, CONTENT_TYPE_LATEST

    )

    app = Flask(__name__)

    # カスタムメトリクスの定義

    REQUEST_COUNT = Counter(

    'http_requests_total',

    'HTTPリクエスト総数',

    ['method', 'endpoint', 'status']

    )

    REQUEST_DURATION = Histogram(

    'http_request_duration_seconds',

    'HTTPリクエスト処理時間(秒)',

    ['method', 'endpoint'],

    buckets=[0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]

    )

    ACTIVE_REQUESTS = Gauge(

    'http_active_requests',

    'アクティブなHTTPリクエスト数'

    )

    def track_request(method, endpoint):

    """リクエストメトリクスを追跡するデコレータ"""

    def decorator(f):

    def wrapper(args, *kwargs):

    ACTIVE_REQUESTS.inc()

    start_time = time.time()

    try:

    result = f(args, *kwargs)

    status = 200

    return result

    except Exception:

    status = 500

    raise

    finally:

    duration = time.time() - start_time

    REQUEST_COUNT.labels(method, endpoint, status).inc()

    REQUEST_DURATION.labels(method, endpoint).observe(duration)

    ACTIVE_REQUESTS.dec()

    wrapper.__name__ = f.__name__

    return wrapper

    return decorator

    @app.route('/')

    @track_request('GET', '/')

    def home():

    time.sleep(random.uniform(0.01, 0.1))

    return {'status': 'ok', 'message': '監視対象アプリからこんにちは!'}

    @app.route('/metrics')

    def metrics():

    return Response(generate_latest(), mimetype=CONTENT_TYPE_LATEST)

    if __name__ == '__main__':

    app.run(host='0.0.0.0', port=8000)

    ---

    ステップ5:起動とテスト

    # スタック全体を起動
    

    docker compose up -d

    # サービス状態を確認

    docker compose ps

    # サンプルアプリにトラフィックを生成

    for i in $(seq 1 50); do

    curl -s http://localhost:8000/ > /dev/null

    done

    # メトリクスを確認

    curl http://localhost:8000/metrics

    アクセス先:

  • Prometheus: http://localhost:9090
  • Grafana: http://localhost:3000(admin/changeme)
  • Alertmanager: http://localhost:9093

---

ステップ6:便利なPromQLクエリ

# 1秒あたりのリクエストレート

rate(http_requests_total[5m])

# 95パーセンタイルレイテンシ

histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

# CPU使用率

100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# メモリ使用率

(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100

---

本番環境向けのヒント

1. Grafanaのセキュリティ: デフォルトパスワードを変更し、HTTPSとOAuthを設定

2. 保持ポリシー: ストレージ容量に基づいてretention.timeを調整

3. リモートストレージ: 長期保存にはThanosまたはCortexを使用

4. サービスディスカバリ: 静的設定をConsulやKubernetes SDに置き換え

5. レコーディングルール: ダッシュボード性能向上のために高負荷クエリを事前計算

本番グレードの監視スタックが完成しました!独自のサービスをスクレイプターゲットとして追加し、ニーズに合わせたカスタムダッシュボードを構築しましょう。