サイトアイコンmaita tomoya dev io

監視ツール

DevOps/インフラ

監視ツール

なぜ監視が必要か

Webアプリケーションやインフラを運用していると、さまざまな問題が発生する。

  • サーバーのCPU使用率が100%に達してレスポンスが返らなくなる
  • メモリリークによりアプリケーションがクラッシュする
  • ディスク容量が不足してデータベースが書き込みエラーを起こす
  • ネットワーク障害でサービスにアクセスできなくなる
  • デプロイ後にエラー率が急増する

これらの問題は、監視していなければユーザーからの報告で初めて気づくことになる。ユーザーが気づくということは、既にサービスに影響が出ているということ。

監視の目的をまとめると以下の通り。

目的説明
障害の早期検知問題が大きくなる前に検知するCPU使用率が80%を超えたらアラート
パフォーマンス改善ボトルネックを特定し改善するレスポンスタイムが遅いAPIを特定
SLAの維持サービスレベル契約を守る可用性99.9%を維持
キャパシティプランニング将来の需要を予測するトラフィック増加に備えてスケール
セキュリティ不正アクセスや異常な挙動を検知するログイン失敗の急増を検知

身近な例で言えば、監視は「健康診断」のようなもの。体(サーバー)に問題がないか定期的にチェックし、異常があれば早めに対処する。病気(障害)になってから気づくのではなく、予兆の段階で発見することが重要。

監視の4つの柱

現代の監視は以下の4つの柱で構成される。

1. メトリクス(Metrics)

数値データの時系列記録。CPU使用率、メモリ使用量、リクエスト数など。

例: CPU使用率の時系列データ
時刻        CPU使用率
10:00       25%
10:01       28%
10:02       45%
10:03       82%  ← 異常な上昇を検知
10:04       95%  ← アラート発報

2. ログ(Logs)

アプリケーションやシステムが出力するテキスト形式の記録。

[2024-01-15 10:03:15] ERROR: Database connection timeout after 30s
[2024-01-15 10:03:15] ERROR: Failed to process request /api/users - 500 Internal Server Error
[2024-01-15 10:03:16] WARN: Connection pool exhausted, waiting for available connection

3. トレース(Traces)

1つのリクエストがシステム内でどのように処理されたかの追跡記録。マイクロサービス環境で特に重要。

リクエスト: GET /api/orders/123
  ├── API Gateway (2ms)
  ├── 認証サービス (15ms)
  ├── 注文サービス (45ms)
  │   ├── DBクエリ (30ms)  ← ここがボトルネック
  │   └── キャッシュ確認 (2ms)
  └── レスポンス生成 (3ms)
合計: 67ms

4. アラート(Alerts)

異常を検知したとき、担当者に通知する仕組み。

[Critical] web-server-01: CPU使用率が95%を超えました
発生時刻: 2024-01-15 10:04:00 JST
持続時間: 2分
対応: オンコール担当者に通知済み

4つの柱の関係

答えられる問いデータの特徴
メトリクス「何が起きているか」数値、集約しやすい、長期保存向き
ログ「なぜ起きたか」テキスト、詳細、データ量が多い
トレース「どこで起きたか」リクエストの流れ、因果関係
アラート「誰に知らせるか」通知、エスカレーション

障害対応の流れ: アラートで気づき → メトリクスで影響範囲を把握 → トレースでボトルネックを特定 → ログで原因を調査

可観測性(Observability)とは

可観測性(Observability、略してo11y)は、「システムの外部出力から内部の状態を理解できる度合い」を指す概念。

「監視(Monitoring)」が「既知の問題を検知する」のに対し、「可観測性」は「未知の問題も含めてシステムの状態を理解できる」という、より広い概念。

比較項目監視(Monitoring)可観測性(Observability)
対象既知の問題未知の問題も含む
アプローチ事前に「何を監視するか」定義データから「何が起きているか」探索
問い「この値は正常か?」「なぜこの状態になったか?」
CPU使用率が閾値を超えたらアラート任意の条件でデータを掘り下げて原因を調査

