1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

モダンな監視の基礎 - マイクロサービス時代のオブザーバビリティ

Last updated at Posted at 2025-10-25

はじめに

マイクロサービスアーキテクチャの普及により、従来のモノリシックな監視手法では対応できない複雑な分散システムの可視化が求められています。この記事では、現代の監視におけるTrace、Log、Metricsの3つの柱と、それらを統合するObservability(o11y)の概念について、実践的な観点から詳しく解説します。

※本記事の内容は、開発環境での検証を推奨します。検証構成や構築手順尾記事を追って書いていきます。

登場する主要ツール・技術の概要

監視・観測可能性ツール

  • OpenTelemetry (OTel): 観測可能性データの収集・処理・エクスポートを行うオープンソースの統一フレームワーク
  • Prometheus: 時系列データベースとモニタリングシステム、Pull型アーキテクチャを採用
  • Grafana: データ可視化とダッシュボード構築のためのオープンソースツール
  • Jaeger: 分散トレーシングシステム、マイクロサービス間の呼び出し関係を可視化
  • Zipkin: 分散トレーシングシステム、Jaegerの代替として利用可能

ログ分析ツール

  • ELK Stack: Elasticsearch(検索エンジン)、Logstash(ログ処理)、Kibana(可視化)の組み合わせ
  • Fluentd: ログ収集・転送ツール、Kubernetes環境でのログ収集に広く使用
  • Fluent Bit: 軽量なログプロセッサー、Fluentdの軽量版

メトリクス・アラートツール

  • Node Exporter: Prometheus用のハードウェア・OSメトリクス収集エージェント
  • Alertmanager: Prometheusのアラート管理システム
  • cAdvisor: コンテナリソース使用量の監視ツール

クラウド・インフラツール

  • Kubernetes: コンテナオーケストレーションシステム
  • Docker: コンテナ化プラットフォーム
  • Helm: Kubernetesパッケージマネージャー

プログラミング言語・フレームワーク

  • Python Flask: 軽量なWebアプリケーションフレームワーク
  • Go: システムプログラミング言語、マイクロサービス開発で人気
  • PromQL: Prometheusのクエリ言語、時系列データの分析に特化

Observabilityとは何か

定義

**Observability(オブザーバビリティ)**とは、システムの内部状態を外部から観測可能な情報のみで理解できる能力のことです。特に、予期しない問題や新しいパターンに対しても、システムの動作を理解し、問題を特定できる能力を指します。

従来の監視との違い

項目 従来の監視 Observability
アプローチ 事前定義されたメトリクスの監視 未知の問題の探索と理解
データ収集 静的、限定的 動的、包括的
問題解決 既知のパターンの検出 未知のパターンの発見
スケーラビリティ 限定的 高い

3つの柱:Trace、Log、Metrics

1. Traces(トレース)

定義

Traceは、単一のリクエストがシステム内を流れる際の完全な実行パスを記録したものです。複数のサービスにまたがる処理の流れを時系列で追跡できます。

特徴

  • 分散トレーシング: マイクロサービス間の呼び出し関係を可視化
  • レイテンシー分析: 各サービスでの処理時間を詳細に把握
  • エラー伝播: 問題の発生源と影響範囲を特定

実装例

参考: OpenTelemetry Tracing Documentation

# OpenTelemetry Collector設定例
apiVersion: v1
kind: ConfigMap
metadata:
  name: otel-collector-config
data:
  otel-collector-config.yaml: |
    receivers:
      jaeger:
        protocols:
          grpc:
            endpoint: 0.0.0.0:14250
    processors:
      batch:
    exporters:
      jaeger:
        endpoint: jaeger:14250
    service:
      pipelines:
        traces:
          receivers: [jaeger]
          processors: [batch]
          exporters: [jaeger]

2. Logs(ログ)

定義

Logは、システム内で発生したイベントの記録です。アプリケーションの実行時情報、エラー、デバッグ情報などを含みます。

特徴

  • イベント記録: 特定の時点で何が起こったかを記録
  • デバッグ支援: 問題の詳細な原因を特定
  • 監査証跡: セキュリティやコンプライアンス要件への対応

構造化ログの例

参考: OpenTelemetry Logging Documentation

{
  "timestamp": "2025-01-13T15:30:45.123Z",
  "level": "ERROR",
  "service": "user-service",
  "trace_id": "abc123def456",
  "span_id": "789xyz012",
  "message": "Failed to create user",
  "error": {
    "type": "ValidationError",
    "code": "INVALID_EMAIL",
    "details": "Email format is invalid"
  },
  "context": {
    "user_id": "user_123",
    "request_id": "req_456"
  }
}

