はじめに
マイクロサービスアーキテクチャの普及により、従来のモノリシックな監視手法では対応できない複雑な分散システムの可視化が求められています。この記事では、現代の監視における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. 問題解決の効率化
- Metricsで異常を検知
- Traceで問題の範囲を特定
- Logで詳細な原因を分析
OpenTelemetry(OTel)の有用性
OTelとは
OpenTelemetryは、観測可能性データ(トレース、メトリクス、ログ)を収集、処理、エクスポートするためのオープンソースの統一フレームワークです。
主な利点
1. ベンダー中立性
# 同じコードで複数のバックエンドに対応
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. アプリケーション側の設定
# 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. ダッシュボード設計
階層別ダッシュボード
- エグゼクティブダッシュボード: ビジネスKPI
- オペレーションダッシュボード: システム健全性
- 開発者ダッシュボード: アプリケーション詳細
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つの柱の重要性
- Trace: 分散システムの複雑な依存関係を可視化
- Log: 問題の詳細な原因と文脈を提供
- Metrics: システムの健全性を数値で把握
現代の監視戦略
- OpenTelemetry: ベンダー中立で統一された観測可能性
- Prometheus: インフラとアプリケーションの数値監視
- 統合アプローチ: 3つの柱を組み合わせた包括的な監視
実践のポイント
- 段階的導入: まずMetricsから始めて、徐々にTraceとLogを追加
- アラート最適化: ノイズを減らし、重要な問題に集中
- 継続的改善: 監視データを活用したシステムの改善
参考資料
OpenTelemetry関連
- OpenTelemetry Documentation
- OpenTelemetry Concepts
- OpenTelemetry Signals (Traces, Metrics, Logs)
- OpenTelemetry Collector
- OpenTelemetry Instrumentation
- OpenTelemetry Language Support