APM & 분산 추적으로 성능 병목 찾기 — OpenTelemetry, Jaeger 실전
로그와 지표(Metrics)만으로는 “어디가 느린지”를 알기 어렵습니다. APM(Application Performance Monitoring)은 요청 하나가 서비스를 거치는 전 과정을 추적합니다.
APM이 해결하는 문제
섹션 제목: “APM이 해결하는 문제”사용자 요청: 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 계측 (언어별)
섹션 제목: “OpenTelemetry 계측 (언어별)”OpenTelemetry는 APM 벤더에 종속되지 않는 표준 계측 라이브러리입니다.
Python (FastAPI)
섹션 제목: “Python (FastAPI)”from opentelemetry import tracefrom opentelemetry.instrumentation.fastapi import FastAPIInstrumentorfrom opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentorfrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporterfrom opentelemetry.sdk.trace import TracerProviderfrom 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 resultNode.js (Express)
섹션 제목: “Node.js (Express)”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();Java (Spring Boot)
섹션 제목: “Java (Spring Boot)”<dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-spring-boot-starter</artifactId> <version>2.3.0</version></dependency>otel: exporter: otlp: endpoint: http://jaeger:4317 service: name: order-service instrumentation: jdbc: enabled: true # DB 쿼리 자동 추적 spring-web: enabled: trueJaeger로 트레이스 시각화
섹션 제목: “Jaeger로 트레이스 시각화”오픈소스 분산 추적 플랫폼입니다. OpenTelemetry와 바로 연동됩니다.
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 타임라인 확인상용 APM 비교
섹션 제목: “상용 APM 비교”| 도구 | 특징 | 적합한 환경 |
|---|---|---|
| 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)다음 단계
섹션 제목: “다음 단계”이 가이드를 내 서비스에 직접 적용해 보세요.
TestForge 무료 스캔 시작 →