可観測性を高めるには、メトリクス・ログ・トレースの3つのデータ(テレメトリデータ)を統合的に収集・分析できる仕組みが必要。

監視の対象

インフラ監視

監視項目閾値の目安ツール例
CPU使用率80%以上で警告、95%以上で緊急CloudWatch, Prometheus
メモリ使用率85%以上で警告CloudWatch, Prometheus
ディスク使用率80%以上で警告、90%以上で緊急CloudWatch, Prometheus
ネットワーク帯域通常時の3倍以上で警告CloudWatch
サーバー死活応答なしで即座にアラートPing監視, ヘルスチェック

アプリケーション監視

監視項目閾値の目安ツール例
レスポンスタイムP95が1秒以上で警告New Relic, Datadog
エラーレート1%以上で警告、5%以上で緊急Sentry, Datadog
スループット通常の50%以下で警告Prometheus, Grafana
HTTPステータスコード5xx系が増加したら警告CloudWatch, Prometheus
データベースクエリスロークエリが増加したら警告RDS Performance Insights

ネットワーク監視

監視項目説明
レイテンシ通信の遅延時間
パケットロス通信の損失率
DNS解決時間ドメイン名の解決にかかる時間
SSL証明書有効期限期限切れの30日前にアラート

セキュリティ監視

監視項目説明
不正ログイン試行短時間に多数のログイン失敗
権限昇格不正なsudoアクセス
異常なトラフィックDDoS攻撃の兆候
脆弱性既知の脆弱性を持つパッケージ

主要な監視ツール一覧

ツール種類特徴コスト適したケース
AWS CloudWatchクラウドネイティブAWS完全統合従量課金AWSインフラの監視
DatadogSaaS統合監視プラットフォーム高め大規模な本番環境
GrafanaOSSダッシュボード/可視化無料(OSS版)メトリクスの可視化
PrometheusOSSメトリクス収集/アラート無料コンテナ/Kubernetes環境
New RelicSaaSAPM中心の統合プラットフォーム無料枠ありアプリケーション性能監視
PagerDutySaaSインシデント管理/オンコール有料アラート管理/エスカレーション
SentrySaaS/OSSエラー追跡無料枠ありフロントエンド/バックエンドのエラー監視
ELK StackOSSログ収集/分析/可視化無料(OSS版)大量ログの分析

組み合わせパターン

パターン構成コスト適したケース
AWS中心CloudWatch + X-Ray + SNS低〜中AWSのみの小〜中規模
OSS中心Prometheus + Grafana + Loki低(サーバー費用のみ)コスト重視/Kubernetes環境
SaaS中心Datadog + PagerDuty大規模/運用負荷を下げたい
ハイブリッドPrometheus + Grafana + Sentry + PagerDutyバランス重視

AWS CloudWatch

AWS CloudWatchはAWSに組み込まれた監視サービス。AWSのリソースを使っていれば、追加設定なしで基本的なメトリクスが自動収集される。

主な機能

機能説明
メトリクスAWSリソースの数値データ(CPU、メモリ等)
ログアプリケーションやシステムのログ収集・検索
アラームメトリクスの閾値に基づくアラート
ダッシュボードメトリクスの可視化
Events/EventBridgeAWSリソースの状態変化をトリガーに自動処理

メトリクスの設定例(Terraformで設定)

# CPU使用率のアラーム
resource "aws_cloudwatch_metric_alarm" "high_cpu" {
  alarm_name          = "high-cpu-utilization"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2              # 2回連続で閾値を超えたら
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = 300            # 5分間隔
  statistic           = "Average"
  threshold           = 80             # 80%以上
  alarm_description   = "CPU使用率が80%を超えました"

  dimensions = {
    InstanceId = aws_instance.web.id
  }

  alarm_actions = [aws_sns_topic.alerts.arn]  # SNSトピックに通知
  ok_actions    = [aws_sns_topic.alerts.arn]  # 回復時にも通知
}

