Tracing — リクエストの旅を追う
🐱 この章の目次
分散トレーシングとは
分散トレーシング(Distributed Tracing) は、複数サービスをまたぐリクエストの経路と所要時間を可視化する技術です。 モノリスでは 1 つのスタックトレースで原因を特定できますが、マイクロサービスではリクエストが複数プロセスを横断するため、呼び出し連鎖を追う仕組みが不可欠です。 『詳解システムパフォーマンス』第 5 章でもトレーシングによるレイテンシ分析が推奨されています。
Trace と Span
Trace は 1 つのリクエストのライフサイクル全体を表すデータ構造です。 Span は Trace の中の 1 つの処理単位(例: DB クエリ、外部 API 呼び出し)で、開始時刻・終了時刻・属性を持ちます。 Span は親子関係を持ち、ツリー構造で因果関係を表現します。
Trace: abc-123
├─ Span: gateway (12ms)
│ ├─ Span: auth-service (3ms)
│ └─ Span: order-service (8ms)
│ └─ Span: db-query (5ms)
コンテキスト伝搬
サービス間で Trace を連結するには、リクエストヘッダーにトレースコンテキストを載せる必要があります。
W3C Trace Context 標準は traceparent ヘッダーで trace_id と span_id を伝搬する仕様です。
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
OpenTelemetry SDK を導入すれば、HTTP クライアントやフレームワークに自動でヘッダーを付与できます。
Python での計装
OpenTelemetry SDK を使った自動計装の例です。
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("process_order") as span:
span.set_attribute("order.id", "abc-123")
# ビジネスロジック
バックエンドの選択: Jaeger と Tempo
Jaeger は CNCF 卒業プロジェクトで、独自の UI とストレージを持つトレーシング基盤です。 Grafana Tempo はオブジェクトストレージに直接保存する設計で、Loki や Prometheus と統合しやすいのが利点です。 どちらも OpenTelemetry Protocol(OTLP)を受け付けるため、アプリケーション側のコードを変えずにバックエンドを切り替えられます。