ストーリー
高橋アーキテクトがマイクロサービスの構成図を指した。
分散トレーシングとは
分散トレーシングは、1つのリクエストがシステム内の複数サービスを横断する経路を記録・可視化する技術です。
ユーザーのリクエスト: POST /api/orders
[API Gateway] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 350ms
│
├─[Auth Service] ━━━━━━━ 25ms
│
└─[Order Service] ━━━━━━━━━━━━━━━━━━━━━━━━━━━ 310ms
│
├─[DB: SELECT order] ━━━ 5ms
│
├─[Inventory Service] ━━━━━━━━━ 40ms
│ └─[DB: UPDATE stock] ━━ 8ms
│
├─[Payment Service] ━━━━━━━━━━━━━━━━━━━ 230ms ← ボトルネック!
│ └─[External: Stripe API] ━━━━━━━━━━━━━━ 220ms
│
└─[Notification Service] ━━━━ 15ms
└─[Email API] ━━ 10ms
基本概念
Trace(トレース)
1つのリクエストの全体的な経路です。一意のTraceIDで識別されます。
Span(スパン)
Trace内の1つの処理区間です。サービス間の呼び出しや、サービス内の重要な処理ごとにSpanが作成されます。
Context Propagation(コンテキスト伝搬)
TraceIDやSpanIDを、サービス間の呼び出し時にHTTPヘッダー等で伝搬する仕組みです。
// Spanの構造
interface Span {
traceId: string; // リクエスト全体のID
spanId: string; // このSpanのID
parentSpanId?: string; // 親SpanのID(ルートSpanはなし)
operationName: string; // 操作名 (e.g., "POST /api/orders")
serviceName: string; // サービス名
startTime: number; // 開始時刻(マイクロ秒)
duration: number; // 所要時間(マイクロ秒)
status: {
code: 'OK' | 'ERROR' | 'UNSET';
message?: string;
};
attributes: Record<string, string | number | boolean>;
events: SpanEvent[]; // Span内のイベント(ログ的な情報)
}
interface SpanEvent {
name: string;
timestamp: number;
attributes: Record<string, string | number | boolean>;
}
Context Propagationの仕組み
// HTTPヘッダーによるコンテキスト伝搬
// W3C Trace Context 標準
// リクエスト送信側(Service A → Service B)
const headers = {
'traceparent': '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
// version-traceId-parentSpanId-flags
};
// リクエスト受信側(Service B)
function extractContext(req: IncomingMessage): TraceContext {
const traceparent = req.headers['traceparent'] as string;
const [version, traceId, parentSpanId, flags] = traceparent.split('-');
return {
traceId, // 同じTraceIDを引き継ぐ
parentSpanId, // 送信側のSpanIDが親になる
sampled: flags === '01',
};
}
OpenTelemetryでの実装
import { trace, context, propagation } from '@opentelemetry/api';
const tracer = trace.getTracer('order-service');
// サービス内でSpanを作成
async function createOrder(orderData: OrderInput): Promise<Order> {
return tracer.startActiveSpan('createOrder', async (span) => {
span.setAttribute('order.items_count', orderData.items.length);
// DBアクセス(子Span)
const order = await tracer.startActiveSpan('db.insertOrder', async (dbSpan) => {
const result = await db.orders.create(orderData);
dbSpan.setAttribute('db.statement', 'INSERT INTO orders');
dbSpan.end();
return result;
});
// 他サービス呼び出し(子Span + コンテキスト伝搬)
await tracer.startActiveSpan('call.paymentService', async (callSpan) => {
const headers: Record<string, string> = {};
propagation.inject(context.active(), headers);
await fetch('http://payment-service/charge', {
method: 'POST',
headers: {
...headers, // traceparentヘッダーを付与
'Content-Type': 'application/json',
},
body: JSON.stringify({ orderId: order.id, amount: order.total }),
});
callSpan.end();
});
span.end();
return order;
});
}
サンプリング戦略
全リクエストのトレースを保存するとコストが膨大になるため、サンプリングが重要です。
// サンプリング戦略
interface SamplingStrategy {
// 確率的サンプリング: 一定割合をランダムに記録
probabilistic: {
rate: 0.1; // 10%のリクエストを記録
};
// Rate Limiting: 1秒あたりの上限を設定
rateLimiting: {
maxTracesPerSecond: 100;
};
// Tail-based: レスポンス後にサンプリング判定
tailBased: {
rules: [
{ condition: 'error', sampleRate: 1.0 }, // エラーは100%記録
{ condition: 'duration > 2s', sampleRate: 1.0 }, // 遅いリクエストは100%
{ condition: 'default', sampleRate: 0.05 }, // それ以外は5%
];
};
}
| 戦略 | 利点 | 欠点 |
|---|---|---|
| 確率的 | 実装が簡単 | 重要なトレースを見逃す可能性 |
| Rate Limiting | コスト予測が容易 | 高負荷時にサンプル率が下がる |
| Tail-based | エラーや遅延を確実に記録 | 実装が複雑、一時バッファが必要 |
Span属性のベストプラクティス
// セマンティック属性(OpenTelemetry Semantic Conventions)
const semanticAttributes = {
// HTTP
'http.method': 'POST',
'http.url': '/api/orders',
'http.status_code': 201,
'http.request_content_length': 1234,
// データベース
'db.system': 'postgresql',
'db.statement': 'SELECT * FROM orders WHERE id = $1',
'db.operation': 'SELECT',
// メッセージング
'messaging.system': 'kafka',
'messaging.destination': 'order-events',
'messaging.operation': 'publish',
// カスタム(ビジネス固有)
'order.id': 'ORD-001',
'order.total': 5000,
'payment.provider': 'stripe',
};
まとめ
| ポイント | 内容 |
|---|---|
| Trace | 1リクエストの全体経路(TraceIDで識別) |
| Span | Trace内の1処理区間(SpanIDで識別) |
| Context Propagation | TraceIDをサービス間でHTTPヘッダーで伝搬 |
| サンプリング | 全件記録はコスト大、Tail-basedが推奨 |
チェックリスト
- Trace、Span、Context Propagationの概念を説明できる
- W3C Trace Contextの仕組みを理解した
- OpenTelemetryでのSpan作成とコンテキスト伝搬を実装できる
- サンプリング戦略の種類と使い分けを把握した
次のステップへ
次は「Jaeger/Zipkinの活用」を学びます。収集したトレースを可視化・分析するツールの使い方を見ていきましょう。
推定読了時間: 40分