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

エンドツーエンドで動かす

🐱 この章の目次

構成の全体像

本章では、FastAPI アプリに OTel SDK を組み込み、Collector 経由で Jaeger・Prometheus・Grafana へデータを送る一気通貫の環境を構築します。 すべてのコンポーネントを Docker Compose で起動し、リクエストがトレースされる様子をブラウザで確認します。

コンポーネントポート役割
FastAPI アプリ8000計装対象のサービス
OTel Collector4317テレメトリーの中継
Jaeger UI16686トレースの可視化
Prometheus9090メトリクスの収集
Grafana3000ダッシュボード

Docker Compose

services:
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      OTEL_SERVICE_NAME: demo-app
      OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
    depends_on:
      - otel-collector

  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.104.0
    command: ["--config", "/etc/otel/config.yaml"]
    volumes:
      - ./otel-collector-config.yaml:/etc/otel/config.yaml
    ports:
      - "4317:4317"
      - "8889:8889"

  jaeger:
    image: jaegertracing/all-in-one:1.58
    ports:
      - "16686:16686"
      - "4317"

  prometheus:
    image: prom/prometheus:v2.53.0
    volumes:
      - ./prometheus.yaml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana:11.1.0
    ports:
      - "3000:3000"
    environment:
      GF_AUTH_ANONYMOUS_ENABLED: "true"
      GF_AUTH_ANONYMOUS_ORG_ROLE: Admin

アプリケーションの計装コード

FastAPI アプリに 3 シグナルすべてを組み込んだエントリーポイントの例です。

import logging
import time
from fastapi import FastAPI
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
from opentelemetry._logs import set_logger_provider
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor

# Traces
trace_provider = TracerProvider()
trace_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(trace_provider)

# Metrics
metric_reader = PeriodicExportingMetricReader(OTLPMetricExporter())
metrics.set_meter_provider(MeterProvider(metric_readers=[metric_reader]))

# Logs
log_provider = LoggerProvider()
log_provider.add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter()))
set_logger_provider(log_provider)
logging.getLogger().addHandler(LoggingHandler(level=logging.INFO, logger_provider=log_provider))

app = FastAPI()
FastAPIInstrumentor.instrument_app(app)

logger = logging.getLogger(__name__)
tracer = trace.get_tracer(__name__)
meter = metrics.get_meter(__name__)
request_counter = meter.create_counter("app.requests", unit="1")

@app.get("/orders/{order_id}")
async def get_order(order_id: str):
    request_counter.add(1, {"endpoint": "/orders"})
    with tracer.start_as_current_span("fetch-order") as span:
        span.set_attribute("order.id", order_id)
        logger.info(f"Fetching order {order_id}")
        await simulate_latency()
        return {"order_id": order_id, "status": "shipped"}

async def simulate_latency():
    """意図的に遅延を入れてトレースで確認する"""
    time.sleep(0.3)

トレースの確認

環境を起動してリクエストを送信します。

docker compose up -d
curl http://localhost:8000/orders/abc-123

ブラウザで http://localhost:16686 を開き、Jaeger UI から demo-app サービスを選択してください。 GET /orders/{order_id} の Span とその子 Span fetch-order が表示され、処理時間の内訳を確認できます。

レイテンシ問題のデバッグ

simulate_latency が 300ms のスリープを挿入しているため、Jaeger のトレースで fetch-order Span に遅延が集中していることが分かります。 Prometheus で app.requests カウンターの増加を確認し、Grafana ダッシュボードでレイテンシの傾向を可視化しましょう。 トレース・メトリクス・ログを横断的に分析することで、問題の特定から原因の深掘りまでを効率的に進められます。