OpenTelemetry, Tempo, Grafana를 활용한 k8s 트레이싱 통합 구성 가이드
분산 시스템 환경이 점점 더 복잡해짐에 따라, 서비스 간의 상호작용을 시각화하고 성능 병목을 분석하는 데 있어 분산 추적(Distributed Tracing)의 중요성이 커지고 있습니다. 이러한 요구를 충족시키기 위해, 오픈소스 기반의 OpenTelemetry, Grafana Tempo, Grafana 대시보드는 통합된 트레이싱 환경을 제공하며, 클라우드 네이티브 환경에서도 유연하고 강력한 추적 기능을 지원합니다.
본 가이드에서는 Kubernetes 기반 환경에서 OpenTelemetry Collector를 활용하여 트레이싱 데이터를 수집하고, Tempo를 통해 저장 및 조회하며, Grafana를 통해 시각화하는 전체 과정을 단계별로 설명합니다. 특히 Grafana에서 제공하는 TNS Demo Application을 활용하여 실시간 트레이싱 데이터를 생성하고 확인하는 과정을 실습 중심으로 다룹니다.
목차
1. 환경 구성
2. Grafana Tempo 구성
3. OpenTelemetry 구성
4. Grafana Demo Application 배포
5. Grafana 대시보드 Tracing 데이터 확인
6. 결론
1. 환경구성
- kubernetes : v1.32.2
- Grafana tns Demo
- Grafana Tempo : 2.7
- Grafana : v12.0
- OpenTelemetry Collector : v0.126.0
2. Grafana Tempo 구성
Grafana Tempo는 분산 시스템에서 발생하는 트레이스를 수집, 저장, 조회할 수 있도록 해주는 트레이싱 전용 스토리지 백엔드입니다.
OpenTelemetry를 배포하기 전 Grafana Tempo의 KubeDns URL이 필요하기 때문에 먼저 Tempo를 배포합니다.
# tempo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tempo
labels:
app: tempo
spec:
selector:
matchLabels:
app: tempo
template:
metadata:
labels:
app: tempo
spec:
containers:
- name: tempo
image: grafana/tempo
ports:
- containerPort: 3100
name: tempo-web
- containerPort: 4317 # OpenTelemetry에서 데이터를 받을 수 있도록 gRPC 포트를 허용합니다.
name: grpc-receive
args:
- --config.file=/etc/tempo/config.yml
volumeMounts:
- name: tempo-config
mountPath: /etc/tempo/config.yml
subPath: config.yml
volumes:
- name: tempo-config
configMap:
name: tempo-conf
---
apiVersion: v1
kind: ConfigMap
metadata:
name: tempo-conf
data:
config.yml: |
server:
http_listen_port: 3100
distributor:
receivers:
otlp: # OpenTelemetery의 트레이싱 데이터를 전달받습니다.
protocols:
grpc: # gRPC 프로토콜로 트레이싱 데이터를 받습니다.
endpoint: 0.0.0.0:4317
storage:
trace:
backend: local # Local Storage에 트레이싱 데이터를 저장합니다.
wal: # wal은 임시 트레이싱 데이터를 저장하는 역할을 합니다.
path: /tmp/tempo/wal
local: # wal에서 임시 트레이싱 데이터를 저장 후 blocks 디렉토리로 트레이싱 데이터를 저장합니다.
path: /tmp/tempo/blocks
---
apiVersion: v1
kind: Service
metadata:
name: tempo-svc
labels:
app: tempo
spec:
selector:
app: tempo
ports:
- name: tempo-web
port: 3100
targetPort: 3100
- name: grpc-tempo-otlp
port: 4317
protocol: TCP
targetPort: 4317
type: NodePort # tempo-web을 접속할 수 있도록 NodePort로 배포합니다.
Grafana Tempo를 배포합니다.


Grafana Tempo가 배포되었는지 확인하기 위해 NodePort로 접속합니다.
Tracing Data 목록 URL
http://[Master Node IP]:[NodePort]/api/search