# SNSトピック(通知先)
resource "aws_sns_topic" "alerts" {
  name = "monitoring-alerts"
}

resource "aws_sns_topic_subscription" "email" {
  topic_arn = aws_sns_topic.alerts.arn
  protocol  = "email"
  endpoint  = "oncall@example.com"
}

CloudWatch Logsの設定

# ロググループ
resource "aws_cloudwatch_log_group" "app" {
  name              = "/app/myapp"
  retention_in_days = 30  # 30日間保持
}

# メトリクスフィルター(ログからメトリクスを作成)
resource "aws_cloudwatch_log_metric_filter" "error_count" {
  name           = "error-count"
  pattern        = "ERROR"
  log_group_name = aws_cloudwatch_log_group.app.name

  metric_transformation {
    name      = "ErrorCount"
    namespace = "MyApp"
    value     = "1"
  }
}

カスタムメトリクスの送信(Node.js)

// AWS SDKを使ってカスタムメトリクスを送信
const { CloudWatchClient, PutMetricDataCommand } = require('@aws-sdk/client-cloudwatch')

const client = new CloudWatchClient({ region: 'ap-northeast-1' })

async function sendMetric(metricName, value, unit = 'Count') {
  const command = new PutMetricDataCommand({
    Namespace: 'MyApp',
    MetricData: [
      {
        MetricName: metricName,
        Value: value,
        Unit: unit,
        Timestamp: new Date(),
        Dimensions: [
          {
            Name: 'Environment',
            Value: 'production',
          },
        ],
      },
    ],
  })

  await client.send(command)
}

// 使用例
sendMetric('ActiveUsers', 150, 'Count')
sendMetric('ResponseTime', 245, 'Milliseconds')

Prometheus + Grafana

Prometheus + Grafanaは、オープンソースの監視スタックとして最も広く使われている組み合わせ。特にKubernetes環境での監視に強い。

Prometheusの仕組み

[アプリケーション] ←─ スクレイプ ─── [Prometheus Server] ──→ [Grafana]
    /metrics                           ↓                    (可視化)
    エンドポイント              [時系列データベース]
                                       ↓
                               [Alertmanager] ──→ Slack/Email/PagerDuty
                                (アラート管理)

Prometheusの特徴的な点は「プル型」であること。監視対象のアプリケーションが /metrics エンドポイントでメトリクスを公開し、Prometheusが定期的にそのデータを取りに行く(スクレイプする)。

Prometheus設定ファイル

# prometheus.yml
global:
  scrape_interval: 15s # 15秒ごとにメトリクスを収集
  evaluation_interval: 15s # 15秒ごとにアラートルールを評価

# アラートルール
rule_files:
  - 'alert_rules.yml'

# Alertmanagerの設定
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:
          - 'web1:9100'
          - 'web2:9100'
          - 'db1:9100'

  # アプリケーション
  - job_name: 'myapp'
    metrics_path: '/metrics'
    static_configs:
      - targets: ['app1:3000', 'app2:3000']

PromQL基礎

PromQLはPrometheusのクエリ言語。メトリクスの検索・集計に使用する。

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

# HTTPリクエストのレート(1秒あたりのリクエスト数)
rate(http_requests_total[5m])

# HTTPエラー率
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100

# レスポンスタイムの95パーセンタイル
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))

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

