EKS Observability

Observability Correlation

Junseok Oh

Sr. Solutions Architect

Amazon Web Services

도구 간 데이터 연결이 안 됩니다

Block 3: Observability Correlation (35분)

"Grafana에 Prometheus, Loki, Tempo 다 연결했는데, 에러 발생 시 각각 따로 검색해야 합니다"
"메트릭 스파이크가 보이는데, 관련 로그와 트레이스를 찾는 데 20분 이상 걸립니다"
핵심 문제: 3가지 시그널(Metrics, Logs, Traces)이 있지만 연결되지 않으면 MTTR(평균 복구 시간)이 늘어남
20분+
수동 상관관계 분석
3개
별도 UI 전환 필요
컨텍스트 손실
시그널 간 연결고리 부재

관측성 3대 축 + Grafana 스택

📊

Metrics

Prometheus / AMP

"무엇이 문제인가?"

수치 기반 이상 탐지
📝

Logs

Loki

"왜 발생했는가?"

상세 컨텍스트
🔗

Traces

Tempo

"어디서 발생했는가?"

분산 요청 추적

Grafana

통합 대시보드

3-Signal Correlation 시뮬레이터

Exemplar를 통한 메트릭 → 트레이스 → 로그 연결

📖 더 알아보기: 상관 분석 전략

Exemplars: 메트릭 → 트레이스 연결

Exemplar란?

메트릭(주로 히스토그램)의 특정 데이터 포인트에 TraceID를 첨부하여 해당 요청의 트레이스로 바로 이동할 수 있게 해주는 기능

  • Prometheus 2.26+: --enable-feature=exemplar-storage 필요 (3.x에서도 feature flag)
  • OpenMetrics 기준 Counter, Histogram bucket에 적용 (Summary 불가)
  • OpenMetrics 형식 필요
  • AMP는 Exemplar 미지원 (AWS AMG 문서 기준)
동작 원리: 메트릭 그래프에서 특정 데이터 포인트 클릭 → TraceID 확인 → Tempo에서 상세 트레이스 조회

Prometheus Datasource 설정

prometheus-datasource.yaml apiVersion: 1 datasources: - name: Prometheus type: prometheus url: http://prometheus:9090 jsonData: exemplarTraceIdDestinations: - name: traceID datasourceUid: tempo urlDisplayLabel: "View Trace"

데이터 포인트 위의 ◆ = Exemplar (클릭 가능)

📖 더 알아보기: Exemplars & Grafana 연동 설정 | 출처: AWS AMG Exemplars 문서

Grafana 데이터소스 설정

핵심 설정

  • url: Prometheus/AMP 엔드포인트
  • AMP: SigV4 인증 필요. Amazon Managed Grafana 12부터 core Prometheus 플러그인은 SigV4 미지원, Amazon Managed Service for Prometheus 데이터소스 플러그인 사용
  • exemplarTraceIdDestinations: 트레이스 연결 설정
  • httpMethod: GET 또는 POST
datasources: - name: Prometheus type: prometheus access: proxy url: http://prometheus:9090 jsonData: httpMethod: POST exemplarTraceIdDestinations: - name: traceID datasourceUid: tempo urlDisplayLabel: "View Trace"

핵심 설정

  • url: Loki 게이트웨이 URL
  • derivedFields: TraceID 추출 정규식
  • maxLines: 최대 로그 라인 수
datasources: - name: Loki type: loki access: proxy url: http://loki-gateway:3100 jsonData: maxLines: 1000 derivedFields: - name: TraceID matcherRegex: '"traceId":"([a-f0-9]+)"' url: '$${__value.raw}' datasourceUid: tempo

핵심 설정

  • tracesToLogsV2: 트레이스 → 로그 연결
  • tracesToMetrics: 트레이스 → 메트릭 연결
  • serviceMap: 서비스 맵 (traces_service_graph_* 메트릭 필요)
  • nodeGraph: 트레이스 그래프 뷰 활성화
datasources: - name: Tempo type: tempo url: http://tempo:3200 jsonData: tracesToLogsV2: datasourceUid: loki tags: [{ key: 'namespace' }, { key: 'pod' }] filterByTraceID: true tracesToMetrics: datasourceUid: prometheus tags: [{ key: 'service.name', value: 'service' }] queries: - name: 'Request rate' # OTel connector는 traces_span_metrics_* query: >- sum(rate(traces_spanmetrics_calls_total{$$__tags}[5m])) serviceMap: datasourceUid: prometheus nodeGraph: enabled: true
📖 더 알아보기: Grafana 연동 가이드 | 출처: Grafana Loki 데이터소스 provisioning | 출처: Grafana Tempo 데이터소스 provisioning