3. OpenTelemetry 배포
OpenTelemetry는 애플리케이션의 로그, 메트릭, 트레이스를 수집하고 전송하는 표준화된 오픈소스 관측 도구 모음입니다.
OpenTelemetry를 배포하여 Tracing 데이터를 수집할 수 있도록 구성합니다.
# otel.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: otel-collector-conf
labels:
app: opentelemetry
data:
otel-collector-config.yaml: |
processors:
batch:
timeout: 5s
send_batch_size: 1024
receivers:
jaeger: # Demo Application의 트레이싱 데이터를 받을 수 있도록 jaeger로 데이터를 전송받습니다.
protocols:
thrift_compact:
endpoint: 0.0.0.0:6831 # jaeger 전송 포트를 열어둡니다.
exporters:
otlp/tempo:
endpoint: "tempo-svc.apm.svc.cluster.local:4317" # Grafana Tempo gRCP URL
tls:
insecure: true
service:
pipelines:
traces:
receivers: [jaeger]
processors: [batch]
exporters: [otlp/tempo]
---
apiVersion: v1
kind: Service
metadata:
name: otel-collector
labels:
app: opentelemetry
spec:
selector:
app: opentelemetry
ports:
- name: jaeger
port: 6831
protocol: UDP
targetPort: 6831
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: otel-collector
labels:
app: opentelemetry
spec:
selector:
matchLabels:
app: opentelemetry
template:
metadata:
labels:
app: opentelemetry
spec:
containers:
- command:
- "/otelcol"
- "--config=/conf/otel-collector-config.yaml"
image: otel/opentelemetry-collector:latest
name: otel-collector
ports:
- containerPort: 6831
protocol: UDP
volumeMounts:
- name: otel-collector-config
mountPath: /conf/otel-collector-config.yaml
subPath: otel-collector-config.yaml
volumes:
- name: otel-collector-config
configMap:
name: otel-collector-conf
OpenTelemetry를 배포합니다.

배포 현황은 아래와 같습니다.

4. Grafana Demo Application 배포
Grafana tns Demo Application은 jaeger 용 Demo로 배포되어 있습니다. OpenTelemetry를 이용하여 Tracing 데이터를 전달받은 후 Grafana Tempo로 전달하여 트레이싱 데이터를 전달받습니다.
# app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 1
selector:
matchLabels:
name: app
template:
metadata:
labels:
name: app
spec:
containers:
- name: app
image: grafana/tns-app:latest
imagePullPolicy: IfNotPresent
args:
- -log.level=debug
- http://db
ports:
- name: http-metrics
containerPort: 80
env:
- name: JAEGER_AGENT_HOST # JAEGER Host 대신 OpenTelemetry Host를 작성합니다.
value: otel-collector.apm.svc.cluster.local
- name: JAEGER_SAMPLER_PARAM # 샘플링 비율을 1로 지정하여 모든 요청을 추적합니다.
value: "1"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: loadgen
spec:
replicas: 1
selector:
matchLabels:
name: loadgen
template:
metadata:
labels:
name: loadgen
spec:
containers:
- name: loadgen
image: grafana/tns-loadgen:latest
imagePullPolicy: IfNotPresent
args:
- -log.level=debug
- http://app
ports:
- name: http-metrics
containerPort: 80
env:
- name: JAEGER_AGENT_HOST # JAEGER Host 대신 OpenTelemetry Host를 작성합니다.
value: otel-collector.apm.svc.cluster.local
- name: JAEGER_SAMPLER_PARAM
value: "1"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: db
spec:
replicas: 1
selector:
matchLabels:
name: db
template:
metadata:
labels:
name: db
spec:
containers:
- name: db
image: grafana/tns-db:latest
imagePullPolicy: IfNotPresent
args:
- -log.level=debug
ports:
- name: http-metrics
containerPort: 80
env:
- name: JAEGER_AGENT_HOST # JAEGER Host 대신 OpenTelemetry Host를 작성합니다.
value: otel-collector.apm.svc.cluster.local
- name: JAEGER_SAMPLER_PARAM
value: "1"
---
kind: Service
apiVersion: v1
metadata:
name: app
spec:
type: NodePort
selector:
name: app
ports:
- protocol: TCP
nodePort: 30033
port: 80
targetPort: 80
---
kind: Service
apiVersion: v1
metadata:
name: db
spec:
selector:
name: db
ports:
- protocol: TCP
port: 80
targetPort: 80
---
kind: Service
apiVersion: v1
metadata:
name: loadgen
spec:
selector:
name: loadgen
ports:
- protocol: TCP
port: 80
targetPort: 80
Demo Application을 배포합니다.