# ディスク使用率
100 - ((node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100)

PromQLの基本構文

構文説明
メトリクス名メトリクスを直接指定http_requests_total
ラベルフィルタリング{status="200"}
rate()増加率(カウンターに使う)rate(metric[5m])
sum()合計sum(metric)
avg()平均avg(metric)
by()グルーピングsum by(instance)(metric)
histogram_quantile()パーセンタイルhistogram_quantile(0.95, metric)

アラートルール

# alert_rules.yml
groups:
  - name: instance_alerts
    rules:
      # サーバーダウン
      - alert: InstanceDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: 'インスタンス {{ $labels.instance }} がダウンしています'
          description: '{{ $labels.instance }} が1分以上応答していません。'

      # CPU使用率が高い
      - alert: HighCpuUsage
        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: 'CPU使用率が80%を超えています'
          description: '{{ $labels.instance }} のCPU使用率が {{ $value }}% です。'

      # メモリ使用率が高い
      - alert: HighMemoryUsage
        expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: 'メモリ使用率が85%を超えています'

  - name: application_alerts
    rules:
      # エラー率が高い
      - alert: HighErrorRate
        expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100 > 5
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: 'HTTPエラー率が5%を超えています'

      # レスポンスタイムが遅い
      - alert: HighLatency
        expr: histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) > 1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: 'P95レスポンスタイムが1秒を超えています'

Node.jsアプリケーションでPrometheusメトリクスを公開

// metrics.js
const promClient = require('prom-client')

// デフォルトメトリクス(Node.jsのメモリ使用量等)を収集
promClient.collectDefaultMetrics({ prefix: 'myapp_' })

// カスタムメトリクス: HTTPリクエスト数
const httpRequestsTotal = new promClient.Counter({
  name: 'http_requests_total',
  help: 'HTTPリクエストの総数',
  labelNames: ['method', 'path', 'status'],
})

// カスタムメトリクス: レスポンスタイム
const httpRequestDuration = new promClient.Histogram({
  name: 'http_request_duration_seconds',
  help: 'HTTPリクエストの処理時間(秒)',
  labelNames: ['method', 'path', 'status'],
  buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
})

// カスタムメトリクス: アクティブな接続数
const activeConnections = new promClient.Gauge({
  name: 'active_connections',
  help: '現在のアクティブな接続数',
})

module.exports = {
  httpRequestsTotal,
  httpRequestDuration,
  activeConnections,
  register: promClient.register,
}
// server.js
const express = require('express')
const { httpRequestsTotal, httpRequestDuration, activeConnections, register } = require('./metrics')

const app = express()

// メトリクス計測ミドルウェア
app.use((req, res, next) => {
  activeConnections.inc()
  const end = httpRequestDuration.startTimer()

  res.on('finish', () => {
    activeConnections.dec()
    const labels = {
      method: req.method,
      path: req.route ? req.route.path : req.path,
      status: res.statusCode,
    }
    httpRequestsTotal.inc(labels)
    end(labels)
  })

  next()
})

// メトリクスエンドポイント
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType)
  res.end(await register.metrics())
})

// アプリケーションのエンドポイント
app.get('/api/users', (req, res) => {
  res.json({ users: [] })
})

app.listen(3000, () => {
  console.log('Server running on port 3000')
})

ログ管理

ELK Stack

ELK Stack(Elastic Stack)は、ログの収集・保存・検索・可視化を行うオープンソースツール群。

[アプリケーション] → [Filebeat/Logstash] → [Elasticsearch] → [Kibana]
    ログ出力            ログ収集/変換          ログ保存/検索      可視化
コンポーネント役割例え
Elasticsearchログの保存と全文検索図書館の蔵書検索システム
Logstashログの収集・変換・転送郵便局の仕分け
Kibanaログの可視化・分析統計グラフを作るツール
Filebeat軽量なログ収集エージェント各家庭のポスト

構造化ログ

ログは構造化(JSON形式)にすることで、検索や分析が格段に容易になる。

// 悪い例: 非構造化ログ
console.log('User 123 logged in from 192.168.1.100')

// 良い例: 構造化ログ(JSON)
const logger = require('pino')()

logger.info({
  event: 'user_login',
  userId: 123,
  ip: '192.168.1.100',
  userAgent: 'Mozilla/5.0...',
  timestamp: new Date().toISOString(),
})
// 出力: {"level":30,"event":"user_login","userId":123,"ip":"192.168.1.100",...}

