分散トレース
リクエストの流れを可視化
分散トレースは、マイクロサービス環境でリクエストがどのサービスを通過し、 どこで時間がかかっているかを可視化する技術。 パフォーマンス問題やエラーの根本原因特定に不可欠です。
基本概念
Trace(トレース)
1つのリクエストの全体像。 複数のSpanで構成される。 一意のtrace_idで識別。
Span(スパン)
1つの処理単位(操作)。 開始時刻、終了時刻、属性、 親Spanへの参照を持つ。
Context(コンテキスト)
trace_id、span_id、フラグ。 サービス間でHTTPヘッダー等で 伝播(Propagation)される。
トレースの構造
上記の例では、DBクエリ(70ms)がボトルネックであることが分かる。 トレースを見ることで、どこを最適化すべきか特定できる。
Spanの属性
| 属性 | 説明 | 例 |
|---|---|---|
| trace_id | トレース全体のID | abc123def456... |
| span_id | このSpanのID | span789 |
| parent_span_id | 親SpanのID | span456 |
| operation_name | 操作名 | GET /api/orders |
| start_time | 開始時刻 | 2024-01-15T10:30:00Z |
| duration | 処理時間 | 150ms |
| status | 成功/エラー | OK, ERROR |
| attributes | カスタム属性 | http.method, db.statement |
コンテキスト伝播(Context Propagation)
サービス間でtrace_idを伝播することで、分散システム全体で一つのトレースとして追跡可能にする。
W3C Trace Context(標準)
traceparent: 00-abc123def456789...-span123-01
tracestate: vendor=valueversion-trace_id-parent_span_id-flags の形式
B3 Header(Zipkin由来)
X-B3-TraceId: abc123
X-B3-SpanId: span456
X-B3-ParentSpanId: span123
X-B3-Sampled: 1OpenTelemetry実装例
1 import ( 2 "go.opentelemetry.io/otel" 3 "go.opentelemetry.io/otel/attribute" 4 "go.opentelemetry.io/otel/trace" 5 ) 6 7 func handleRequest(ctx context.Context, req *Request) (*Response, error) { 8 // Tracerを取得 9 tracer := otel.Tracer("order-service") 10 11 // Spanを開始 12 ctx, span := tracer.Start(ctx, "handleRequest", 13 trace.WithSpanKind(trace.SpanKindServer), 14 ) 15 defer span.End() 16 17 // 属性を追加 18 span.SetAttributes( 19 attribute.String("user.id", req.UserID), 20 attribute.String("http.method", "POST"), 21 attribute.String("http.url", "/api/orders"), 22 ) 23 24 // 子Spanを作成(DB操作) 25 ctx, dbSpan := tracer.Start(ctx, "db.query") 26 result, err := db.Query(ctx, "SELECT * FROM orders WHERE user_id = ?", req.UserID) 27 if err != nil { 28 dbSpan.RecordError(err) 29 dbSpan.SetStatus(codes.Error, err.Error()) 30 } 31 dbSpan.End() 32 33 // エラー記録 34 if err != nil { 35 span.RecordError(err) 36 span.SetStatus(codes.Error, "failed to process request") 37 return nil, err 38 } 39 40 return &Response{Data: result}, nil 41 }
トレーシングバックエンド
Jaeger
Uber発のOSS。分散トレーシングの定番。 K8sデプロイ対応、サービスマップ、比較機能。
Zipkin
Twitter発のOSS。軽量でシンプル。 Java、Spring Cloudとの相性が良い。
Grafana Tempo
Lokiと同じ思想。インデックスなし、オブジェクトストレージ。 低コストで大量トレースを保存。
AWS X-Ray
AWSマネージド。Lambda、ECS、EKSと統合。 サービスマップ、アノテーション対応。
サンプリング戦略
全リクエストをトレースするとコストが爆発する。サンプリングで保存するトレースを選択。
確率サンプリング
一定割合(例: 1%)をランダムに保存
Rate Limiting
秒間N件まで保存(例: 100 traces/sec)
Tail-based Sampling
トレース完了後に判断。エラーや遅いトレースを優先的に保存。
推奨: ハイブリッド
エラー: 100%保存 / 遅いリクエスト: 100%保存 / 正常: 1-10%
ベストプラクティス
一部だけだとトレースが途切れる。全サービスで一貫して実装。
user_id、order_id、エラー詳細など。調査に役立つ情報。
ログにtrace_idを含めて、トレースとログを相関付け。
パスワード、トークン、PIIを属性に入れない。