目次

この記事の内容

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

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

従来の監視(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. 継続的改善: アラート疲れを防ぎ、精度向上

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


参考資料

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