ログレベル

レベル用途
FATALアプリケーションが動作不能データベース接続が完全に失敗
ERRORエラーが発生したが動作は継続APIリクエストが失敗
WARN注意が必要な状態メモリ使用率が高い
INFO正常な動作の記録ユーザーがログイン
DEBUG開発時のデバッグ情報クエリの実行内容
TRACE最も詳細な情報関数の引数と戻り値

本番環境ではINFO以上、開発環境ではDEBUG以上を出力するのが一般的。

CloudWatch Logsへのログ送信(Node.js)

const winston = require('winston')
const WinstonCloudWatch = require('winston-cloudwatch')

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    // コンソール出力
    new winston.transports.Console(),
    // CloudWatch Logsに送信
    new WinstonCloudWatch({
      logGroupName: '/app/myapp',
      logStreamName: `${process.env.HOSTNAME}-${new Date().toISOString().split('T')[0]}`,
      awsRegion: 'ap-northeast-1',
      jsonMessage: true,
    }),
  ],
})

// 使用例
logger.info('Application started', { port: 3000, env: 'production' })
logger.error('Database query failed', { query: 'SELECT...', error: 'timeout' })

アラート設計

アラート設計の原則

良いアラートは「対応が必要なこと」だけを通知する。対応不要な通知が多いと、重要なアラートが埋もれてしまう(アラート疲れ)。

原則説明
対応可能アラートを受けた人が何かしらの対応ができること
緊急性があるすぐに対応が必要なものだけアラートにする
明確何が起きているか、何をすべきか明確にする
適切なルーティング適切なチーム/担当者に通知する

重要度レベル

レベル基準対応時間通知方法
Criticalサービスダウン、データ損失の危険即時(5分以内)電話 + Slack + メール
Warningパフォーマンス低下、閾値超過30分以内Slack + メール
Info注意が必要だが緊急ではない営業時間内メール

アラート疲れの防止

対策説明
閾値の適切な設定厳しすぎる閾値は緩和する
集約同じ原因のアラートをグループ化
抑制メンテナンス中はアラートを一時停止
自動復旧自動で復旧できるものはアラートにしない
定期的な見直し無視されがちなアラートは閾値を見直す

Alertmanagerの設定例

# alertmanager.yml
global:
  resolve_timeout: 5m

route:
  group_by: ['alertname', 'instance']
  group_wait: 30s # グループ化の待機時間
  group_interval: 5m # 同じグループのアラート間隔
  repeat_interval: 4h # 同じアラートの再通知間隔
  receiver: 'default'

  routes:
    # Criticalアラートはオンコールチームへ
    - match:
        severity: critical
      receiver: 'oncall'
      continue: true

    # Warningアラートはエンジニアリングチームへ
    - match:
        severity: warning
      receiver: 'engineering'

receivers:
  - name: 'default'
    slack_configs:
      - channel: '#alerts-general'
        send_resolved: true

  - name: 'oncall'
    slack_configs:
      - channel: '#alerts-critical'
        send_resolved: true
    pagerduty_configs:
      - service_key: 'your-pagerduty-key'

  - name: 'engineering'
    slack_configs:
      - channel: '#alerts-engineering'
        send_resolved: true

メトリクスの種類

システムメトリクス

メトリクス説明警告閾値の目安緊急閾値の目安
CPU使用率プロセッサの使用率80%95%
メモリ使用率RAMの使用率85%95%
ディスク使用率ストレージの使用率80%90%
ディスクI/O読み書きの速度通常の3倍通常の5倍
ネットワークI/O送受信の帯域幅通常の3倍通常の5倍
ロードアベレージCPUの負荷(実行待ちプロセス数)CPUコア数と同じCPUコア数の2倍

アプリケーションメトリクス

