[OpenTelemetry] Jaeger와 OpenSearch를 활용한 분산 트레이싱 구축
마이크로서비스 환경에서 "어디서 느려졌지?"를 추적하는 건 쉽지 않다. OpenTelemetry + Jaeger + OpenSearch를 활용해 분산 트레이싱 인프라를 구축한 경험을 정리한다.
1. 왜 분산 트레이싱일까?
게임 서버를 마이크로서비스로 운영하면서 겪은 문제:
- 요청이 어디서 병목인지 파악이 어려움: GameServer → ChatServer → DB → Redis를 거치는 요청에서 어디가 느린 건지?
- 에러 발생 시 추적이 어려움: 로그만으로는 전체 흐름을 파악하기 힘듦
- 서비스 간 의존성 파악 어려움: 어떤 서비스가 어떤 서비스를 호출하는지?
flowchart LR
Client --> GameServer
GameServer --> ChatServer
GameServer --> MySQL
GameServer --> Redis
ChatServer --> RealTimeServer
style GameServer fill:#ef4444,color:#fff2. 아키텍처 설계
기술 스택 선정
| 컴포넌트 | 역할 | 선택 이유 |
|---|---|---|
| OpenTelemetry SDK | Trace 데이터 생성 | 벤더 중립적, 표준화 |
| OpenTelemetry Collector | 수집/처리/전송 | Tail-based sampling 가능 |
| Jaeger | 시각화 | 무료, 강력한 UI |
| OpenSearch | 영구 저장 | AWS 관리형, Elasticsearch 호환 |
전체 아키텍처
flowchart TB
subgraph Pod["Game Server Pod"]
App["GameServer<br/>.NET + OTel SDK"]
Collector["OTEL Collector<br/>(Sidecar)"]
end
App -->|localhost:4317| Collector
Collector -->|OTLP gRPC| Jaeger
Jaeger -->|Storage| OpenSearch
style Collector fill:#3b82f6,color:#fff
style Jaeger fill:#10b981,color:#fff3. 핵심 구현
3.1 OpenTelemetry SDK 설정
services.AddOpenTelemetry()
.ConfigureResource(resource => resource
.AddService("MonsterQuestGameServer", serviceVersion: version))
.WithTracing(tracing => tracing
.AddSource("MonsterQuest.GameServer")
.SetSampler(new AlwaysOnSampler()) // Collector에서 sampling
.AddEntityFrameworkCoreInstrumentation() // MySQL
.AddRedisInstrumentation() // Redis
.AddGrpcClientInstrumentation() // gRPC
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("http://localhost:4317");
options.Protocol = OtlpExportProtocol.Grpc;
}));핵심 포인트:
AlwaysOnSampler: 앱에서는 모든 Trace를 전송, Collector에서 샘플링 결정localhost:4317: Sidecar이므로 localhost 사용- Auto Instrumentation: EF Core, Redis, gRPC, HTTP 자동 계측
3.2 Sidecar 패턴
각 Pod에 OTEL Collector를 Sidecar로 배포:
containers:
- name: game-server
# 앱 컨테이너
- name: otel-collector
image: opentelemetry-collector
env:
- name: GOMEMLIMIT
value: "200MiB" # 컨테이너 메모리의 80%장점:
- 네트워크 지연 최소화 (localhost)
- 앱별 독립적인 Collector 설정
- 장애 격리
3.3 Tail-based Sampling
processors:
tail_sampling:
decision_wait: 10s
num_traces: 50000
policies:
- name: error-traces
type: status_code
status_code:
status_codes: [ERROR]
- name: success-traces
type: probabilistic
probabilistic:
sampling_percentage: 10- 에러 Trace: 100% 저장 (문제 분석에 필수)
- 성공 Trace: 10%만 저장 (비용 절감)
4. Jaeger v2 설정
Jaeger v2는 OpenTelemetry Collector 기반으로 변경되었다.
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
jaeger_storage_exporter:
trace_storage: opensearch_backend
extensions:
jaeger_storage:
backends:
opensearch_backend:
opensearch:
server_urls: "${OPENSEARCH_URL}"
jaeger_query:
trace_storage: opensearch_backend
ui:
config_file: /etc/jaeger/ui-config.json
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [jaeger_storage_exporter]5. AWS OpenSearch 연동
AWS OpenSearch는 SigV4 인증이 필요하다. aws-sigv4-proxy 사이드카를 사용:
containers:
- name: jaeger
# Jaeger 컨테이너
env:
- name: OPENSEARCH_URL
value: "http://localhost:8080" # proxy 경유
- name: aws-sigv4-proxy
image: amazon/aws-sigv4-proxy
args:
- --host=search-xxx.es.amazonaws.com
- --region=ap-northeast-2
- --service=es6. Span Metrics (RED 메트릭 자동 생성)
Jaeger의 SpanMetrics Connector로 Trace에서 자동으로 RED 메트릭 생성:
connectors:
spanmetrics:
namespace: traces.spanmetrics
dimensions:
- name: service
- name: operation
- name: status_code
exporters:
prometheusremotewrite:
endpoint: "${PROMETHEUS_REMOTE_WRITE_URL}"
service:
pipelines:
traces:
exporters: [jaeger_storage_exporter, spanmetrics]
metrics/spanmetrics:
receivers: [spanmetrics]
exporters: [prometheusremotewrite]생성되는 메트릭:
calls_total: 호출 수duration_milliseconds_bucket: 응답 시간 분포
7. 결과
Before/After
| 항목 | Before | After |
|---|---|---|
| 병목 파악 시간 | 수십 분 (로그 분석) | 수 초 (Jaeger UI) |
| 에러 추적 | 어려움 | Trace로 즉시 확인 |
| 서비스 의존성 | 문서화 필요 | 자동 생성 |
알게 된 것
-
Sidecar 패턴이 핵심
- localhost 통신으로 지연 최소화
- 앱별 독립적인 설정 가능
-
Tail-based Sampling 필수
- 에러는 100%, 성공은 일부만 저장
- 저장 비용 대폭 절감
-
GOMEMLIMIT 설정 중요
- Go 런타임이 컨테이너 메모리를 인식
- OOMKilled 방지
-
Jaeger v2는 OTel Collector 기반
- 기존 Jaeger exporter 대신 OTLP 사용
- 더 유연한 파이프라인 구성
마무리
분산 트레이싱은 마이크로서비스 운영에 없으면 안 되는 것 중 하나다. OpenTelemetry 표준을 쓰면 벤더 종속 없이 유연하게 구성할 수 있다.
핵심은 "어디서 느려졌는지, 어디서 에러가 났는지"를 즉시 파악할 수 있다는 것이다.
Loading comments...