Loki ↔ Tempo 양방향 연동

Tempo는 트레이스 저장소, Loki는 로그 저장소: Grafana가 양쪽을 조회하며 연결합니다. 로그의 traceId로 트레이스를 찾거나, 트레이스의 메타데이터로 관련 로그를 필터링합니다.

Loki → Tempo (Derived Fields) derivedFields: - name: TraceID matcherRegex: '"traceId":"([a-f0-9]+)"' datasourceUid: tempo
Tempo → Loki (tracesToLogsV2) tracesToLogsV2: datasourceUid: loki tags: [{ key: 'namespace' }, { key: 'pod' }] filterByTraceID: true
📖 더 알아보기: Loki 운영 가이드

Derived Fields & tracesToLogsV2 설정

정규식 테스터

{"timestamp":"2024-01-15T10:30:00Z","level":"ERROR","traceId":"4bf92f3577b34da6a3ce929d0e0e4736","service":"payment-service","message":"Payment failed: Connection timeout to RDS"}

일반적인 TraceID 패턴

형식정규식
JSON 필드"traceId":"([a-f0-9]+)"
W3C Trace Contexttraceparent.*-([a-f0-9]{32})-
Jaegeruber-trace-id:([a-f0-9]+):

주의사항

  • 정규식은 캡처 그룹 ()이 필수
  • 첫 번째 캡처 그룹이 TraceID로 사용됨
  • 대소문자 구분에 주의 (보통 소문자)
  • 로그 형식 변경 시 정규식도 업데이트 필요

TraceQL 소개

Tempo의 강력한 트레이스 쿼리 언어

{ resource.service.name = "payment-service" }

payment-service에서 발생한 모든 트레이스를 검색합니다.

{ span.http.method = "POST" }

HTTP POST 요청이 포함된 트레이스를 검색합니다.

참고: 최신 OTel semconv는 http.request.method, http.response.status_code를 사용합니다. 구형 SDK는 http.method, http.status_code를 emit하므로 SDK 버전에 맞춰 속성명을 선택하세요.

{ status = error }

에러 상태의 span이 포함된 트레이스를 검색합니다.

{ span.http.status_code >= 500 }

HTTP 5xx 에러가 발생한 트레이스를 검색합니다.

{ duration > 1s }

1초 이상 걸린 span이 포함된 트레이스를 검색합니다.

{ duration > 500ms && duration < 2s }

500ms ~ 2s 사이의 span을 검색합니다.

{ span.http.status_code >= 400 && duration > 500ms }

HTTP 4xx/5xx 에러이면서 500ms 이상 걸린 트레이스를 검색합니다.

{ resource.service.name = "api-gateway" && status = error } | count() > 5

api-gateway에서 에러 span이 5개 초과인 트레이스를 검색합니다.

>> (descendant) 연산자: 두 span 사이의 호출 경로(parent → child)를 추적합니다. A >> B는 "A span 하위에 B span이 존재하는 트레이스"를 의미합니다.
{ resource.service.name = "api-gateway" } >> { resource.service.name = "payment-service" }

api-gateway에서 payment-service로 이어지는 호출 경로를 가진 트레이스를 검색합니다.

{ name = "HTTP GET" } >> { name = "SELECT" && duration > 100ms }

HTTP GET 이후 100ms 이상 걸리는 DB SELECT가 있는 트레이스를 검색합니다.

📖 더 알아보기: TraceQL 분석 패턴

Service Map 시각화

마이크로서비스 의존성 그래프

● Healthy
● Slow (p99 > 500ms)
● Error (> 1%)
📖 더 알아보기: Service Map & 서비스 그래프 설정

RED Method vs 4 Golden Signals

두 방법론의 관계와 차이: 메트릭 3개가 겹칩니다

RED Tom Wilkie (Grafana Labs)

마이크로서비스 관점: "내 서비스가 잘 응답하는가?"

1,247
Rate (req/s)
0.12%
Errors
45ms
Duration (p99)
4 Golden Google SRE Book (2016)

인프라 + 서비스 관점: "시스템 전체가 건강한가?"

45ms
Latency
1,247
Traffic
0.12%
Errors
72%
Saturation
RED Method4 Golden SignalsPromQL 예시
Rate=Trafficrate(http_requests_total[5m])
Errors=Errorsrate(http_requests_total{code=~"5.."}[5m])
Duration=Latencyhistogram_quantile(0.99, ...)
없음+Saturationcontainer_memory_usage / limits
핵심: Saturation이 유일한 차이. RED는 "서비스가 요청을 잘 처리하는가"에 집중, Golden Signals는 "리소스가 포화되고 있는가"까지 포함합니다.

장애 분석 워크플로우