メトリクス説明計算方法
レスポンスタイムリクエストの処理時間P50, P95, P99で評価
エラーレートエラーの発生率5xxレスポンス / 全リクエスト
スループット単位時間あたりの処理量リクエスト数/秒(RPS)
Apdexユーザー満足度スコア(満足+許容/2) / 全体
サチュレーションリソースの飽和度キュー長、待機スレッド数

USE/REDメソッド

メトリクスを体系的に収集するためのフレームワーク。

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

項目意味
Utilization(使用率)リソースの使用割合CPU 75%
Saturation(飽和度)リソースの待ち行列ロードアベレージ 4.0
Errors(エラー)エラーの発生数ディスクI/Oエラー 3回

REDメソッド(アプリケーション向け):

項目意味
Rate(レート)1秒あたりのリクエスト数500 RPS
Errors(エラー)失敗したリクエストの割合0.5%
Duration(処理時間)リクエストの処理にかかる時間P95: 200ms

APM(Application Performance Monitoring)

APMはアプリケーションのパフォーマンスを詳細に監視する仕組み。コードレベルでのボトルネック特定が可能。

APMが提供する情報

機能説明
トランザクション追跡リクエストの処理フロー全体を可視化
データベースクエリ分析遅いクエリの特定
外部API呼び出し外部サービスへの通信時間
エラー分析エラーの発生箇所とスタックトレース
リアルタイムマップサービス間の依存関係を可視化

主要なAPMツール

ツール特徴対応言語
New Relic包括的なAPMNode.js, Python, Java, Go等
Datadog APMインフラ監視と統合多数
Elastic APMELK Stackと統合多数
AWS X-RayAWSサービスと統合Node.js, Python, Java等
JaegerOSSの分散トレーシング多数

ヘルスチェック

ヘルスチェックはアプリケーションの死活監視のためのエンドポイント。ロードバランサーやオーケストレーターがアプリケーションの状態を確認するために使用する。

ヘルスチェックエンドポイントの設計

const express = require('express')
const app = express()

// シンプルなヘルスチェック(Liveness)
// アプリケーションが起動しているかどうか
app.get('/health', (req, res) => {
  res.status(200).json({ status: 'ok' })
})

// 詳細なヘルスチェック(Readiness)
// アプリケーションがリクエストを処理できる状態かどうか
app.get('/readiness', async (req, res) => {
  const checks = {}
  let isHealthy = true

  // データベース接続チェック
  try {
    await db.query('SELECT 1')
    checks.database = { status: 'ok' }
  } catch (error) {
    checks.database = { status: 'error', message: error.message }
    isHealthy = false
  }

  // Redis接続チェック
  try {
    await redis.ping()
    checks.redis = { status: 'ok' }
  } catch (error) {
    checks.redis = { status: 'error', message: error.message }
    isHealthy = false
  }

  // 外部API接続チェック
  try {
    const response = await fetch('https://api.external-service.com/health')
    checks.externalApi = { status: response.ok ? 'ok' : 'error' }
    if (!response.ok) isHealthy = false
  } catch (error) {
    checks.externalApi = { status: 'error', message: error.message }
    isHealthy = false
  }

  const statusCode = isHealthy ? 200 : 503
  res.status(statusCode).json({
    status: isHealthy ? 'healthy' : 'unhealthy',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    checks,
  })
})

Liveness vs Readiness

