Lab AI 監視とオブザーバビリティの基礎——Prometheus、Grafana、ELK スタック
目次

この記事の内容

現代のシステム運用において、監視とオブザーバビリティは不可欠な要素です。本記事では、監視の基本概念から、主要ツールの活用方法までを解説します。

監視とオブザーバビリティの違い

従来の監視(Monitoring)

**「システムが正常に動作しているか」**を確認する活動です。

【監視の主な対象】
・CPU 使用率
・メモリ使用量
・ディスク容量
・ネットワークトラフィック
・サービスのアップ/ダウン

特徴:

  • 事前に指標(メトリクス)を定義
  • 閾値を超えた場合にアラート
  • 「何がおかしいか」を検知

オブザーバビリティ(Observability)

**「システム内部の状態を外部から観測・理解する能力」**です。

【オブザーバビリティの 3 本柱】
1. メトリクス(Metrics)—— 時系列の数値データ
2. ログ(Logs)—— イベント記録
3. トレース(Traces)—— リクエストの流れ

特徴:

  • 予期せぬ問題の調査が可能
  • 「なぜおかしいか」を理解
  • 分散システム時代に必須

比較表

特徴 監視 オブザーバビリティ
目的 問題の検知 問題の理解
アプローチ トップダウン(既知の問題) ボトムアップ(未知の問題)
データ メトリクス中心 メトリクス+ログ+トレース
質問 「何が壊れたか?」 「なぜ壊れたか?」
適する状況 単純なシステム 分散システム、マイクロサービス

重要な洞察: オブザーバビリティは監視を置き換えるものではなく、補完するものです。両方必要です。


メトリクス監視:Prometheus

Prometheus とは

オープンソースの監視・アラートツールキットです。CNCF(Cloud Native Computing Foundation)の 2 番目のプロジェクトとして採択されました。

アーキテクチャ

┌─────────────────────────────────────────────┐
│          Prometheus Server                   │
│  ┌─────────┐    ┌─────────┐                │
│  │ TSDB    │    │ Alert   │                │
│  │(時系列DB)│    │ Manager │                │
│  └─────────┘    └─────────┘                │
└─────────┬─────────────────┬─────────────────┘
          │ Pull             │
    ┌─────▼─────┐      ┌────▼─────┐
    │  Job 1    │      │  Job 2   │
    │ (Node)    │      │ (App)    │
    └───────────┘      └──────────┘
          │                    │
    ┌─────▼─────┐      ┌──────▼─────┐
    │ Exporter  │      │ Instrument │
    └───────────┘      └────────────┘

主要コンポーネント

コンポーネント 説明
Prometheus Server メトリクス収集・保存・クエリ処理
Exporter 外部システムからメトリクスをエクスポート
Pushgateway 短期間ジョブ用メトリクス受付
Alertmanager アラート通知管理

インストール(Docker)

# docker-compose.yml
version: '3.8'

services:
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
    restart: unless-stopped

volumes:
  prometheus_data:

設定ファイル(prometheus.yml)

global:
  scrape_interval: 15s      # 収集間隔
  evaluation_interval: 15s  # ルール評価間隔

# アラートルール
rule_files:
  - "alerts.yml"

# スクレイプ設定
scrape_configs:
  # Prometheus 自身
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # Node Exporter(サーバー監視)
  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']

  # アプリケーション
  - job_name: 'app'
    static_configs:
      - targets: ['app:8080']
    metrics_path: '/metrics'

主要メトリクス

Prometheus は 4 つのメトリクス型をサポート:

説明
Counter 増加のみ(リセットあり) リクエスト数、エラー数
Gauge 増減する値 CPU 使用率、メモリ使用量
Histogram 分布(ヒストグラム) レイテンシ、レスポンスタイム
Summary 分布(パーセンタイル) レイテンシの 95 パーセンタイル

PromQL(クエリ言語)

# 基本クエリ
http_requests_total                    # 全リクエスト数
node_cpu_seconds_total                 # CPU 時間

# 変化率(5 分間)
rate(http_requests_total[5m])

# 平均(5 分間)
avg_over_time(node_memory_usage[5m])

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

#  aggregated
sum(rate(http_requests_total[5m])) by (service)

