콘텐츠로 이동

APM & 분산 추적으로 성능 병목 찾기 — OpenTelemetry, Jaeger 실전

로그와 지표(Metrics)만으로는 “어디가 느린지”를 알기 어렵습니다. APM(Application Performance Monitoring)은 요청 하나가 서비스를 거치는 전 과정을 추적합니다.

사용자 요청: POST /checkout → 평균 3.2초
로그만 보면: 에러 없음, 3.2초 걸림
APM으로 보면:
├─ 인증 미들웨어 12ms
├─ 재고 확인 API 호출 45ms
├─ DB 주문 생성 쿼리 87ms
├─ 결제 게이트웨이 호출 2,940ms ← 병목!
└─ 응답 직렬화 8ms

원인이 결제 게이트웨이라는 것을 APM 없이는 추측에 의존해야 합니다.

분산 추적 (Distributed Tracing) 개념

섹션 제목: “분산 추적 (Distributed Tracing) 개념”

마이크로서비스 환경에서 요청은 여러 서비스를 거칩니다.

[클라이언트]
│ TraceID: abc-123
[API Gateway] Span 1: 5ms
├──▶ [User Service] Span 2: 12ms
├──▶ [Order Service] Span 3: 890ms
│ │
│ └──▶ [DB] Span 4: 870ms ← 병목
└──▶ [Payment Service] Span 5: 45ms
  • Trace: 요청 하나의 전체 여정 (TraceID로 식별)
  • Span: 각 서비스/함수의 처리 단위

OpenTelemetry는 APM 벤더에 종속되지 않는 표준 계측 라이브러리입니다.

from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# 트레이서 설정
provider = TracerProvider()
provider.add_span_processor(
BatchSpanProcessor(OTLPSpanExporter(endpoint="http://jaeger:4317"))
)
trace.set_tracer_provider(provider)
# FastAPI 자동 계측
FastAPIInstrumentor.instrument_app(app)
SQLAlchemyInstrumentor().instrument(engine=engine)
# 커스텀 Span 추가
tracer = trace.get_tracer(__name__)
async def process_payment(order_id: str):
with tracer.start_as_current_span("payment.process") as span:
span.set_attribute("order.id", order_id)
result = await payment_gateway.charge(order_id)
span.set_attribute("payment.status", result.status)
return result
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: 'http://jaeger:4317',
}),
instrumentations: [getNodeAutoInstrumentations()],
// HTTP, Express, MySQL, Redis 등 자동 계측
});
sdk.start();
pom.xml
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
application.yml
otel:
exporter:
otlp:
endpoint: http://jaeger:4317
service:
name: order-service
instrumentation:
jdbc:
enabled: true # DB 쿼리 자동 추적
spring-web:
enabled: true

오픈소스 분산 추적 플랫폼입니다. OpenTelemetry와 바로 연동됩니다.

docker-compose.yml
services:
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686" # Jaeger UI
- "4317:4317" # OTLP gRPC
environment:
- COLLECTOR_OTLP_ENABLED=true
브라우저에서 http://localhost:16686 접속
→ Service 선택 → Find Traces
→ 느린 요청 클릭 → Span 타임라인 확인
도구특징적합한 환경
Datadog APM풍부한 대시보드, 알림 연동엔터프라이즈
New Relic코드 레벨 분석, AI 이상 탐지풀스택 모니터링
Grafana Tempo오픈소스, Prometheus 연동자체 운영 선호
Jaeger순수 분산 추적, 경량트레이스만 필요
Sentry에러 추적 + 성능 프로파일링에러-성능 통합
Span 시간이 길다
├─ DB Span이 길다
│ → EXPLAIN ANALYZE로 쿼리 분석
│ → N+1 쿼리 여부 확인 (DB span이 수십~수백 개)
├─ 외부 API Span이 길다
│ → 타임아웃 설정 확인
│ → 병렬화 가능 여부 검토
│ → 캐싱(Redis) 도입 검토
└─ 자체 코드 Span이 길다
→ 커스텀 Span 세분화로 함수 단위 분석
→ CPU 프로파일러 연동 (py-spy, async-profiler)
visitor count

이 가이드를 내 서비스에 직접 적용해 보세요.

TestForge 무료 스캔 시작 →