1
Alert 수신
2
Dashboard 확인
3
Trace 분석
4
Log 확인
5
근본 원인 파악

1. Alert 수신 (AlertManager → Slack)

[FIRING] PaymentService High Error Rate - alertname: PaymentServiceErrors - severity: critical - service: payment-service - error_rate: 5.2% - threshold: 1%

OpenTelemetry Collector 파이프라인

Receivers
→
Processors
→
Exporters
+
Connectors

Receivers

텔레메트리 데이터 수신

  • OTLP (gRPC): 4317 포트 (OpenTelemetry Protocol)
  • OTLP (HTTP): 4318 포트
  • Jaeger: 14250/14268 포트
  • Zipkin: 9411 포트
otel-collector-config.yaml (1/2) receivers: otlp: # 4317, 4318 protocols: { grpc: {}, http: {} } processors: memory_limiter: check_interval: 1s limit_percentage: 80 tail_sampling: decision_wait: 10s policies: [...] # errors, slow, 10% batch: { timeout: 5s } connectors: spanmetrics: {} # traces → RED metrics exporters: otlp_grpc/tempo: # 구 이름 otlp endpoint: tempo-distributor:4317 tls: { insecure: true } # 운영은 실제 TLS otlp_http/loki: # loki exporter 대체 endpoint: http://loki-gateway/otlp tls: { insecure: true } prometheusremotewrite: # AMP는 sigv4auth endpoint: http://prometheus:9090/api/v1/write
otel-collector-config.yaml (2/2) service: pipelines: traces: receivers: [otlp] processors: [memory_limiter, tail_sampling, batch] exporters: [otlp_grpc/tempo] traces/spanmetrics: # 샘플링 전 전체 span receivers: [otlp] exporters: [spanmetrics] metrics: receivers: [otlp, spanmetrics] processors: [memory_limiter, batch] exporters: [prometheusremotewrite] logs: receivers: [otlp] processors: [memory_limiter, batch] exporters: [otlp_http/loki] # Prometheus: remote-write receiver 활성화 # tail_sampling: 한 trace의 span을 # 같은 Collector로 (loadbalancing)
📖 더 알아보기: 관측성 스택 아키텍처 | 출처: opentelemetry-collector-contrib CHANGELOG

비용 효율적 샘플링 전략

샘플링률 조절

1% 100% 10%
1M
일일 트레이스
$1,500
월 저장 비용 (예시 가정)
100%
Error 캡처율
Tip: Tail Sampling을 사용하면 샘플링률을 낮춰도 모든 에러를 캡처할 수 있습니다.

예시 가정: 100% 기준 일 10M 트레이스, 트레이스 100만 건당 월 $50 저장 비용. 실제 비용은 span 크기, 보존 기간, 백엔드에 따라 달라집니다.

Adaptive Sampling이란?

트래픽 양에 따라 샘플링률을 자동 조절하는 전략. OTEL Collector의 tail_sampling + probabilistic_sampler를 조합하여 구현합니다.

  • Low traffic (< 100 req/s): 50% 수집, 데이터 충분
  • Normal (100-1k req/s): 10% 수집, 비용 최적화
  • High traffic (> 1k req/s): 1-5% 수집, 저장소 보호
  • 에러/느린 요청은 항상 100% 수집 (Tail Sampling과 동일)

샘플링 전략 비교 (수치는 예시 가정)

전략 샘플링률 비용 절감 Error 캡처
Head Sampling 10% 90% 10%
Tail Sampling 10% + errors 80-90% 100%
Adaptive 동적 (1~50%) 85-95% 100%
No Sampling 100% 0% 100%

Tail Sampling 권장 정책

  • errors: 모든 에러 100% 수집
  • slow: 1초 이상 요청 100% 수집
  • default: 나머지 10% 랜덤 샘플링
📖 더 알아보기: Tempo 운영 가이드 | 📖 분산 추적 & 샘플링 전략 상세

Block 3 요약 & 퀴즈

핵심 개념 정리

  • Exemplars: 메트릭 → 트레이스 연결
  • Derived Fields: 로그 → 트레이스 연결
  • tracesToLogsV2: 트레이스 → 로그 연결
  • TraceQL: 강력한 트레이스 쿼리 언어
  • Tail Sampling: 비용 효율적 샘플링
1. 메트릭에서 트레이스로 연결하는 Grafana 기능은?
2. Loki에서 TraceID를 추출하는 설정은?
3. 비용 효율적이면서 모든 에러를 캡처하는 샘플링 전략은?

Thank You

Block 3 — 통합 관측성: Logs/Metrics/Traces 상관분석 완료

← 목차로 돌아가기 다음: Block 4 →