observability-sre について
Observability & Site Reliability Engineering は、サービスの設計・運用に携わるエンジニア向けの可観測性と信頼性のリファレンススキルです。メトリクス、ログ、トレース、SLO、アラート、インシデント対応の指針と実装例をまとめています。
主な機能
- 01メトリクス、ログ、トレースの指針
- 02ユーザー症状に基づくアラート原則
- 03SLOとエラーバジェットの実践
- 04トレースコンテキストと構造化ログ
- 05PrometheusとOpenTelemetryの例
活用シーン
- 01サービスのSLOとエラーバジェットの定義
- 02症状に基づく本番アラートの設計
- 03ログと分散トレースの関連付け
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ majiayu000/ spellbook --skill observability-sreインストール後、Claude Code で/observability-sreを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
reference/extended.mdincident-response.mdlogging.mdmonitoring.mdtracing.mdtemplates/slo-template.md詳細
可観測性とサイト信頼性エンジニアリング
基本原則
- 3つの柱 — メトリクス、ログ、トレースによって全体を把握できます
- 可観測性を最優先 — システムが自身の動作を説明できるように構築します
- SLO 主導 — ユーザーにとって重要な信頼性目標を定義します
- 事前検知 — 顧客が気付く前に問題を発見します
- 非難しない文化 — 誰かを責めずに障害から学びます
- 運用負荷を自動化 — 繰り返し発生する運用作業を減らします
- 継続的な改善 — インシデントのたびにシステムの耐障害性を高めます
- フルスタックの可視性 — インフラストラクチャからビジネスメトリクスまで監視します
厳守事項
これらのルールは必須です。違反すると、このスキルが正しく機能しません。
症状に基づくアラートのみ
内部インフラストラクチャのメトリクスではなく、ユーザーに影響する症状に対してアラートを設定します。
# ❌ FORBIDDEN: Alerting on internal metrics
- alert: CPUHigh
expr: cpu_usage > 70%
# Users don't care about CPU, they care about latency
- alert: MemoryHigh
expr: memory_usage > 80%
# Internal metric, may not affect users
# ✅ REQUIRED: Alert on user experience
- alert: APILatencyHigh
expr: slo:api_latency:p95 > 0.200
annotations:
summary: "Users experiencing slow response times"
- alert: ErrorRateHigh
expr: slo:api_errors:rate5m > 0.001
annotations:
summary: "Users encountering errors"
低カーディナリティのラベル
Loki/Prometheus のラベルは低カーディナリティ(固有のラベルは 10 個未満)にする必要があります。
# ❌ FORBIDDEN: High cardinality labels
labels:
user_id: "usr_123" # Millions of values!
order_id: "ord_456" # Millions of values!
request_id: "req_789" # Every request is unique!
# ✅ REQUIRED: Low cardinality only
labels:
namespace: "production" # Few values
app: "api-server" # Few values
level: "error" # 5-6 values
method: "GET" # ~10 values
# High cardinality data goes in log body:
logger.info({
user_id: "usr_123", # In JSON body, not label
order_id: "ord_456",
}, "Order processed");
SLO に基づくエラーバジェット
すべてのサービスで SLO を定義し、エラーバジェットを追跡する必要があります。
# ❌ FORBIDDEN: No SLO definition
# Just monitoring without targets
# ✅ REQUIRED: Explicit SLO with budget
# SLO: 99.9% availability
# Error Budget: 0.1% = 43.2 minutes/month downtime
groups:
- name: slo_tracking
rules:
- record: slo:api_availability:ratio
expr: sum(rate(http_requests_total{status!~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
- alert: ErrorBudgetBurnRate
expr: slo:api_availability:ratio < 0.999
for: 5m
annotations:
summary: "Burning error budget too fast"
ログ内のトレースコンテキスト
分散トレースとの関連付けのため、すべてのログに trace_id を含める必要があります。
// ❌ FORBIDDEN: Logs without trace context
logger.info("Payment processed");
// ✅ REQUIRED: Include trace_id in every log
const span = trace.getActiveSpan();
logger.info({
trace_id: span?.spanContext().traceId,
span_id: span?.spanContext().spanId,
order_id: "ord_123",
}, "Payment processed");
// Output includes correlation:
// {"trace_id":"abc123","span_id":"def456","order_id":"ord_123","msg":"Payment processed"}
クイックリファレンス
用途に応じた選択
| シナリオ | ツール/パターン | 理由 |
|---|---|---|
| メトリクス収集 | Prometheus + Grafana | 業界標準で、強力なクエリ言語を備えています |
| 分散トレーシング | OpenTelemetry + Tempo/Jaeger | ベンダーに依存せず、CNCF 標準に準拠しています |
| ログ集約(コスト重視) | Grafana Loki | ラベルのみをインデックス化し、コストを 10 分の 1 に抑えます |
| ログ集約(検索重視) | ELK Stack | 全文検索と高度な分析が可能です |
| 統合オブザーバビリティ | Elastic/Datadog/Dynatrace | すべてのテレメトリを一元的に把握できます |
| インシデント管理 | PagerDuty/Opsgenie | アラートのルーティングとオンコールのスケジュール管理に対応します |
| カオスエンジニアリング | Gremlin/Chaos Mesh | 制御された障害注入が可能です |
| AIOps/異常検知 | Dynatrace/Datadog | AI 主導の根本原因分析に対応します |
3つの柱
| 柱 | 内容 | 用途 | ツール |
|---|---|---|---|
| メトリクス | 数値の時系列データ | リアルタイム監視、アラート | Prometheus、StatsD、CloudWatch |
| ログ | コンテキストを含むイベント記録 | デバッグ、監査証跡 | Loki、ELK、Splunk |
| トレース | サービス間のリクエスト経路 | パフォーマンス分析、依存関係の把握 | OpenTelemetry、Jaeger、Zipkin |
第 4 の柱(新興): 継続的プロファイリング — コードレベルのパフォーマンスデータ(関数単位の CPU 使用率、メモリ使用量)
オブザーバビリティアーキテクチャ
階層化された Prometheus のセットアップ
# 2025 Best Practice: Federated architecture
# Prevents metric chaos while enabling drill-down
# Layer 1: Application Prometheus
# - Detailed business logic metrics
# - High cardinality acceptable
# - Short retention (7 days)
# Layer 2: Cluster Prometheus
# - Per-environment/cluster metrics
# - Medium retention (30 days)
# - Aggregates from application level
# Layer 3: Global Prometheus
# - Cross-cluster critical metrics
# - Long retention (1 year)
# - Federation from cluster level
# Global Prometheus config
scrape_configs:
- job_name: 'federate'
scrape_interval: 15s
honor_labels: true
metrics_path: '/federate'
params:
'match[]':
- '{job="kubernetes-nodes"}'
- '{__name__=~"job:.*"}' # Recording rules only
static_configs:
- targets:
- 'cluster-prom-us-east.internal:9090'
- 'cluster-prom-eu-west.internal:9090'
パフォーマンス向上のための記録ルール
# Precompute expensive queries
groups:
- name: api_performance
interval: 30s
rules:
# Request rate (requests per second)
- record: job:api_requests:rate5m
expr: sum(rate(http_requests_total[5m])) by (job, method, status)
# Error rate
- record: job:api_errors:rate5m
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) by (job)
/
sum(rate(http_requests_total[5m])) by (job)
# P95 latency
- record: job:api_latency:p95
expr: histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (job, le))
リソースの最適化
# Increase scrape interval for high-target deployments
scrape_interval: 30s # Default: 15s reduces load by 50%
# Use relabeling to drop unnecessary metrics
metric_relabel_configs:
- source_labels: [__name__]
regex: 'go_.*|process_.*' # Drop Go runtime metrics
action: drop
# Limit sample retention
storage:
tsdb:
retention.time: 15d # Keep only 15 days locally
retention.size: 50GB # Or max 50GB
OpenTelemetry による分散トレーシング
自動計装のセットアップ
// Node.js auto-instrumentation
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: 'http://otel-collector:4318/v1/traces',
}),
instrumentations: [
getNodeAutoInstrumentations({
// Auto-instruments HTTP, Express, PostgreSQL, Redis, etc.
'@opentelemetry/instrumentation-fs': { enabled: false }, // Too noisy
}),
],
});
sdk.start();
ビジネスロジックへの手動計装
import { trace, SpanStatusCode } from '@opentelemetry/api';
const tracer = trace.getTracer('payment-service', '1.0.0');
async function processPayment(orderId: string, amount: number) {
// Create custom span for business operation
return tracer.startActiveSpan('processPayment', async (span) => {
try {
// Add business context
span.setAttributes({
'order.id': orderId,
'payment.amount': amount,
'payment.currency': 'USD',
});
// Child span for external API call
const paymentResult = await tracer.startActiveSpan('stripe.charge', async (childSpan) => {
const result = await stripe.charges.create({ amount, currency: 'usd' });
childSpan.setAttribute('stripe.charge_id', result.id);
childSpan.setStatus({ code: SpanStatusCode.OK });
childSpan.end();
return result;
});
span.setStatus({ code: SpanStatusCode.OK });
return paymentResult;
} catch (error) {
span.recordException(error);
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
throw error;
} finally {
span.end();
}
});
}
サンプリング戦略
# OpenTelemetry Collector config
processors:
# Probabilistic sampling: Keep 10% of traces
probabilistic_sampler:
sampling_percentage: 10
# Tail sampling: Make decisions after seeing full trace
tail_sampling:
policies:
# Always sample errors
- name: error-traces
type: status_code
status_code: {status_codes: [ERROR]}
# Always sample slow requests
- name: slow-traces
type: latency
latency: {threshold_ms: 1000}
# Sample 5% of normal traffic
- name: normal-traces
type: probabilistic
probabilistic: {sampling_percentage: 5}
コンテキスト伝播
// Ensure trace context flows across services
import { propagation, context } from '@opentelemetry/api';
// Outgoing HTTP request (automatic with auto-instrumentation)
fetch('https://api.example.com/data', {
headers: {
// W3C Trace Context headers injected automatically:
// traceparent: 00-<trace-id>-<span-id>-01
// tracestate: vendor=value
},
});
// Manual propagation for non-HTTP (e.g., message queues)
const carrier = {};
propagation.inject(context.active(), carrier);
await publishMessage(queue, { data: payload, headers: carrier });
構造化ロギングのベストプラクティス
JSON ロギング形式
// Use structured logging library
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => ({ level: label }),
},
timestamp: pino.stdTimeFunctions.isoTime,
// Include trace context in logs
mixin() {
const span = trace.getActiveSpan();
if (!span) return {};
const { traceId, spanId } = span.spanContext();
return {
trace_id: traceId,
span_id: spanId,
};
},
});
// Structured logging with context
logger.info(
{
user_id: '123',
order_id: 'ord_456',
amount: 99.99,
payment_method: 'card',
},
'Payment processed successfully'
);
// Output:
// {"level":"info","time":"2025-01-15T10:30:00.000Z","trace_id":"abc123","span_id":"def456","user_id":"123","order_id":"ord_456","amount":99.99,"payment_method":"card","msg":"Payment processed successfully"}
ログレベル
// Follow standard severity levels
logger.trace({ details }, 'Low-level debugging'); // Very verbose
logger.debug({ state }, 'Debug information'); // Development
logger.info({ event }, 'Normal operation'); // Production default
logger.warn({ issue }, 'Warning condition'); // Potential issues
logger.error({ error, context }, 'Error occurred'); // Errors
logger.fatal({ critical }, 'Fatal error'); // Process crash
Grafana Loki の設定
# Promtail config - ships logs to Loki
server:
http_listen_port: 9080
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: kubernetes
kubernetes_sd_configs:
- role: pod
relabel_configs:
# Add pod labels as Loki labels (LOW cardinality only!)
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
- source_labels: [__meta_kubernetes_pod_name]
target_label: pod
- source_labels: [__meta_kubernetes_pod_label_app]
target_label: app
pipeline_stages:
# Parse JSON logs
- json:
expressions:
level: level
trace_id: trace_id
# Extract fields as labels
- labels:
level:
trace_id:
Loki のベストプラクティス
- 低カーディナリティのラベル — 使用するラベルは 5~10 個(namespace、app、level)のみにします
- ログ本文には高カーディナリティの値を含める — user_id や order_id はラベルではなく JSON に含めます
- フィルタリングには LogQL を使用 —
{app="api"} | json | user_id="123"を使用します - 保持ポリシー — 最近のログは長く保持し、古いログは圧縮します
# LogQL query examples
{namespace="production", app="api"} |= "error" # Text search
{app="api"} | json | level="error" | line_format "{{.msg}}" # JSON parsing
rate({app="api"}[5m]) # Log rate per second
sum by (level) (count_over_time({namespace="production"}[1h])) # Count by level
詳細リファレンス
このスキルを簡潔に保つため、## SLO/SLI/SLA Management 以降の詳細な資料は reference/extended.md に移動しました。移動された使用例、コマンド一覧、チェックリスト、プラットフォームの詳細、実装テンプレートが必要なタスクでは、このリファレンスを読み込んでください。
コメント
Markdown 対応