3. Metrics(メトリクス)

定義

Metricsは、システムの状態を数値で表現した時系列データです。パフォーマンス、リソース使用率、ビジネス指標などを測定します。

種類

  • Counter: 累積値(リクエスト数、エラー数)
  • Gauge: 現在値(CPU使用率、メモリ使用量)
  • Histogram: 分布(レスポンス時間の分布)
  • Summary: 分位数(パーセンタイル値)

Prometheusメトリクス例

参考: OpenTelemetry Metrics Documentation

# HTTP リクエスト数
http_requests_total{method="GET", endpoint="/api/users", status="200"} 1500

# レスポンス時間のヒストグラム
http_request_duration_seconds_bucket{le="0.1"} 1000
http_request_duration_seconds_bucket{le="0.5"} 1200
http_request_duration_seconds_bucket{le="1.0"} 1300
http_request_duration_seconds_bucket{le="+Inf"} 1500

# CPU使用率
cpu_usage_percent{instance="web-01"} 45.2

なぜこの3つが重視されるのか

1. 相互補完的な関係

2. マイクロサービス特有の課題への対応

課題 Trace Log Metrics
サービス間の依存関係 ✅ 可視化 ❌ 限定的 ❌ 不明
パフォーマンスボトルネック ✅ 特定可能 ⚠️ 部分的 ✅ 数値化
エラーの伝播経路 ✅ 追跡可能 ✅ 詳細記録 ❌ 不明
システム全体の健全性 ❌ 限定的 ❌ 限定的 ✅ 包括的

3. 問題解決の効率化

  1. Metricsで異常を検知
  2. Traceで問題の範囲を特定
  3. Logで詳細な原因を分析

OpenTelemetry(OTel)の有用性

OTelとは

OpenTelemetryは、観測可能性データ(トレース、メトリクス、ログ)を収集、処理、エクスポートするためのオープンソースの統一フレームワークです。

主な利点

1. ベンダー中立性

参考: OpenTelemetry Overview

# 同じコードで複数のバックエンドに対応
exporters:
  jaeger:
    endpoint: jaeger:14250
  zipkin:
    endpoint: zipkin:9411
  datadog:
    api_key: ${DD_API_KEY}

2. 言語横断的な統一API

参考: OpenTelemetry Language Support

# Python
from opentelemetry import trace
tracer = trace.get_tracer(__name__)

# Go
import "go.opentelemetry.io/otel/trace"
tracer := trace.NewTracerProvider()

3. 自動インストルメンテーション

参考: OpenTelemetry Auto-instrumentation

# Kubernetes環境での自動設定
apiVersion: v1
kind: ConfigMap
metadata:
  name: otel-instrumentation
data:
  OTEL_SERVICE_NAME: "my-service"
  OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector:4317"
  OTEL_RESOURCE_ATTRIBUTES: "service.name=my-service,service.version=1.0.0"

実装例

1. アプリケーション側の設定

参考: OpenTelemetry Python SDK

# Python Flask アプリケーション
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# トレーサープロバイダーの設定
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)

# OTLPエクスポーターの設定
otlp_exporter = OTLPSpanExporter(endpoint="http://otel-collector:4317")
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

# トレースの作成
with tracer.start_as_current_span("user_creation"):
    # ビジネスロジック
    create_user(user_data)

2. Collector設定

参考: OpenTelemetry Collector Documentation

# OpenTelemetry Collector設定
apiVersion: v1
kind: ConfigMap
metadata:
  name: otel-collector-config
data:
  otel-collector-config.yaml: |
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
    
    processors:
      batch:
        timeout: 1s
        send_batch_size: 1024
      resource:
        attributes:
          - key: environment
            value: production
            action: upsert
    
    exporters:
      jaeger:
        endpoint: jaeger:14250
        tls:
          insecure: true
      prometheus:
        endpoint: "0.0.0.0:8889"
    
    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [batch, resource]
          exporters: [jaeger]
        metrics:
          receivers: [otlp]
          processors: [batch, resource]
          exporters: [prometheus]

Prometheusが適しているケース

Prometheusの特徴

1. Pull型アーキテクチャ

# Prometheus設定例
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
    - role: pod
    relabel_configs:
    - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
      action: keep
      regex: true

2. 強力なクエリ言語(PromQL)

# 5分間の平均CPU使用率
rate(container_cpu_usage_seconds_total[5m]) * 100

# エラー率の計算
rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])

# アラート条件
up{job="api-server"} == 0

Prometheusが適している場面

1. インフラストラクチャ監視