Demo Application에 접속합니다.
http://[Cluster IP]:30033
Demo 페이지는 아래와 같습니다.

Grafana Tempo로 접속하게 되면 아래와 같이 Tracing Data를 확인할 수 있습니다.

5. Grafana 대시보드 Tracing 데이터 확인
grafana를 배포합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: grafana
name: grafana
spec:
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
spec:
containers:
- name: grafana
image: grafana/grafana:latest
ports:
- containerPort: 3000
name: http-grafana
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: grafana
spec:
ports:
- port: 3000
protocol: TCP
targetPort: http-grafana
selector:
app: grafana
type: NodePort # Grafana에 접속할 수 있도록 NodePort로 배포합니다.
Grafana 리소스를 추가합니다.

최종 배포 리소스는 아래와 같습니다.

Grafana NodePort로 접근합니다.
Grafana의 초기 ID와 비밀번호는 admin/admin입니다.
Grafana의 목록에서 Connections로 이동하여 Tempo를 추가합니다.

Tempo 추가 Connection에서 배포했었던 Grafana Tempo의 KubeDNS URL를 작성합니다.

seve & test를 클릭하여 연동을 끝마칩니다.

Explore로 이동하여 Tempo의 트레이싱 데이터를 확인합니다.

Query Type에서 트레이싱 데이터를 확인할 수 있습니다.

Service Name에서 서비스의 종류를 확인할 수 있습니다.

Span Name에서 Span의 종류를 확인할 수 있습니다.
Span Name은 Application 마다 다릅니다. NGINX의 경우 URI 경로로 지정되어있습니다.

Status는 OpenTelemetry로 보낸 작업의 상태를 뜻합니다.
Unset : 기본 상태입니다.
Ok : 해당 작업은 애플리케이션 개발자 또는 운영자에 의해 성공적으로 완료되었음이 검증되었습니다.
Error : 해당 작업에 오류가 있습니다.

Duration은 어떤 작업(Span)이 수행된 전체 시간을 의미합니다.
Duration을 통해 작업이 수행된 시간을 지정하여 검색할 수 있습니다.

Tags를 통하여 Span의 tag와 값을 이용하여 트레이싱 데이터를 검색할 수 있습니다.

TraceQL을 이용하여 Trace ID를 이용하여 데이터를 검색할 수 있습니다.

각각 Service name, Span name, Duration, Tags를 이용하여 서비스를 검색할 수 있습니다.

Service를 선택하여 선택한 서비스의 자세한 정보를 확인할 수 있습니다.

Grafana TNS Demo에서는 아래와 같은 Tracing Data를 확인할 수 있습니다.
Grafana Tns Demo에서는 Component, Method, Status Code, 접속 URI을 이용하여 Span데이터로 검색할 수 있으며 Demo에서 사용한 Client UUID와 PodName(host.name) 접속 IP와 메트릭 데이터 Exporter 및 Service 이름의 정보를 확인할 수 있습니다.

아래의 그래프를 통하여 Tracing Data의 작업 속도를 확인할 수 있습니다.

6. 결론
본 가이드를 통해 OpenTelemetry, Grafana Tempo, Grafana를 활용한 분산 추적 시스템을 Kubernetes 클러스터 내에 성공적으로 구성하고, 실시간으로 서비스 간의 트랜잭션 흐름과 성능 지표를 시각화할 수 있음을 확인하였습니다.
Tempo는 가볍고 확장성이 뛰어난 트레이싱 백엔드로서, OpenTelemetry와의 통합을 통해 다양한 서비스에서 발생하는 트레이싱 데이터를 효율적으로 수집하고 저장할 수 있습니다. Grafana의 대시보드는 이러한 트레이싱 정보를 직관적으로 탐색하고 분석할 수 있도록 지원함으로써, 운영자는 병목 현상이나 오류 지점을 신속히 파악하고 대응할 수 있습니다.
Kubernetes 환경에서의 Monitoring에 대해 더 알고싶으시다면 NGINX STORE Kubernetes 카테고리를 방문해주세요.
댓글을 달려면 로그인해야 합니다.