본문 바로가기
서버 인프라 실무

트레이스 기반 SLO 구축: RED/USE 메트릭, 샘플링 전략, Grafana 경보까지 한 번에

by yamoojin83 2025. 10. 30.

트레이스 기반 SLO 구축: RED/USE 메트릭, 샘플링 전략, Grafana 경보까지 한 번에

이전 글에서 OpenTelemetry + Tempo로 분산 트레이싱 파이프라인을 만들었습니다. 이제 그 트레이스에서 직접 SLI/SLO를 뽑아 경보까지 이어가 봅니다. 핵심은 RED(Rate·Errors·Duration)와 USE(Utilization·Saturation·Errors) 모델을 트레이스/메트릭에 매핑하고, 샘플링 손실을 고려한 집계 방법을 정하는 것입니다.


1) 왜 “트레이스 기반” SLO인가?

  • 병목 구간 가시화: 단순 5xx 비율이 아니라 “외부 API 지연”처럼 구간별 시간이 타임라인으로 보입니다.
  • 근본원인으로 점프: 경보 → 해당 트레이스 → 관련 로그/스팬으로 3클릭 분석.
  • 엔드-투-엔드 경험: 게이트웨이~DB까지 하나의 요청 단위로 SLI를 계산 가능.

2) RED/USE 빠른 정리(트레이스와의 연결)

  • RED: 초당 처리량(Rate), 실패 비율(Errors), 지연(Duration). → HTTP 서버 스팬에서 추출.
  • USE: 사용률(Utilization), 포화도(Saturation), 오류(Errors). → 리소스(스레드풀/커넥션풀)에 매핑.

RED는 트레이스의 http.server.duration, 상태코드 태그, 스팬 카운트를 이용해 산출합니다. USE는 보통 메트릭(스레드 풀 큐 길이 등)을 병행합니다.


3) 계측 표준화(OTel 스팬 속성 확인)

# 서버 측 스팬(예시) 주요 속성
http.request.method=GET
http.route=/orders/{id}
http.response.status_code=200
service.name=order-api
duration_ms=123 (스팬 자체의 지속시간)

# DB/외부API 스팬 (병목 분석용)
db.system=postgresql
db.operation=SELECT
net.peer.name=payments.example.com

자주 쓰는 라벨은 service.name, http.route, status_code, environment 정도로 제한해 카디널리티 폭발을 막습니다.


4) 샘플링 전략과 SLO 정확도

  • 헤드 샘플링(고정비율): 단순하지만 저QPS 서비스는 표본이 부족할 수 있습니다.
  • 테일 샘플링(Collector): 에러/지연 케이스를 선별 저장 → SLO 계산에 유리. 단, 정상 샘플이 줄어 Rate지표 기반으로 보완하는 것이 좋습니다.

실무 팁: 처리량(Rate)은 애플리케이션 메트릭(예: Micrometer HTTP 요청 카운터)로, 오류/지연은 트레이스로 결합하는 혼합 방식을 권장합니다.


5) Prometheus 녹임: RED 기록식(Recording Rules)

Micrometer(HTTP 서버) 지표와 트레이스 상관(예: exemplar) 조합을 예시로 듭니다.

# /etc/prometheus/rules/red.yml
groups:
- name: red.http
  rules:
  - record: svc:http:rate_rps
    expr: sum by(service, route) (rate(http_server_requests_seconds_count{uri!~"/actuator.*"}[5m]))

  - record: svc:http:error_ratio
    expr: sum by(service, route) (rate(http_server_requests_seconds_count{status=~"5..|4.."}[5m]))
          /
          sum by(service, route) (rate(http_server_requests_seconds_count[5m]))

  - record: svc:http:latency_p95
    expr: histogram_quantile(0.95, sum by (service, route, le)
          (rate(http_server_requests_seconds_bucket{uri!~"/actuator.*"}[5m])))

이 규칙으로 RED 기초 SLI를 생성하고, 트레이스는 Grafana에서 해당 시점의 exemplar나 traceID 필드로 연결합니다.


6) 트레이스에서 직접 집계(Tempo → Grafana → 루ール)