チェック目的失敗時の対応
Liveness(/healthアプリケーションが生きているかコンテナを再起動
Readiness(/readinessリクエストを受けられる状態かロードバランサーから外す

SLI / SLO / SLA

概念の違い

概念正式名称意味決める人
SLIService Level Indicatorサービスの品質を測定する指標エンジニアリングチーム
SLOService Level ObjectiveSLIの目標値エンジニアリング + ビジネスチーム
SLAService Level Agreement顧客との契約上の保証値ビジネスチーム + 法務

具体例

SLI(指標): Webサイトのレスポンスタイム(P95)
SLO(目標): P95レスポンスタイムが500ms以下を99.9%の時間維持する
SLA(契約): 月間の可用性99.9%を保証。違反時はクレジット返金。

可用性の計算

可用性月間の許容ダウンタイム年間の許容ダウンタイム
99%(ツーナイン)約7.3時間約3.65日
99.9%(スリーナイン)約43.8分約8.77時間
99.95%約21.9分約4.38時間
99.99%(フォーナイン)約4.38分約52.6分
99.999%(ファイブナイン)約26.3秒約5.26分

99.9%から99.99%への改善は、数字上は0.09%しか違わないが、許容されるダウンタイムは約10分の1になる。高い可用性を実現するにはインフラ・アーキテクチャ・運用の全てを高度に設計する必要があり、コストも大幅に増加する。

エラーバジェット

SLOの目標値と実際の達成率の差分を「エラーバジェット」と呼ぶ。

SLO: 可用性 99.9%(月間)
実績: 可用性 99.95%

エラーバジェット残り:
  月間の許容ダウンタイム: 43.8分
  実際のダウンタイム: 21.9分
  残りバジェット: 21.9分

→ まだバジェットがあるので、新機能のリリースや実験が可能
→ バジェットを使い切ったら、安定化作業を優先する

インシデント対応

インシデント対応フロー

検知(Detection)
  ↓ アラートの受信、またはユーザー報告
対応(Response)
  ↓ 影響範囲の把握、一次対応
復旧(Recovery)
  ↓ サービスの正常化
振り返り(Postmortem)
  ↓ 原因分析、再発防止策
改善(Improvement)
  → 防止策の実装

インシデントの重要度

重要度基準対応体制
SEV1(重大)サービス全体がダウン全チーム招集データベース障害
SEV2(高)主要機能が利用不可関連チーム決済処理の失敗
SEV3(中)一部機能に影響担当チーム検索が遅い
SEV4(低)軽微な影響通常の営業時間内UIの表示崩れ

ポストモーテム(振り返り)のテンプレート

# インシデントレポート: [タイトル]

## 概要

- 発生日時: 2024-01-15 10:03 JST
- 検知日時: 2024-01-15 10:05 JST
- 復旧日時: 2024-01-15 10:45 JST
- 影響時間: 42分
- 重要度: SEV2
- 影響範囲: API全体のレスポンスが10秒以上に悪化

## タイムライン

- 10:03 - データベースのCPU使用率が急上昇
- 10:05 - CloudWatchアラートが発報
- 10:08 - オンコール担当者がSlackで確認
- 10:15 - スロークエリが原因と特定
- 10:30 - 問題のクエリを停止
- 10:45 - レスポンスタイムが正常に復帰

## 根本原因

バッチ処理のクエリがインデックスなしのフルテーブルスキャンを実行し、
データベースのCPUを占有した。

## 対策

- [完了] 問題のクエリにインデックスを追加
- [TODO] バッチ処理をリードレプリカで実行するように変更
- [TODO] データベースのスロークエリ監視を強化

ダッシュボード設計のベストプラクティス

ダッシュボードの種類

種類目的更新頻度対象者
概要ダッシュボードサービス全体の健全性を一目で確認リアルタイム全チーム
インフラダッシュボードサーバーリソースの詳細リアルタイムインフラチーム
アプリケーションダッシュボードアプリの性能詳細リアルタイム開発チーム
ビジネスダッシュボードビジネスKPI日次/週次マネジメント

概要ダッシュボードに含めるべき項目

+-------------------------------------------+
|  サービス概要ダッシュボード                 |
+-------------------------------------------+
|                                           |
|  [リクエスト数/秒]  [エラーレート]  [P95]  |
|   1,234 RPS         0.1%          120ms   |
|                                           |
|  [レスポンスタイム推移]                    |
|  ┌─────────────────────┐                  |
|  │  ─── P50            │                  |
|  │  ─── P95            │                  |
|  │  ─── P99            │                  |
|  └─────────────────────┘                  |
|                                           |
|  [HTTPステータスコード割合]                |
|  200: 98.5% | 400: 0.8% | 500: 0.1%     |
|                                           |
|  [インフラ概要]                            |
|  CPU: 45%  Memory: 62%  Disk: 55%        |
|                                           |
+-------------------------------------------+

ダッシュボード設計のルール

ルール説明
最も重要な情報を上部に一目で状況がわかるように
色でステータスを表現緑=正常、黄=警告、赤=異常
時間範囲を統一全パネルの時間軸を合わせる
コンテキストを追加閾値ラインやアノテーション(デプロイマーカー等)
情報過多を避ける1つのダッシュボードに詰め込みすぎない

実践例: Node.jsアプリにPrometheus + Grafanaで監視を導入

Docker Composeを使って、監視環境を構築する完全な例。

docker-compose.yml

services:
  # Node.jsアプリケーション
  app:
    build: .
    ports:
      - '3000:3000'
    environment:
      - NODE_ENV=production
    networks:
      - monitoring

  # Prometheus(メトリクス収集)
  prometheus:
    image: prom/prometheus:v2.48.0
    ports:
      - '9090:9090'
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
      - ./monitoring/alert_rules.yml:/etc/prometheus/alert_rules.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.retention.time=15d'
    networks:
      - monitoring

  # Grafana(可視化)
  grafana:
    image: grafana/grafana:10.2.0
    ports:
      - '3001:3000'
    volumes:
      - grafana_data:/var/lib/grafana
      - ./monitoring/grafana/provisioning:/etc/grafana/provisioning
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_USERS_ALLOW_SIGN_UP=false
    networks:
      - monitoring

  # Node Exporter(サーバーメトリクス)
  node-exporter:
    image: prom/node-exporter:v1.7.0
    ports:
      - '9100:9100'
    networks:
      - monitoring

  # Alertmanager(アラート管理)
  alertmanager:
    image: prom/alertmanager:v0.26.0
    ports:
      - '9093:9093'
    volumes:
      - ./monitoring/alertmanager.yml:/etc/alertmanager/alertmanager.yml
    networks:
      - monitoring

volumes:
  prometheus_data:
  grafana_data:

networks:
  monitoring:
    driver: bridge

monitoring/prometheus.yml

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - 'alert_rules.yml'

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

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

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

  - job_name: 'myapp'
    metrics_path: '/metrics'
    static_configs:
      - targets: ['app:3000']

monitoring/alertmanager.yml

global:
  resolve_timeout: 5m

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'slack'

receivers:
  - name: 'slack'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
        channel: '#alerts'
        send_resolved: true
        title: '{{ if eq .Status "firing" }}[FIRING]{{ else }}[RESOLVED]{{ end }} {{ .CommonLabels.alertname }}'
        text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'

起動と確認

# 起動
docker-compose up -d

# 確認
# Prometheus: http://localhost:9090
# Grafana: http://localhost:3001 (admin / admin)
# アプリケーション: http://localhost:3000
# Node Exporter: http://localhost:9100/metrics
# Alertmanager: http://localhost:9093

Grafanaの初期設定

  1. ブラウザで http://localhost:3001 にアクセス
  2. admin / admin でログイン
  3. データソースの追加: Configuration → Data Sources → Add data source → Prometheus
    • URL: http://prometheus:9090
    • Save & Test
  4. ダッシュボードの作成: Create → Dashboard → Add panel
  5. コミュニティダッシュボードのインポート: Create → Import → 1860(Node Exporter Full)

まとめ

監視の学習ステップ:

段階習得すべき内容
初級ヘルスチェックエンドポイントの実装、CloudWatchの基本
中級Prometheus + Grafanaの構築、アラート設計、ログの構造化
上級SLI/SLO設計、分散トレーシング、インシデント対応プロセスの構築

監視は「設定して終わり」ではなく、継続的に改善していくもの。アラートの閾値は定期的に見直し、ダッシュボードはチームの成長に合わせて進化させる必要がある。

参考リンク