アラート設定(alerts.yml)

groups:
  - name: example
    rules:
      # サービスダウン
      - alert: ServiceDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Service {{ $labels.job }} is down"
          description: "{{ $labels.instance }} has been down for more than 1 minute."

      # 高 CPU
      - alert: HighCPU
        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 on {{ $labels.instance }}"
          description: "CPU usage is above 80% for more than 5 minutes."

      # メモリ不足
      - alert: LowMemory
        expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 < 10
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Low memory on {{ $labels.instance }}"
          description: "Memory available is below 10%."

ログ管理:ELK スタック

ELK スタックとは

Elasticsearch、Logstash、Kibanaの 3 つのオープンソースプロジェクトを統合したログ管理ソリューションです。

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  Logstash   │ →  │Elasticsearch│ ←  │   Kibana    │
│ (収集・変換) │    │  (保存・検索) │    │  (可視化)   │
└─────────────┘    └─────────────┘    └─────────────┘
       ↑
┌──────┴──────┐
│  各種ログ    │
│ (App, Nginx)│
└─────────────┘

近年: Filebeat などの軽量フォワーダーを追加しELK StackまたはElastic Stackと呼ばれます。


各コンポーネント

コンポーネント 説明
Elasticsearch 分散型検索・分析エンジン
Logstash ログ収集・変換パイプライン
Kibana 可視化・ダッシュボード
Filebeat 軽量ログフォワーダー

Docker Compose 設定

version: '3.8'

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - "9200:9200"
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data

  logstash:
    image: docker.elastic.co/logstash/logstash:8.11.0
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    ports:
      - "5000:5000"
    depends_on:
      - elasticsearch

  kibana:
    image: docker.elastic.co/kibana/kibana:8.11.0
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch

  filebeat:
    image: docker.elastic.co/filebeat/filebeat:8.11.0
    volumes:
      - ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
      - /var/log:/var/log:ro
    depends_on:
      - elasticsearch

volumes:
  elasticsearch_data:

Logstash 設定(logstash.conf)

input {
  beats {
    port => 5000
  }

  tcp {
    port => 5001
    codec => json
  }
}

filter {
  # 日付パース
  date {
    match => ["timestamp", "ISO8601"]
  }

  # JSON パース
  json {
    source => "message"
    target => "parsed"
  }

  # 不要フィールド削除
  mutate {
    remove_field => ["@version", "host"]
  }

  # 環境フィールド追加
  if [environment] == "" {
    mutate {
      add_field => { "environment" => "production" }
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "logs-%{+YYYY.MM.dd}"
  }

  # デバッグ用標準出力
  stdout {
    codec => rubydebug
  }
}

Filebeat 設定(filebeat.yml)

filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/log/*.log
    fields:
      environment: production
    multiline.pattern: '^\d{4}-\d{2}-\d{2}'
    multiline.negate: true
    multiline.match: after

  - type: container
    paths:
      - /var/lib/docker/containers/*/*.log

processors:
  - add_host_metadata: ~
  - add_cloud_metadata: ~

output.elasticsearch:
  hosts: ["elasticsearch:9200"]
  indices:
    - index: "filebeat-%{+yyyy.MM.dd}"

logging.level: info

分散トレース:Jaeger

分散トレースとは

マイクロサービス間でのリクエストフローを追跡する技術です。

【ユーザーリクエスト】
     │
     ▼
┌─────────────┐
│ API Gateway │ ← トレース開始(ルートスパン)
└──────┬──────┘
       │
       ├─────────────┐
       ▼             ▼
┌───────────┐  ┌───────────┐
│ User Svc  │  │ Order Svc │ ← 子スパン
└─────┬─────┘  └─────┬─────┘
      │              │
      ▼              ▼
┌───────────┐  ┌───────────┐
│  Database │  │  Payment  │ ← 孫スパン
└───────────┘  └───────────┘

【各スパンに共通の Trace ID】

Jaeger アーキテクチャ

┌──────────────────────────────────────────┐
│            Application                    │
│  (OpenTelemetry SDK Instrumented)         │
└────────────┬─────────────────────────────┘
             │
             ▼
┌──────────────────────────────────────────┐
│            Jaeger Agent                   │
│  (サイドカーとしてデプロイ)                │
└────────────┬─────────────────────────────┘
             │
             ▼
┌──────────────────────────────────────────┐
│          Jaeger Collector                 │
└────────────┬─────────────────────────────┘
             │
       ┌─────┴─────┐
       ▼           ▼
┌───────────┐ ┌───────────┐
│ Cassandra │ │   Query   │
│   (DB)    │ │   Service │
└───────────┘ └─────┬─────┘
                    │
                    ▼
              ┌───────────┐
              │    UI     │
              └───────────┘

Docker Compose 設定

version: '3.8'

services:
  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "16686:16686"  # UI
      - "4317:4317"    # OTLP gRPC
      - "4318:4318"    # OTLP HTTP
    environment:
      - COLLECTOR_OTLP_ENABLED=true
    restart: unless-stopped

アプリケーション計装(Node.js 例)

// OpenTelemetry 設定
const opentelemetry = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');

const sdk = new opentelemetry.NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'http://jaeger:4317'
  }),
  instrumentations: [
    getNodeAutoInstrumentations()
  ],
});

