LESSON 30分

ストーリー

高橋アーキテクト
サーバーが10台あって、それぞれにSSHしてログを見るなんてやってられない

高橋アーキテクトが同意した。

高橋アーキテクト
その通り。分散システムのログは1箇所に集約して、横断的に検索できるようにするのが鉄則だ。ELK Stack、CloudWatch Logs、Grafana Lokiなど、いくつかの選択肢を理解しておこう

ログ集約のアーキテクチャ

┌──────────┐  ┌──────────┐  ┌──────────┐
│ Service A │  │ Service B │  │ Service C │
│  (logs)   │  │  (logs)   │  │  (logs)   │
└─────┬─────┘  └─────┬─────┘  └─────┬─────┘
      │              │              │
      ▼              ▼              ▼
┌─────────────────────────────────────────┐
│         Log Shipper / Agent             │
│   (Fluentd, Fluent Bit, Filebeat)       │
└────────────────────┬────────────────────┘


┌─────────────────────────────────────────┐
│         Log Storage & Index             │
│  (Elasticsearch, CloudWatch, Loki)      │
└────────────────────┬────────────────────┘


┌─────────────────────────────────────────┐
│         Search & Visualization          │
│     (Kibana, CloudWatch Insights,       │
│      Grafana)                           │
└─────────────────────────────────────────┘

ELK Stack(Elasticsearch + Logstash + Kibana)

アーキテクチャ

// ELK Stackの構成
interface ELKStack {
  elasticsearch: {
    role: 'ログの保存とインデックス作成';
    features: ['全文検索', '集計', 'フィルタリング'];
  };
  logstash: {
    role: 'ログの収集・変換・送信';
    features: ['パイプライン処理', 'フィルタ', 'エンリッチメント'];
  };
  kibana: {
    role: 'ログの可視化と検索UI';
    features: ['ダッシュボード', 'クエリ', 'アラート'];
  };
}

Logstashパイプラインの例

# logstash.conf
input {
  beats {
    port => 5044
  }
}

filter {
  json {
    source => "message"
  }

  # traceIdが存在する場合にインデックスを分ける
  if [level] == "ERROR" {
    mutate {
      add_tag => ["error"]
    }
  }

  # タイムスタンプのパース
  date {
    match => ["timestamp", "ISO8601"]
    target => "@timestamp"
  }
}

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

CloudWatch Logs(AWS)

// CloudWatch Logs Insightsのクエリ例
const queries = {
  // 過去1時間のエラーログ
  recentErrors: `
    fields @timestamp, @message, orderId, errorCode
    | filter level = "ERROR"
    | sort @timestamp desc
    | limit 100
  `,

  // サービス別エラー数の集計
  errorsByService: `
    fields service
    | filter level = "ERROR"
    | stats count(*) as errorCount by service
    | sort errorCount desc
  `,

  // 特定traceIdの全ログを時系列で取得
  traceSearch: `
    fields @timestamp, service, level, message
    | filter traceId = "abc123def456"
    | sort @timestamp asc
  `,

  // レスポンスタイムのパーセンタイル分析
  latencyAnalysis: `
    fields duration
    | filter path = "/api/orders"
    | stats avg(duration) as avg_ms,
            pct(duration, 50) as p50,
            pct(duration, 95) as p95,
            pct(duration, 99) as p99
  `,
};

Grafana Loki

// Loki: Prometheusライクなラベルベースのログ管理
interface LokiConfig {
  advantages: [
    'Prometheusと同じラベルモデルで親和性が高い',
    'ログの中身をインデックスしないため、ストレージコストが低い',
    'Grafanaとネイティブに統合',
  ];
  tradeoff: 'フルテキスト検索はElasticsearchほど高速ではない';
}

// LogQLクエリの例
const logqlQueries = {
  // サービスのエラーログ
  serviceErrors: '{service="payment-service"} |= "ERROR"',

  // JSON解析してフィルタ
  parsedFilter: '{service="api-gateway"} | json | status >= 500',

  // 5分間のエラー率
  errorRate: 'sum(rate({service="api-gateway"} | json | level="ERROR" [5m]))',
};

ツール比較

項目ELK StackCloudWatch LogsGrafana Loki
デプロイセルフホスト/マネージドフルマネージド(AWS)セルフホスト/クラウド
検索方式フルテキスト検索クエリ言語ラベル + フィルタ
コストストレージ + 運用取り込み量 + クエリ量ストレージのみ(安い)
スケーラビリティ高い(運用コスト大)自動スケール高い(S3バックエンド)
適したケース大規模検索・分析AWSネイティブ環境Prometheus環境

まとめ

ポイント内容
集約の原則分散したログを1箇所に集めて横断検索
ELK Stackフルテキスト検索に強い、大規模分析向き
CloudWatchAWS環境でのフルマネージドソリューション
Grafana Loki低コストでPrometheus環境と親和性が高い

チェックリスト

  • ログ集約のアーキテクチャパターンを説明できる
  • ELK Stackの3つのコンポーネントの役割を理解した
  • CloudWatch Logs Insightsのクエリを書ける
  • 3つのツールの特徴と使い分けを把握した

次のステップへ

次は「ログによるデバッグとトラブルシューティング」を学びます。集約したログを使って、実際に障害を調査する方法を見ていきましょう。


推定読了時間: 30分