分散トレース

リクエストの流れを可視化

分散トレースは、マイクロサービス環境でリクエストがどのサービスを通過し、 どこで時間がかかっているかを可視化する技術。 パフォーマンス問題やエラーの根本原因特定に不可欠です。

基本概念

Trace(トレース)

1つのリクエストの全体像。 複数のSpanで構成される。 一意のtrace_idで識別。

Span(スパン)

1つの処理単位(操作)。 開始時刻、終了時刻、属性、 親Spanへの参照を持つ。

Context(コンテキスト)

trace_id、span_id、フラグ。 サービス間でHTTPヘッダー等で 伝播(Propagation)される。

トレースの構造

Trace: abc123 (trace_id)
Span: api-gateway0-150ms
Span: auth-service10-40ms
Span: order-service50-140ms
Span: PostgreSQL query60-130ms

上記の例では、DBクエリ(70ms)がボトルネックであることが分かる。 トレースを見ることで、どこを最適化すべきか特定できる。

Spanの属性

属性説明
trace_idトレース全体のIDabc123def456...
span_idこのSpanのIDspan789
parent_span_id親SpanのIDspan456
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=value

version-trace_id-parent_span_id-flags の形式

B3 Header(Zipkin由来)

X-B3-TraceId: abc123
X-B3-SpanId: span456
X-B3-ParentSpanId: span123
X-B3-Sampled: 1

OpenTelemetry実装例

tracing.go
go
1import (
2 "go.opentelemetry.io/otel"
3 "go.opentelemetry.io/otel/attribute"
4 "go.opentelemetry.io/otel/trace"
5)
6
7func 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を属性に入れない。