# Node Exporter メトリクス
node_cpu_seconds_total{cpu="0",mode="idle"} 1234567
node_memory_MemTotal_bytes 8589934592
node_filesystem_size_bytes{device="/dev/sda1",fstype="ext4",mountpoint="/"} 107374182400

2. サービスメトリクス

# アプリケーションメトリクス
http_requests_total{method="GET",endpoint="/api/users",status="200"} 1500
http_request_duration_seconds{method="GET",endpoint="/api/users",quantile="0.95"} 0.2

3. ビジネスメトリクス

# ビジネス指標
orders_total{status="completed"} 1000
revenue_total{currency="USD"} 50000
active_users_gauge 150

Prometheusが適さない場面

1. 高頻度のイベント

  • ログデータの詳細分析
  • リアルタイムのイベントストリーミング

2. 長期保存

  • 1年以上のデータ保持
  • コスト効率を重視する場合

マイクロサービス環境でのOpenTelemetry導入

サンプル構成図

導入に必要な対応事項リスト

1. 基盤構築

Kubernetes環境でのOpenTelemetry Collector導入

# OpenTelemetry Operator のインストール
apiVersion: v1
kind: Namespace
metadata:
  name: observability
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: otel-collector
  namespace: observability
spec:
  replicas: 2
  selector:
    matchLabels:
      app: otel-collector
  template:
    metadata:
      labels:
        app: otel-collector
    spec:
      containers:
      - name: otel-collector
        image: otel/opentelemetry-collector-contrib:latest
        ports:
        - containerPort: 4317
        - containerPort: 4318
        - containerPort: 8889
        env:
        - name: OTEL_RESOURCE_ATTRIBUTES
          value: "service.name=otel-collector,service.version=1.0.0"
        volumeMounts:
        - name: otel-collector-config
          mountPath: /etc/otel-collector-config.yaml
          subPath: otel-collector-config.yaml
      volumes:
      - name: otel-collector-config
        configMap:
          name: otel-collector-config

可視化ツールの基盤構築

# Jaeger のデプロイメント
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jaeger
  namespace: observability
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jaeger
  template:
    metadata:
      labels:
        app: jaeger
    spec:
      containers:
      - name: jaeger
        image: jaegertracing/all-in-one:latest
        ports:
        - containerPort: 16686
        - containerPort: 14250
        env:
        - name: COLLECTOR_OTLP_ENABLED
          value: "true"

2. アプリケーション側の設定

Python Flask アプリケーション例

# requirements.txt
opentelemetry-api==1.20.0
opentelemetry-sdk==1.20.0
opentelemetry-exporter-otlp==1.20.0
opentelemetry-instrumentation-flask==0.42b0
opentelemetry-instrumentation-requests==0.42b0

# app.py
from flask import Flask
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor

app = Flask(__name__)

# OpenTelemetry の設定
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)

# OTLP エクスポーターの設定
otlp_exporter = OTLPSpanExporter(
    endpoint="http://otel-collector.observability.svc.cluster.local:4317",
    insecure=True
)
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

# 自動インストルメンテーション
FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()

@app.route('/api/users')
def get_users():
    with tracer.start_as_current_span("get_users"):
        # ビジネスロジック
        return {"users": []}

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Node.js Express アプリケーション例

// package.json
{
  "dependencies": {
    "@opentelemetry/api": "^1.7.0",
    "@opentelemetry/sdk-node": "^0.45.1",
    "@opentelemetry/exporter-otlp-grpc": "^0.45.1",
    "@opentelemetry/instrumentation-express": "^0.33.0",
    "@opentelemetry/instrumentation-http": "^0.33.0"
  }
}

// app.js
const express = require('express');
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-otlp-grpc');

// OpenTelemetry の設定
const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'http://otel-collector.observability.svc.cluster.local:4317',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

const app = express();

app.get('/api/orders', (req, res) => {
  res.json({ orders: [] });
});

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

3. 監視ダッシュボードの構築

Grafana ダッシュボード設定

{
  "dashboard": {
    "title": "Microservices Observability",
    "panels": [
      {
        "title": "Service Overview",
        "type": "stat",
        "targets": [
          {
            "expr": "sum(up{job=~\"api-.*\"})",
            "legendFormat": "Active Services"
          }
        ]
      },
      {
        "title": "Request Rate by Service",
        "type": "graph",
        "targets": [
          {
            "expr": "sum(rate(http_requests_total[5m])) by (service_name)"
          }
        ]
      },
      {
        "title": "Error Rate",
        "type": "graph",
        "targets": [
          {
            "expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m])) by (service_name) / sum(rate(http_requests_total[5m])) by (service_name)"
          }
        ]
      }
    ]
  }
}

