트레이스 기반 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 코릴레이션(로그/트레이스로 점프)
- Prometheus 패널: p95 선 위 특정 시점을 클릭 → Exemplar 트레이스로 이동(설정 시).
- 또는 데이터 링크에 Tempo 쿼리를 연결:
trace_id={{__exemplar.trace_id}}또는 로그 필드 이용. - 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)과 자동 스케일링 지표를 설계해 운영 자동화를 한 단계 더 올려보겠습니다.