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

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+ 지원
  • 히스토그램/Summary 메트릭에 적용
  • OpenMetrics 형식 필요
동작 원리: 메트릭 그래프에서 특정 데이터 포인트 클릭 → 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 (클릭 가능)

Grafana 데이터소스 설정

핵심 설정

  • endpoint: Prometheus/AMP 엔드포인트
  • 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"

핵심 설정

  • endpoint: 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

핵심 설정

  • tracesToLogs: 트레이스 → 로그 연결
  • tracesToMetrics: 트레이스 → 메트릭 연결
  • serviceMap: 서비스 맵 활성화
datasources: - name: Tempo type: tempo url: http://tempo:3200 jsonData: tracesToLogs: datasourceUid: loki tags: ['namespace', 'pod'] filterByTraceID: true tracesToMetrics: datasourceUid: prometheus serviceMap: datasourceUid: prometheus

Loki ↔ Tempo 양방향 연동

Loki → Tempo (Derived Fields) derivedFields: - name: TraceID matcherRegex: '"traceId":"([a-f0-9]+)"' datasourceUid: tempo
Tempo → Loki (tracesToLogs) tracesToLogs: datasourceUid: loki tags: ['namespace', 'pod'] filterByTraceID: true

Derived Fields & tracesToLogs 설정

정규식 테스터

{"timestamp":"2024-01-15T10:30:00Z","level":"ERROR","traceId":"abc123def456789012345678","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 요청이 포함된 트레이스를 검색합니다.

{ 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개 이상인 트레이스를 검색합니다.

{ 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가 있는 트레이스를 검색합니다.

Service Map 시각화

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

Healthy
Slow (p99 > 500ms)
Error (> 1%)

RED Method + 4 Golden Signals

서비스 레벨 모니터링에 적합 (마이크로서비스)

1,247
Rate (req/s)

초당 요청 수

0.12%
Errors

에러율

45ms
Duration (p99)

응답 시간

Google SRE 방법론 - 인프라 + 서비스 모니터링

45ms
Latency

요청 처리 시간

1,247
Traffic

시스템 부하

0.12%
Errors

실패율

72%
Saturation

리소스 포화도

장애 분석 워크플로우

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

Receivers

텔레메트리 데이터 수신

  • OTLP (gRPC): 4317 포트
  • OTLP (HTTP): 4318 포트
  • Jaeger: 14250/14268 포트
  • Zipkin: 9411 포트
otel-collector-config.yaml receivers: otlp: protocols: grpc: { endpoint: "0.0.0.0:4317" } http: { endpoint: "0.0.0.0:4318" } processors: batch: timeout: 5s send_batch_size: 1000 tail_sampling: policies: - name: errors type: status_code status_code: { status_codes: [ERROR] } - name: slow type: latency latency: { threshold_ms: 1000 } - name: default type: probabilistic probabilistic: { sampling_percentage: 10 } exporters: otlp/tempo: endpoint: tempo-distributor:4317 prometheusremotewrite: endpoint: http://prometheus:9090/api/v1/write service: pipelines: traces: receivers: [otlp] processors: [batch, tail_sampling] exporters: [otlp/tempo]

비용 효율적 샘플링 전략

샘플링률 조절

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

샘플링 전략 비교

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

Tail Sampling 권장 정책

  • errors: 모든 에러 100% 수집
  • slow: 1초 이상 요청 100% 수집
  • default: 나머지 10% 랜덤 샘플링

Block 3 요약 & 퀴즈

핵심 개념 정리

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