既存SaaS監視サービスとの比較・併用戦略

OpenTelemetry vs 既存SaaS監視サービス

項目 OpenTelemetry Datadog New Relic JP1 Senju
コスト オープンソース 従量課金 従量課金 ライセンス ライセンス
ベンダーロックイン なし あり あり あり あり
カスタマイズ性 高い 中程度 中程度 低い 低い
運用負荷 高い 低い 低い 中程度 中程度
学習コスト 高い 低い 低い 中程度 中程度

推奨併用パターン

1. OpenTelemetry + Datadog 併用

# OpenTelemetry Collector設定(Datadog連携)
exporters:
  datadog:
    api:
      key: ${DD_API_KEY}
      site: datadoghq.com
  jaeger:
    endpoint: jaeger:14250
  prometheus:
    endpoint: "0.0.0.0:8889"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [datadog, jaeger]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [datadog, prometheus]

参考: Datadog OpenTelemetry Integration

2. OpenTelemetry + New Relic 併用

# New Relic 連携設定
exporters:
  otlp/newrelic:
    endpoint: https://otlp.nr-data.net:4317
    headers:
      api-key: ${NEW_RELIC_LICENSE_KEY}
  jaeger:
    endpoint: jaeger:14250

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/newrelic, jaeger]

参考: New Relic OpenTelemetry Integration

既存監視サービスのみで良いケース

1. 小規模チーム・予算重視

  • 運用リソースが限られている
  • 監視の専門知識が不足
  • 迅速な導入が必要

2. 既存システムとの統合重視

  • 既存の監視基盤が充実
  • 運用プロセスが確立済み
  • 変更リスクを最小化したい

3. 特定機能に特化

  • APM(Application Performance Monitoring)のみ必要
  • インフラ監視のみ必要
  • ログ分析のみ必要

推奨選択基準

実践的な監視戦略

1. 監視の階層化

2. アラート戦略

アラートの分類

# Critical: サービス停止
- alert: ServiceDown
  expr: up{job="api-server"} == 0
  for: 0m
  labels:
    severity: critical
  annotations:
    summary: "API server is down"

# Warning: パフォーマンス劣化
- alert: HighLatency
  expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: "High latency detected"

# Info: 容量計画
- alert: HighMemoryUsage
  expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.8
  for: 10m
  labels:
    severity: info
  annotations:
    summary: "High memory usage detected"

3. ダッシュボード設計

階層別ダッシュボード

  1. エグゼクティブダッシュボード: ビジネスKPI
  2. オペレーションダッシュボード: システム健全性
  3. 開発者ダッシュボード: アプリケーション詳細

Grafanaダッシュボード例

{
  "dashboard": {
    "title": "Microservices Overview",
    "panels": [
      {
        "title": "Request Rate",
        "type": "graph",
        "targets": [
          {
            "expr": "sum(rate(http_requests_total[5m])) by (service)"
          }
        ]
      },
      {
        "title": "Error Rate",
        "type": "graph",
        "targets": [
          {
            "expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m])) by (service) / sum(rate(http_requests_total[5m])) by (service)"
          }
        ]
      }
    ]
  }
}

4. ログ分析戦略

ELK Stack設定例

# Elasticsearch設定
apiVersion: v1
kind: ConfigMap
metadata:
  name: elasticsearch-config
data:
  elasticsearch.yml: |
    cluster.name: "microservices-logs"
    node.name: "elasticsearch-0"
    network.host: 0.0.0.0
    discovery.type: single-node

ログパース設定

# Logstash設定
input {
  beats {
    port => 5044
  }
}

filter {
  if [fields][service] == "user-service" {
    grok {
      match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}" }
    }
  }
}

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

まとめ

監視の3つの柱の重要性

  1. Trace: 分散システムの複雑な依存関係を可視化
  2. Log: 問題の詳細な原因と文脈を提供
  3. Metrics: システムの健全性を数値で把握

現代の監視戦略

  • OpenTelemetry: ベンダー中立で統一された観測可能性
  • Prometheus: インフラとアプリケーションの数値監視
  • 統合アプローチ: 3つの柱を組み合わせた包括的な監視

実践のポイント

  1. 段階的導入: まずMetricsから始めて、徐々にTraceとLogを追加
  2. アラート最適化: ノイズを減らし、重要な問題に集中
  3. 継続的改善: 監視データを活用したシステムの改善

参考資料

OpenTelemetry関連

監視・可視化ツール

ログ分析

書籍・学習リソース

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?