분산 추적

마이크로서비스 간 요청 흐름을 추적하고 성능을 분석하는 방법


마이크로서비스 간 요청 흐름을 추적하고 성능을 분석하는 방법

목표: 분산 시스템의 병목 현상을 식별하고 최적화할 수 있는 수준


🎯 분산 추적 개요

개념

graph LR
    subgraph "분산 추적"
        CLIENT["👤 Client"] -->|"Trace ID: abc123"| GW["🌐 Gateway"]
        GW -->|"Trace ID: abc123"| SVC1["📦 Service A"]
        SVC1 -->|"Trace ID: abc123"| SVC2["📦 Service B"]
        SVC2 -->|"Trace ID: abc123"| DB["🗄️ Database"]
        
        SVC1 -->|"Span: 50ms"| JAEGER["🎯 Jaeger"]
        SVC2 -->|"Span: 100ms"| JAEGER
        DB -->|"Span: 200ms"| JAEGER
    end

핵심 개념:

  • Trace: 전체 요청 흐름
  • Span: 개별 작업 단위
  • Trace ID: 전체 요청 식별자
  • Span ID: 개별 작업 식별자
  • Parent Span ID: 부모 작업 식별자

📊 OpenTelemetry

개요

관측성 데이터(메트릭, 로그, 추적)의 표준.

graph TB
    subgraph "OpenTelemetry 아키텍처"
        APP["📦 Application"] -->|"OTel SDK"| COLLECTOR["🌐 OTel Collector"]
        COLLECTOR -->|"Trace"| JAEGER["🎯 Jaeger"]
        COLLECTOR -->|"Metric"| PROM["📊 Prometheus"]
        COLLECTOR -->|"Log"| ES["🗄️ Elasticsearch"]
    end

애플리케이션 계측

# Python 예시
from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor

# 트레이서 설정
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)

# Jaeger 내부 출력
jaeger_exporter = JaegerExporter(
    agent_host_name="jaeger-agent",
    agent_port=6831,
)

span_processor = BatchSpanProcessor(jaeger_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

# 자동 계측
FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()

# 수동 계측
with tracer.start_as_current_span("process_order") as span:
    span.set_attribute("order.id", "12345")
    span.set_attribute("order.amount", 100.00)
    
    with tracer.start_as_current_span("validate_payment"):
        validate_payment()
    
    with tracer.start_as_current_span("update_inventory"):
        update_inventory()

🎯 Jaeger

설치

# Jaeger Operator 설치
kubectl create namespace observability
kubectl apply -f https://github.com/jaegertracing/jaeger-operator/releases/latest/download/jaeger-operator.yaml -n observability

# Jaeger 인스턴스 생성
kubectl apply -f - <<EOF
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: simplest
EOF

# 또는 Helm
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm install jaeger jaegertracing/jaeger

접근

# 포트 포워딩
kubectl port-forward svc/simplest-query 16686:16686

# 브라우저에서 접속
http://localhost:16686

🔍 추적 분석

Jaeger UI

# 1. 서비스 선택
# 2. 작업 선택
# 3. 태그 필터
# 4. 시간 범위
# 5. 검색

주요 기능

기능설명
Trace 검색서비스, 작업, 태그로 검색
Trace 뷰전체 요청 흐름 시각화
Span 상세개별 작업의 태그, 로그
의존성 그래프서비스 간 호출 관계
비교여러 Trace 비교

📋 추적 모범 사례

계측 가이드

# 좋은 예시
with tracer.start_as_current_span("process_payment") as span:
    span.set_attribute("payment.method", "credit_card")
    span.set_attribute("payment.amount", 100.00)
    span.set_attribute("payment.currency", "USD")
    
    try:
        result = process_payment()
        span.set_attribute("payment.status", "success")
        span.set_attribute("payment.transaction_id", result.id)
    except PaymentError as e:
        span.set_attribute("payment.status", "failed")
        span.set_attribute("error.message", str(e))
        span.set_status(StatusCode.ERROR, str(e))
        raise

# 나쁜 예시
with tracer.start_as_current_span("do_stuff"):
    do_stuff()  # 태그 없음, 의미 없는 이름

샘플링

# OpenTelemetry Collector 설정
apiVersion: v1
kind: ConfigMap
metadata:
  name: otel-collector-config
data:
  otel-collector-config.yaml: |
    receivers:
      otlp:
        protocols:
          grpc:
          http:
    
    processors:
      batch:
      tail_sampling:
        decision_wait: 10s
        num_traces: 100
        expected_new_traces_per_sec: 10
        policies:
          - name: errors
            type: status_code
            status_code: {status_codes: [ERROR]}
          - name: slow_requests
            type: latency
            latency: {threshold_ms: 1000}
    
    exporters:
      jaeger:
        endpoint: jaeger-collector:14250
        tls:
          insecure: true
    
    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [batch, tail_sampling]
          exporters: [jaeger]

📋 체크리스트

  • OpenTelemetry SDK 설치
  • 애플리케이션 계측
  • Jaeger 설치
  • 추적 데이터 확인
  • 샘플링 정책 설정
  • 의존성 그래프 분석
  • 성능 병목 식별
  • 알림 설정

🔗 관련 노트

  • 03-05-모니터링-Prometheus-Grafana — 메트릭 모니터링
  • 03-06-로깅-EFK-스택 — 로그 수집
  • 03-10-서비스메시-Istio — 자동 추적

마지막 업데이트: 2026-06-01