OTel spanmetrics 커넥터: 코드 수정 없이 트레이스에서 RED 메트릭을 자동 생성하고 Grafana에 연결하는 방법
마이크로서비스 환경에서 서비스 상태를 빠르게 파악하려면 세 가지 질문에 즉시 답할 수 있어야 합니다. "지금 요청이 얼마나 들어오고 있는가?", "오류는 얼마나 발생하고 있는가?", "응답은 얼마나 걸리는가?" 이 세 가지가 바로 **RED 메트릭(Rate, Errors, Duration)**입니다. 문제는 이를 위해 애플리케이션 코드에 메트릭 계측 코드를 따로 심어야 한다는 인식인데, OpenTelemetry Collector의 spanmetrics 커넥터를 사용하면 그럴 필요가 전혀 없습니다.
spanmetrics 커넥터는 트레이스 스팬 데이터를 분석해 Prometheus 호환 RED 메트릭을 자동으로 생성합니다. 애플리케이션 코드를 단 한 줄도 수정하지 않고, Collector 설정만으로 전체 파이프라인을 구성할 수 있습니다. 아직 OTel을 도입하지 않은 상태라면, 이 글에서 파이프라인 구조를 먼저 파악해두면 도입 즉시 적용할 수 있습니다.
이 글을 읽고 나면 spanmetrics 커넥터의 동작 원리를 이해하고, 실제 Collector YAML 설정을 작성해 Grafana에서 Rate·Errors·Duration 패널을 구성하는 전체 파이프라인을 직접 구성할 수 있게 됩니다. 커넥터 원리부터 YAML 전체 구성, PromQL 쿼리, Alloy 통합, 운영 주의사항까지 단계별로 살펴봅니다.
이 글의 전제 조건: OTel Collector를 운영 중이며 기본 파이프라인 구성(OTLP Receiver, Batch Processor, Prometheus 스크레이핑)에 익숙한 백엔드·인프라 개발자를 기준으로 합니다.
핵심 개념
RED 메트릭이란 무엇인가
RED 메트릭은 서비스 헬스를 모니터링하기 위한 세 가지 골든 시그널입니다.
| 메트릭 | 의미 | spanmetrics가 생성하는 지표 |
|---|---|---|
| Rate | 초당 처리된 요청 수 | calls_total 카운터 |
| Errors | 오류 상태의 요청 비율 | calls_total{status_code="STATUS_CODE_ERROR"} |
| Duration | 요청 처리 시간 분포 | duration_bucket (히스토그램) |
트레이스의 각 스팬은 서비스명, 오퍼레이션명, 상태 코드, 스팬 종류 등의 정보를 이미 담고 있습니다. spanmetrics 커넥터는 이 정보를 집계해 메트릭으로 변환하므로 별도 계측이 불필요합니다.
메트릭명 앞에 namespace prefix가 붙을 수 있습니다.
namespace설정을 지정하면calls_total이traces_spanmetrics_calls_total처럼 변경됩니다. PromQL 쿼리를 복사하기 전에:8889/metrics엔드포인트에서 실제 메트릭명을 먼저 확인하는 것을 권장합니다.
Connector 컴포넌트의 역할
spanmetrics는 Processor가 아닌 Connector 컴포넌트입니다. 이 차이가 아키텍처 설계에서 중요합니다.
Connector란: OTel Collector 내부에서 두 개의 독립적인 파이프라인을 연결하는 컴포넌트입니다. 트레이스 파이프라인에서는 Exporter 역할을, 메트릭 파이프라인에서는 Receiver 역할을 동시에 수행합니다. Processor는 단일 파이프라인 내에서만 데이터를 변환하지만, Connector는 파이프라인 경계를 넘어 데이터를 전달할 수 있습니다.
파이프라인 데이터 흐름은 다음과 같습니다.
OTLP Receiver
↓
Batch Processor
↓
spanmetrics Connector ──→ (메트릭 파이프라인) → Prometheus Exporter → Grafana
↓
Tempo / Jaeger Exporter (트레이스 파이프라인 계속)트레이스 데이터는 Tempo나 Jaeger로 그대로 전달되면서, 동시에 spanmetrics 커넥터가 메트릭을 추출해 Prometheus로 내보냅니다. 하나의 스팬 흐름에서 두 가지 신호를 동시에 생산하는 구조입니다.
2024~2026년 주요 변화: spanmetricsprocessor → spanmetricsconnector
과거에는 spanmetricsprocessor라는 이름의 Processor 컴포넌트로 동일한 기능을 제공했습니다. 현재는 spanmetricsconnector로 완전히 전환되었으며, 공식 Contrib 배포판(v0.147.0+)에 포함되어 있습니다. 기존 Processor 방식 설정을 사용하고 있다면 이 글의 구성 예시를 참고해 마이그레이션을 진행해보시면 좋습니다.
이 개념을 바탕으로 실제 Collector 설정 파일을 구성해보겠습니다.
실전 적용
예시 1: 기본 파이프라인 YAML 전체 구성
가장 기본적인 spanmetrics 파이프라인 설정입니다. order-service에서 들어오는 트레이스를 받아 RED 메트릭을 생성하고 Prometheus로 내보내는 구성입니다. (v0.147.0에서 검증된 설정)
connectors:
spanmetrics:
histogram:
explicit:
buckets: [5ms, 10ms, 25ms, 50ms, 100ms, 250ms, 500ms, 1s, 2s, 5s]
dimensions:
- name: http.method
- name: http.route
- name: http.status_code
- name: service.version
exemplars:
enabled: true
metrics_flush_interval: 15s
metrics_expiration: 5m
aggregation_temporality: AGGREGATION_TEMPORALITY_CUMULATIVE
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
otlp/tempo:
endpoint: tempo:4317 # Jaeger 사용 시 jaeger exporter로 교체
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [spanmetrics, otlp/tempo] # Connector가 Exporter로 등장
metrics:
receivers: [spanmetrics] # Connector가 Receiver로 등장
processors: [batch]
exporters: [prometheus]| 설정 항목 | 역할 |
|---|---|
histogram.explicit.buckets |
P50/P95/P99 계산을 위한 지연 시간 버킷 범위 지정 |
dimensions |
메트릭에 추가할 스팬 속성 (레이블로 변환됨) |
exemplars.enabled: true |
메트릭 데이터 포인트에 trace ID를 삽입해 Grafana에서 드릴다운 가능하게 함 |
metrics_flush_interval: 15s |
집계된 메트릭을 내보내는 주기 (Prometheus scrape_interval 이하로 설정 권장) |
metrics_expiration: 5m |
5분간 새 스팬이 없으면 해당 시계열을 만료시켜 메모리 절약 |
aggregation_temporality |
Prometheus 스크레이핑 호환을 위해 Cumulative 방식 사용 |
metrics_flush_interval과scrape_interval의 관계: Prometheus의scrape_interval(기본값 15s)보다metrics_flush_interval이 길면, 스크레이핑 시점에 메트릭이 아직 반영되지 않아 빈 결과가 나올 수 있습니다.metrics_flush_interval은 Prometheusscrape_interval보다 짧거나 같게 설정하는 것을 권장합니다.
Exemplar란: Prometheus 메트릭 데이터 포인트에
trace_id같은 메타데이터를 첨부하는 기능입니다. Grafana에서 지연 시간 스파이크 구간을 클릭하면 해당 시점의 실제 트레이스로 바로 이동할 수 있어, "메트릭 이상 감지 → 트레이스 원인 분석" 워크플로가 원클릭으로 연결됩니다.
Exemplar 수집을 위한 추가 설정:
exemplars.enabled: true만으로는 충분하지 않습니다. Prometheus 실행 시--enable-feature=exemplar-storage플래그가 필요하고,scrape_configs의scrape_protocols에OpenMetricsText1.0.0을 추가해 OpenMetrics 형식으로 수집해야 Exemplar가 실제로 저장됩니다.
예시 2: Grafana 대시보드 PromQL 쿼리
Collector가 Prometheus 메트릭을 노출하면 Grafana 대시보드에서 다음 쿼리로 RED 패널을 구성할 수 있습니다.
# Rate — order-service의 초당 요청 수 (서버 스팬만 집계)
rate(calls_total{
service_name="order-service",
span_kind="SPAN_KIND_SERVER"
}[1m])
# Errors — order-service의 전체 요청 대비 오류 비율 (%)
rate(calls_total{service_name="order-service",status_code="STATUS_CODE_ERROR"}[1m])
/
rate(calls_total{service_name="order-service"}[1m])
* 100
# Duration — P99 지연 시간
histogram_quantile(
0.99,
rate(duration_bucket{service_name="order-service"}[5m])
)
# P50 지연 시간 (비교용)
histogram_quantile(
0.50,
rate(duration_bucket{service_name="order-service"}[5m])
)
histogram_quantile을 사용하려면duration_bucket메트릭이 존재해야 합니다. 버킷 설정이 서비스의 실제 응답 시간 범위를 커버하지 않으면 P99 값이+Inf로 나타날 수 있으므로, 서비스 특성에 맞게 버킷 범위를 튜닝하는 것을 권장합니다.
예시 3: Grafana Alloy를 활용한 통합 구성
Grafana Alloy는 Grafana Agent의 후속 제품으로, otelcol.connector.spanmetrics 컴포넌트를 내장하고 있습니다. 별도로 OTel Collector를 관리하지 않고 Alloy 단일 바이너리로 수집·변환·전송을 처리할 수 있습니다.
// Alloy 구성 파일 (config.alloy)
otelcol.receiver.otlp "default" {
grpc { endpoint = "0.0.0.0:4317" }
http { endpoint = "0.0.0.0:4318" }
output {
traces = [otelcol.processor.batch.default.input]
}
}
otelcol.processor.batch "default" {
output {
traces = [otelcol.connector.spanmetrics.default.input]
}
}
otelcol.connector.spanmetrics "default" {
histogram {
explicit {
buckets = ["5ms", "10ms", "25ms", "50ms", "100ms", "250ms", "500ms", "1s", "2s", "5s"]
}
}
// YAML 예시와 동일한 4개 dimension
dimensions { name = "http.method" }
dimensions { name = "http.route" }
dimensions { name = "http.status_code" }
dimensions { name = "service.version" }
exemplars { enabled = true }
output {
metrics = [otelcol.exporter.prometheus.default.input]
traces = [otelcol.exporter.otlp.tempo.input]
}
}
otelcol.exporter.prometheus "default" {
forward_to = [prometheus.remote_write.mimir.receiver]
}
otelcol.exporter.otlp "tempo" {
client {
endpoint = "tempo:4317"
tls { insecure = true }
}
}Alloy 방식은 마이크로서비스 환경에서 서비스별 SLO 대시보드를 자동 생성하는 패턴에 특히 잘 어울립니다. Grafana Cloud 사용자라면 Tempo Metrics Generator 옵션도 고려해볼 수 있는데, 두 방식의 차이는 아래 표를 참고해주세요.
| 방식 | 메트릭 생성 위치 | 적합한 환경 |
|---|---|---|
| spanmetrics 커넥터 | Collector / Alloy 레벨 | 기존 OTel 파이프라인 통합, 자체 호스팅 |
| Tempo Metrics Generator | Tempo 서버 내부 | Grafana Cloud, 간편 설정 선호 |
장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 이중 계측 제거 | 애플리케이션 코드에 메트릭 계측 없이 트레이스에서 자동으로 RED 메트릭 생성 |
| 데이터 일관성 | 메트릭과 트레이스가 동일한 스팬에서 파생되어 수치 불일치 없음 |
| Exemplar 연동 | Grafana에서 지연 스파이크 지점 클릭 → Tempo 실제 트레이스로 원클릭 드릴다운 |
| 유연한 차원 설정 | HTTP 속성, 커스텀 태그를 dimension으로 추가해 세분화된 메트릭 생성 가능 |
| 표준 Prometheus 호환 | 기존 Grafana / AlertManager 생태계와 즉시 연동 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 카디널리티 폭발 | dimension 속성값 조합 수만큼 시계열 수 급증. 조합이 10,000개를 초과하면 OOM 위험 | user_id, request_id 같은 고유값은 dimension에서 제외 |
| 메모리 사용량 증가 | 집계 상태를 메모리에 유지 | aggregation_cardinality_limit, metrics_expiration 설정으로 완화 |
| Collector SPOF | spanmetrics 커넥터 Collector가 단일 장애점이 될 수 있음 | HA 구성 또는 Gateway 패턴 적용 |
| 재시작 시 데이터 유실 | Cumulative 집계 중 Collector 재시작 시 일부 데이터 소실 | Delta temporality 고려 (단, Prometheus 호환성 확인 필요) |
| 버킷 사전 정의 필요 | Explicit Histogram은 버킷 범위를 미리 지정해야 함 | Exponential Histogram으로 전환하거나 서비스 특성에 맞게 버킷 튜닝 |
카디널리티(Cardinality)란: 시계열 데이터베이스에서 고유한 레이블 조합의 수를 의미합니다. 예를 들어
user_id를 dimension으로 추가하면 사용자 수만큼 시계열이 생성되어 Prometheus와 Collector 모두 메모리가 급증할 수 있습니다.
Exponential Histogram: 버킷 경계를 미리 지정하지 않고 동적으로 분포를 표현하는 히스토그램 방식입니다. 서비스 응답 시간 범위가 불확실하거나 자주 변하는 경우 Explicit 방식보다 적합합니다.
실무에서 가장 흔한 실수
user_id,trace_id,request_id를 dimension에 추가하는 것 — 카디널리티 폭발로 Collector와 Prometheus 모두 OOM이 발생할 수 있습니다. dimension은 반드시 제한된 값의 집합(예: HTTP 메서드, 상태 코드, 라우트)만 지정하는 것을 권장합니다.service.pipelines에서 Connector의 이중 등록을 누락하는 것 — spanmetrics는traces파이프라인의exporters와metrics파이프라인의receivers양쪽에 모두 명시해야 합니다. 한쪽만 등록하면 Collector가 시작 시 오류를 반환합니다.- Prometheus에서 Exemplar 수집 설정을 완전히 활성화하지 않는 것 —
exemplars.enabled: true로 설정하더라도, Prometheus 실행 시--enable-feature=exemplar-storage플래그와scrape_configs의scrape_protocols에OpenMetricsText1.0.0추가가 모두 필요합니다. 두 설정 중 하나라도 빠지면 Grafana의 트레이스 드릴다운이 동작하지 않습니다.
마치며
spanmetrics 커넥터는 이미 수집 중인 트레이스를 그대로 활용해 애플리케이션 코드 변경 없이 RED 메트릭과 Grafana 대시보드를 구성할 수 있는 가장 실용적인 방법입니다.
지금 바로 시작해볼 수 있는 3단계입니다.
- OTel Collector Contrib 이미지를 확인해보세요.
otel/opentelemetry-collector-contrib:0.147.0이미지는 spanmetrics 커넥터를 포함하고 있습니다. 이미 Contrib 이미지를 사용 중이라면 바로 2단계로 넘어가셔도 됩니다. - 이 글의 기본 YAML 구성을 복사해 Collector에 적용해보세요.
connectors,service.pipelines섹션을 추가하고 Collector를 재시작하면:8889/metrics엔드포인트에서calls_total과duration_bucket메트릭이 나타나는 것을 확인하실 수 있습니다. - Grafana에 Prometheus 데이터 소스를 추가하고 PromQL 쿼리 세 개로 RED 패널을 만들어보세요. Rate, Errors, Duration 패널을 하나의 대시보드 행에 나란히 배치하면 서비스 헬스를 한눈에 파악하는 기본 SLO 대시보드가 완성됩니다.
다음 글: 이 글에서 구성한 RED 메트릭 파이프라인 위에 Grafana Tempo의 Service Graph를 추가하면 마이크로서비스 간 의존성 지도와 호출별 RED 메트릭을 하나의 화면에서 동시에 파악할 수 있게 됩니다. 다음 글에서 그 연동 방법을 다룰 예정입니다.
참고 자료
- Span Metrics Connector 공식 README | opentelemetry-collector-contrib
- otelcol.connector.spanmetrics | Grafana Alloy 공식 문서
- Metrics from traces | Grafana Tempo 공식 문서
- Use Alloy to generate span metrics from spans | Grafana Tempo 공식 문서
- Convert OpenTelemetry Traces to Metrics with SpanMetrics | Last9 블로그
- Converting Traces to Metrics for Grafana Dashboards | nsalexamy GitHub Pages
- Span Metrics Connector | Splunk Observability Cloud 공식 문서
- How to Use the Span Metrics Connector to Generate RED Metrics | OneUptime 블로그
- How to Build a Grafana RED Metrics Dashboard from OpenTelemetry Span Metrics | OneUptime 블로그
- OpenTelemetry Collector Data Flow Dashboard | Grafana Dashboards