sdk.start();

// マニュアル計装
const { trace, context } = require('@opentelemetry/api');
const tracer = trace.getTracer('my-app');

async function processOrder(orderId) {
  return tracer.startActiveSpan('processOrder', async (span) => {
    try {
      span.setAttribute('order.id', orderId);

      // 処理実行
      await validateOrder(orderId);
      await chargePayment(orderId);
      await shipOrder(orderId);

      span.setStatus({ code: 0 }); // OK
    } catch (error) {
      span.setStatus({ code: 2, message: error.message }); // ERROR
      span.recordException(error);
      throw error;
    } finally {
      span.end();
    }
  });
}

可視化:Grafana

Grafana とは

メトリクス・ログ・トレースのための可視化プラットフォームです。

主要機能

  1. ダッシュボード: 複数のパネルを配置
  2. データソース接続: Prometheus、Elasticsearch、Jaeger など
  3. アラート: 閾値ベースの通知
  4. テンプレート変数: 動的なダッシュボード

Docker Compose 設定

version: '3.8'

services:
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=grafana123
      - GF_INSTALL_PLUGINS=grafana-piechart-panel
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
      - ./grafana/dashboards:/var/lib/grafana/dashboards
    restart: unless-stopped

volumes:
  grafana_data:

データソース設定(provisioning/datasources.yml)

apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: true

  - name: Elasticsearch
    type: elasticsearch
    access: proxy
    url: http://elasticsearch:9200
    database: "logs-*"
    basicAuth: false

  - name: Jaeger
    type: jaeger
    access: proxy
    url: http://jaeger:16686

ダッシュボード設定(provisioning/dashboards.yml)

apiVersion: 1

providers:
  - name: 'default'
    orgId: 1
    folder: ''
    type: file
    disableDeletion: false
    updateIntervalSeconds: 10
    options:
      path: /var/lib/grafana/dashboards

実践的クエリ例

システムダッシュボード(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 - (node_filesystem_avail_bytes / node_filesystem_size_bytes)) * 100

# ネットワークトラフィック
sum(rate(node_network_receive_bytes_total[5m])) by (instance)

アプリケーションダッシュボード

# リクエストレート(5 分間平均)
sum(rate(http_requests_total[5m])) by (service)

# エラーレート
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

# レイテンシ(P95)
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service))

# HTTP ステータス分布
sum(rate(http_requests_total[5m])) by (status)

アラート設計のベストプラクティス

アラートレベルの定義

レベル 説明 対応時間 通知方法
Critical サービス停止、データ損失 即時(24 時間 365 日) PagerDuty、電話
Warning 性能劣化、容量逼迫 営業時間内 Slack、メール
Info 参考情報 定例レビュー ダッシュボードのみ

アラート設計原則

✅ 良いアラート
・アクション可能(何かすべきことがある)
・誤報が少ない
・ビジネスインパクトに基づく
・自動復旧を待つ(一時的なスパイク)

❌ 悪いアラート
・「何かおかしい」だけ
・誤報が多くオンコール疲れ
・技術指標のみ(ビジネス無関係)
・一時的なスパイクで毎回流れる