Grafana Tempo는 트레이스에 대한 검색/집계는 지원하지만 최종 알림은 Prometheus 경보가 안정적입니다. 따라서 “메트릭은 Prometheus, 드릴다운은 Tempo” 구조를 추천합니다.


7) SLO 정의와 알림(에러버짓 기반)

예시 SLO: “order-api의 라우트 /orders/{id}에 대해 월간 가용성 99.9%, p95 지연 ≤ 800ms”.

7-1) 단기 추세 경보(빠른 소진)

# /etc/prometheus/rules/slo-alerts.yml
groups:
- name: slo
  rules:
  - alert: ApiErrorBudgetFastBurn
    expr: svc:http:error_ratio{service="order-api"} > 0.05
    for: 10m
    labels:
      severity: critical
    annotations:
      summary: "order-api 오류율 급증(5m>5%)"
      description: "RED: Errors 5% 초과. 트레이스로 드릴다운하세요."

7-2) 지연 경보

- alert: ApiLatencyHighP95
  expr: svc:http:latency_p95{service="order-api"} > 0.8
  for: 15m
  labels:
    severity: warning
  annotations:
    summary: "order-api p95 지연 > 800ms"
    description: "해당 윈도우의 대표 트레이스로 병목 구간 확인"

Alertmanager에서는 근본 경보(가용성)가 뜨면 파생 경보(지연)inhibit해 소음을 줄이세요.


8) Grafana 코릴레이션(로그/트레이스로 점프)

  1. Prometheus 패널: p95 선 위 특정 시점을 클릭 → Exemplar 트레이스로 이동(설정 시).
  2. 또는 데이터 링크에 Tempo 쿼리를 연결: trace_id={{__exemplar.trace_id}} 또는 로그 필드 이용.
  3. Tempo 트레이스 화면 → “Logs for this trace” 버튼으로 Loki 이동(로그에 trace_id 포함 필요).

9) 운영 템플릿(복붙)

9-1) OTel Collector: 테일 샘플링

# /etc/otelcol/config.yaml (추가)
processors:
  tailsampling:
    decision_wait: 5s
    policies:
      - name: error
        type: status_code
        status_code: {status_codes: [ERROR]}
      - name: latency
        type: latency
        latency: {threshold_ms: 800}

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, tailsampling, batch]
      exporters: [otlp]

9-2) Logback: trace_id 파이프

# logback-spring.xml (요지)
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
  <providers>
    <timestamp/><mdc/> ...  
  </providers>
</encoder>

9-3) Grafana 패널 링크

# 패널 → Field → Data links
Title: "View trace"
URL:   /explore?schemaVersion=1&panes=%7B%22traces%22%3A...%7D&var-trace_id=${__data.fields.trace_id}

10) 함정과 해결

  • 지표와 트레이스 불일치: 샘플링으로 트레이스 수가 적어 보일 수 있음 → Rate은 지표 기준, Errors/Duration은 트레이스로 드릴다운.
  • 라벨 폭발: http.route는 패턴화된 경로만 라벨링하고, http.target처럼 동적 값은 로그로만 남기기.
  • 프록시 헤더 손실: API 게이트웨이/리버스프록시에서 traceparent 헤더가 그대로 전달되는지 검증.
  • 경보 소음: for:로 일시 스파이크 필터, Alertmanager inhibit/grouping 설계.

11) 체크리스트(복붙용)

  • Micrometer HTTP 지표 기록식 생성(RED: rate, error_ratio, latency_p95)
  • OTel 테일 샘플링(에러/지연 우선 저장)
  • 로그에 trace_id 포함 → Loki 파싱
  • Grafana 패널에 Tempo 링크/Exemplar 연결
  • Alertmanager: 근본 경보 우선, 파생 경보 억제
  • 라벨 가이드 준수(카디널리티 관리)

이렇게 구성하면 SLO 위반 징후를 빠르게 포착하고, 즉시 트레이스로 근본 원인을 확인한 뒤 로그로 세부 메시지를 추적할 수 있습니다. 다음 글에서는 트레이스 기반 용량 계획(스레드/커넥션 풀 USE)자동 스케일링 지표를 설계해 운영 자동화를 한 단계 더 올려보겠습니다.