OpenTelemetry + Grafana Tempo로 분산 트레이싱 구축: Spring Boot 연동, OTel Collector, 지표·로그 연계
로그와 메트릭만으로는 요청이 어디서 지연되는지를 한 눈에 보기 어렵습니다. 분산 트레이싱을 붙이면 요청 한 건이 Gateway → API → Service → DB/외부API로 흘러가는 여정을 스팬(Span) 단위로 추적하고, 문제 구간(느린 쿼리, 외부 호출, 스레드 대기)을 타임라인에서 바로 찾아낼 수 있습니다. 이 글은 Ubuntu 24.04 기준으로 Grafana Tempo(트레이싱 백엔드), OpenTelemetry Collector(게이트웨이), Spring Boot 애플리케이션 연동까지 단계별로 구성합니다. 마지막에 Prometheus/Loki와 연결해 지표·로그·트레이스를 한 화면에서 넘나드는 방법도 정리합니다.
1) 아키텍처 개요
[Spring Boot] --OTLP gRPC/HTTP--> [OpenTelemetry Collector] --(ingest/배치)--> [Tempo]
| ^
+--> [Prometheus exemplar] --------------|
+--> [Loki traceID 라벨] -----------------|
- Application: OpenTelemetry SDK/Auto-Instrumentation로 trace 생성(HTTP, JDBC, gRPC 등).
- OTel Collector: 여러 앱에서 오는 트레이스를 받아(OTLP) 배치, 샘플링, 라우팅.
- Tempo: 인덱스 없는(traceID 기반) 트레이스 저장/조회. 장점은 가볍고 운영 간단.
- Grafana: Explore → Traces(Tempo), Metrics(Prometheus), Logs(Loki) 탭을 연결(“코릴레이션”).
2) Tempo 단일 인스턴스 설치
# 사용자/디렉터리
sudo useradd --system --no-create-home --shell /usr/sbin/nologin tempo
sudo mkdir -p /etc/tempo /var/lib/tempo
sudo chown -R tempo:tempo /etc/tempo /var/lib/tempo
# /etc/tempo/tempo.yml (최소 예시)
server:
http_listen_port: 3200
grpc_listen_port: 3201
distributor:
receivers:
otlp:
protocols:
http:
grpc:
storage:
trace:
backend: local
local:
path: /var/lib/tempo/traces
compactor:
compaction:
block_retention: 168h # 7일 보관 예시
querier:
frontend_worker:
frontend_address: 127.0.0.1:9095
query_frontend:
max_outstanding_per_tenant: 200
metrics_generator:
registry:
external_labels:
cluster: dc1
# systemd
sudo tee /etc/systemd/system/tempo.service >/dev/null <<'S'
[Unit]
Description=Grafana Tempo
After=network-online.target
[Service]
User=tempo
ExecStart=/usr/local/bin/tempo -config.file=/etc/tempo/tempo.yml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
S
sudo systemctl daemon-reload
sudo systemctl enable --now tempo
systemctl status tempo --no-pager
포트: 수집은 3201 gRPC / 3200 HTTP, 조회는 Grafana 데이터소스에서 http://127.0.0.1:3200를 사용합니다.
3) OpenTelemetry Collector(게이트웨이) 구성
# 사용자/디렉터리
sudo useradd --system --no-create-home --shell /usr/sbin/nologin otel
sudo mkdir -p /etc/otelcol /var/lib/otelcol
sudo chown -R otel:otel /etc/otelcol /var/lib/otelcol
# /etc/otelcol/config.yaml
receivers:
otlp:
protocols:
http:
grpc:
processors:
batch:
timeout: 5s
send_batch_size: 8192
memory_limiter:
check_interval: 5s
limit_percentage: 60
spike_limit_percentage: 20
attributes:
actions:
- key: service.namespace
value: "shop"
action: upsert
exporters:
otlp:
endpoint: 127.0.0.1:3201
tls:
insecure: true
logging:
loglevel: warn
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch, attributes]
exporters: [otlp, logging]
# systemd
sudo tee /etc/systemd/system/otelcol.service >/dev/null <<'S'
[Unit]
Description=OpenTelemetry Collector
After=network-online.target
[Service]
User=otel
ExecStart=/usr/local/bin/otelcol --config=/etc/otelcol/config.yaml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
S
sudo systemctl daemon-reload
sudo systemctl enable --now otelcol
Collector는 수집 포인트(4317/4318)를 열고, Tempo로 전달합니다.
향후 샘플링(tail_sampling)이나 멀티 라우팅(Tempo+Jaeger 동시)도 여기서 처리합니다.
4) Spring Boot 애플리케이션 연동
4-1) Auto-Instrumentation(Java Agent)
# build.gradle
dependencies {
runtimeOnly("io.opentelemetry.javaagent:opentelemetry-javaagent:2.7.0") // 버전은 환경에 맞게
}
# 실행 옵션 (systemd 서비스 등)
JAVA_TOOL_OPTIONS="-javaagent:/path/opentelemetry-javaagent.jar"
OTEL_SERVICE_NAME="order-api"
OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:4317"
OTEL_METRICS_EXPORTER=none
OTEL_LOGS_EXPORTER=none
장점: 코드 수정 없이 HTTP 클라이언트/서버, JDBC, gRPC 등 주요 라이브러리가 자동 계측됩니다. 세밀한 제어가 필요하면 SDK 방식으로 전환합니다.
4-2) SDK 방식(선택)
implementation("io.opentelemetry:opentelemetry-sdk:1.44.1")
implementation("io.opentelemetry:opentelemetry-exporter-otlp:1.44.1")
@Bean
SdkTracerProvider tracerProvider() {
return SdkTracerProvider.builder()
.setResource(Resource.getDefault().merge(
Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "order-api"))))
.build();
}
@Bean
OpenTelemetrySdk openTelemetry(SdkTracerProvider provider) {
OtlpGrpcSpanExporter exporter = OtlpGrpcSpanExporter.builder()
.setEndpoint("http://127.0.0.1:4317")
.build();
return OpenTelemetrySdk.builder()
.setTracerProvider(provider)
.build();
}
5) TraceID를 로그/지표와 연결(코릴레이션)
5-1) 로그에 trace_id 필드 추가
# logback-spring.xml
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<mdc/>
<pattern>
<pattern>{"level":"%level","logger":"%logger","msg":"%msg"}</pattern>
</pattern>
</providers>
</encoder>
</appender>
<root level="INFO"><appender-ref ref="CONSOLE"/></root>
</configuration>
OpenTelemetry Java 에이전트는 MDC에 trace_id, span_id를 넣습니다.
Loki 스크레이프에서 해당 필드를 라벨 또는 파싱 필드로 보관하면 Grafana에서 Logs ↔ Traces를 상호 이동할 수 있습니다.
5-2) Prometheus exemplar(선택)
HTTP 요청 히스토그램에 exemplar로 traceID를 매달면, Grafana 그래프의 특정 지점 → 해당 트레이스로 점프할 수 있습니다. Spring Boot Micrometer + OTel 브릿지 조합을 고려하세요.
6) Grafana 데이터소스 연결
- Connections → Data sources → Tempo 선택
- URL:
http://127.0.0.1:3200→ Save & Test - Explore → Traces에서 Service name =
order-api조회
이미 Prometheus와 Loki가 연결되어 있다면, Explore 우측 패널의 “Logs for this trace”, “Metrics for this trace” 링크가 활성화됩니다.
7) 샘플링 전략: 비용·가시성 균형
- 고정 비율(예: 10%): 간단하지만 낮은 QPS 서비스에서는 표본 수가 부족할 수 있음.
- Tail Sampling(Collector): 에러/느린 요청만 사후 선별 저장 → 비용 절감 + 유의미 샘플 확보.
# /etc/otelcol/config.yaml (발췌)
processors:
tailsampling:
decision_wait: 5s
policies:
- name: errors
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]
8) 성능·운영 팁
- Collector 앞단: 앱 수가 많아지면 Gateway 1대 → N대 수평 확장, 로드밸런서로 OTLP 트래픽 분산.
- Tempo 스토리지: 단일/로컬로 시작하고, 규모 커지면 S3 호환 오브젝트 스토리지 도입 검토.
- 라벨 가이드:
service.name,service.namespace,environment정도만. 과도한 속성은 비용↑. - 보안: OTLP gRPC/HTTP 포트는 내부망으로 제한(UFW/보안그룹). 멀티 테넌트면 텐언트 헤더/리밋 적용.
9) 문제 해결 빠른 분기
- 트레이스가 안 보임 →
otelcol로그에서 OTLP → Tempo exporter 에러 확인, Tempo/status점검. - 스팬이 끊김 → 게이트웨이/서비스 간
traceparent헤더 전달 누락 검토(프록시/게이트웨이에서 헤더 보존). - 부하 급증 →
batch파라미터 조절, 샘플링 도입, Tempo retention/압축 설정 점검. - 로그/지표와 점프 실패 → Loki 파서에
trace_id필드가 들어오는지, Prometheus exemplar 설정 여부 확인.
10) 체크리스트(복붙용)
- Tempo/OTel Collector 설치 및 서비스 기동
- Spring Boot: OTel Java Agent 적용,
OTEL_EXPORTER_OTLP_ENDPOINT설정 - Grafana Tempo 데이터소스 연결(Explore 동작 확인)
- Tail Sampling: 에러/지연 정책 추가
- 로그 MDC에
trace_id포함 → Loki 파싱 - Prometheus exemplar(선택)로 트레이스-지표 점프
여기까지 적용하면, “에러율이 올랐다 → 느린 구간 스팬 열람 → 관련 로그 열람”까지 3클릭에 끝납니다. 다음 단계로는 트레이스 기반 SLO(RED/USE)를 정의하고, 경보와 런북을 붙여 탐지→진단→조치 시간을 더 줄여보세요.