🐱 うさねこ教室 Python と可観測性の教室

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)を受け付けるため、アプリケーション側のコードを変えずにバックエンドを切り替えられます。