アラート例(Prometheus)

groups:
  - name: application
    rules:
      # サービスダウン(Critical)
      - alert: ServiceDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "サービスダウン:{{ $labels.job }}"
          description: "{{ $labels.instance }} が 1 分以上ダウンしています"

      # エラーレート増加(Warning)
      - alert: HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m]))
          / sum(rate(http_requests_total[5m])) > 0.05
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "エラーレート上昇"
          description: "5 分間のエラーレートが 5% を超えています"

      # レイテンシ悪化(Warning)
      - alert: HighLatency
        expr: |
          histogram_quantile(0.95,
            sum(rate(http_request_duration_seconds_bucket[5m]))
            by (le, service)
          ) > 1
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "レイテンシ悪化"
          description: "P95 レイテンシが 1 秒を超えています"

運用プラクティス

1. ゴールデンシグナルの監視

Google SRE が推奨する 4 つの主要指標:

シグナル 説明
レイテンシ リクエスト処理時間 P95、P99
トラフィック システム負荷 リクエスト数、QPS
エラー 失敗率 エラーレート、5xx
サチュレーション リソース飽和 CPU、メモリ、ディスク

2. RED メソッド(マイクロサービス向け)

指標 説明
Rate リクエストレート
Errors エラーレート
Duration レスポンス時間

3. USE メソッド(インフラ向け)

指標 説明
Utilization リソース使用率
Saturation リソース飽和度
Errors ハードウェアエラー

監視システム構築のステップ

ステップ 1: 可観測性の要件定義

【質問事項】
1. 何を監視する必要がありますか?
   - インフラ(サーバー、ネットワーク)
   - アプリケーション(API、バッチ)
   - ビジネス(売上、ユーザー数)

2. 誰が監視データを使いますか?
   - 開発者
   - 運用チーム
   - 経営陣

3. どのくらいの頻度でデータが必要ですか?
   - リアルタイム(秒単位)
   - 近リアルタイム(分単位)
   - 集計(時間・日単位)

ステップ 2: ツール選定

【小规模システム】
・Prometheus + Grafana(メトリクス)
・Filebeat + Elasticsearch(ログ)

【中規模システム】
・Prometheus + Grafana
・ELK Stack
・Jaeger(トレース)

【大規模システム】
・マネージドサービス検討(Datadog, New Relic)
・カスタムメトリクス対応
・自動スケーリング連携

ステップ 3: 実装・計装

【チェックリスト】
□ メトリクス収集ポイント定義
□ ログフォーマット標準化(JSON 推奨)
□ トレースコンテキスト伝播実装
□ ダッシュボード作成
□ アラートルール設定
□ 通知チャネル設定

ステップ 4: 運用・改善

【継続的改善】
・アラート精度の定期的レビュー
・誤報アラートの削除・調整
・新しいメトリクスの追加
・ダッシュボードの更新
・インシデントからの学習

よくある落とし穴

1. アラート疲れ

【問題】
・多すぎるアラート
・誤報の繰り返し
・結果:重要なアラートを見逃す

【対策】
・アラート件数を制限(10 件以下)
・閾値調整
・自動復旧を待つ

2. メトリクス過多

【問題】
・全てを収集しようとする
・ストレージコスト増大
・ノイズに埋もれる

【対策】
・ビジネス価値に基づく選別
・サンプリング検討
・集計粒度の調整

3. 計装の遅れ

【問題】
・本番後付けで計装
・データ不足で調査不能

【対策】
・開発初期から計装を設計
・標準ライブラリ用意
・コードレビューでチェック

まとめ

監視とオブザーバビリティの核心:

  1. 監視: 問題を検知する(何がおかしいか)
  2. オブザーバビリティ: 問題を理解する(なぜおかしいか)
  3. 3 本柱: メトリクス、ログ、トレース
  4. 主要ツール: Prometheus、ELK、Jaeger、Grafana
  5. 継続的改善: アラート疲れを防ぎ、精度向上

「監視は手段であって目的ではない。健全なシステム運用のために」


参考資料

免責事項 — 掲載情報は執筆時点のものです。料金・機能は変更される場合があります。最新情報は各公式サイトをご確認ください。