목차
- [Kubernetes] OpenTelemetry K8s 분산 추적: 13년차 삽질 & 활용 사례
- OpenTelemetry, 분산 추적, 그리고 Observability: 개념 정리
- 실전 구현: K8s 환경에서 OpenTelemetry Collector 설정하기
- 1. OpenTelemetry Collector 배포 전략 선택
- 2. Helm으로 OpenTelemetry Collector 배포
- 3. 애플리케이션 계측 (Instrumentation)
- ⚠️ 삽질 & 트러블슈팅 경험
- 결과 확인 및 활용 사례
- 마무리하며: OpenTelemetry, 선택이 아닌 필수
[Kubernetes] OpenTelemetry K8s 분산 추적: 13년차 삽질 & 활용 사례
안녕하세요, 13년차의 서버실 주인장입니다. 오늘은 마이크로서비스 아키텍처(MSA)를 운영하시는 분들이라면 한 번쯤은 머리 싸매고 고민했을 주제, 바로 분산 추적(Distributed Tracing)에 대해 이야기해보려고 해요. 특히 Kubernetes(쿠버네티스) 환경에서 OpenTelemetry(오픈텔레메트리)를 활용해서 어떻게 이 복잡한 문제를 풀어갈 수 있었는지, 제가 직접 삽질했던 경험들을 솔직하게 공유해볼게요.
요즘 애플리케이션들은 웬만하면 다 마이크로서비스 형태로 쪼개져 있잖아요? 서비스 간 호출이 꼬리에 꼬리를 물고 이어지다 보니, '도대체 이 에러가 어디서부터 시작된 거야?' 하고 원인을 찾기 시작하면 정말 막막할 때가 많더라고요. 저도 처음엔 로그만 가지고 씨름했는데, 시간이 갈수록 이건 아니다 싶었어요. 그래서 자연스럽게 분산 추적에 관심을 가지게 되었고, 홈랩에서 이것저것 실험하다가 OpenTelemetry라는 보석 같은 녀석을 만나게 되었죠. 🎉
이 글을 통해 여러분도 저처럼 서비스 트러블슈팅 시간을 확 줄이고, 시스템 전체를 한눈에 파악하는 데 큰 도움을 받으실 수 있을 거예요. 자, 그럼 함께 시작해볼까요?
OpenTelemetry와 Kubernetes 환경에서 분산 추적이 어떻게 작동하는지 보여주는 전체 아키텍처 다이어그램입니다.
OpenTelemetry, 분산 추적, 그리고 Observability: 개념 정리
본격적인 설정에 들어가기 전에, 몇 가지 핵심 개념을 짚고 넘어가야겠죠? 제가 처음 이 분야를 접했을 때 가장 헷갈렸던 부분들이거든요.
- Observability (옵저버빌리티, 관측 가능성): 시스템 내부 상태를 외부에서 추론할 수 있는 능력이에요. 단순히 '이상이 있다/없다'를 넘어 '왜 이상이 발생했는지'까지 파악할 수 있는 거죠. 보통 Logs(로그), Metrics(메트릭), Traces(추적) 세 가지 기둥으로 구성돼요.
- Distributed Tracing (분산 추적): 마이크로서비스 아키텍처에서 사용자 요청이 여러 서비스를 거쳐 처리될 때, 그 전체 흐름을 따라가며 추적하는 기술입니다. 각 서비스 간 호출이 하나의 Trace(트레이스, 추적)로 묶이고, 그 안에서 각 작업 단위는 Span(스팬, 구간)으로 표현돼요. 쉽게 말해, 요청의 여권을 만들어서 각 서비스마다 도장을 찍고 다니게 하는 거라고 보면 돼요.
- OpenTelemetry (오픈텔레메트리): CNCF(Cloud Native Computing Foundation)에서 주도하는 오픈소스 프로젝트예요. 벤더에 종속되지 않고, 모든 Observability 데이터를 수집하고 내보내는 표준화된 방법을 제공합니다. 애플리케이션 코드에 SDK(Software Development Kit)를 넣어 계측(Instrumentation)하고, OpenTelemetry Collector(컬렉터)를 통해 데이터를 수집하고 원하는 백엔드(Jaeger, Prometheus, Zipkin 등)로 전송할 수 있어요.
처음엔 이 용어들이 다 비슷비슷하게 느껴졌는데, 결국 OpenTelemetry는 Observability를 구현하기 위한 도구고, 그중 분산 추적은 특히 마이크로서비스 환경에서 빛을 발하는 핵심 기능이라고 이해하면 편해요.
실전 구현: K8s 환경에서 OpenTelemetry Collector 설정하기
자, 이제 직접 Kubernetes 클러스터에 OpenTelemetry를 설정해볼 시간입니다. 저는 주로 Helm(헬름)을 사용해서 배포하는 편인데, 이게 관리하기도 편하고 설정도 직관적이더라고요.
1. OpenTelemetry Collector 배포 전략 선택
OpenTelemetry Collector는 데이터를 수집하고 처리해서 백엔드로 보내는 역할을 합니다. K8s 환경에서는 보통 두 가지 방식으로 배포할 수 있어요.
- Agent (DaemonSet) 모드: 각 노드에 하나씩 Collector를 배포해서, 해당 노드에서 실행되는 애플리케이션들의 트레이스/메트릭을 수집합니다. 노드별로 리소스를 분리할 수 있고 안정적이거든요. 저는 주로 이 방식을 선호해요.
- Gateway (Deployment) 모드: 클러스터 내부에 중앙 Collector를 배포해서 모든 트래픽을 한곳으로 모읍니다. 관리는 편하지만, 부하가 한곳에 집중될 수 있고 네트워크 경로가 복잡해질 수 있죠.
이 글에서는 DaemonSet 모드로 Agent를 배포하는 방법을 기준으로 설명드릴게요. 각 노드에서 효율적으로 데이터를 수집하는 데 유리하거든요.
2. Helm으로 OpenTelemetry Collector 배포
먼저 OpenTelemetry Helm Chart를 추가하고 업데이트합니다.
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
다음으로, values.yaml 파일을 만들어서 Collector 설정을 커스터마이징해요. 여기서는 Jaeger(예거) 백엔드로 트레이스를 내보내는 설정을 해볼 거고, Jaeger는 분산 추적 데이터를 시각화하는 데 정말 유용한 도구예요.
# otel-collector-values.yaml
agent:
mode: "daemonset"
config:
receivers:
otlp:
protocols:
grpc:
http:
processors:
batch:
exporters:
jaeger:
endpoint: "jaeger-collector.monitoring.svc.cluster.local:14250" # Jaeger Collector 서비스 주소
tls:
insecure: true # 실제 프로덕션 환경에서는 TLS 설정 필요
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [jaeger]
# Jaeger도 함께 배포한다고 가정합니다. (별도 Helm Chart 사용)
# jaeger:
# agent:
# strategy: "daemonset"
# collector:
# ingress:
# enabled: true
# hosts:
# - jaeger.mydomain.com
위 values.yaml 파일로 Helm을 이용해 Collector를 배포해요.
helm install otel-collector open-telemetry/opentelemetry-collector -f otel-collector-values.yaml -n monitoring --create-namespace
-n monitoring은 monitoring 네임스페이스에 배포하겠다는 의미예요. --create-namespace로 없으면 새로 만들어줍니다. 이 과정이 끝나면 각 노드에 OpenTelemetry Collector Agent가 실행될 거고요.
Kubernetes 클러스터 내에서 OpenTelemetry Collector Agent가 DaemonSet으로 배포되어 각 노드의 애플리케이션 트레이스를 수집하는 구성도입니다.
3. 애플리케이션 계측 (Instrumentation)
Collector가 준비되었으니, 이제 애플리케이션에서 트레이스를 생성해서 Collector로 보내야 해요. OpenTelemetry는 다양한 언어별 SDK를 제공하거든요. 예를 들어 Python 애플리케이션이라면 다음과 같이 계측할 수 있어요.
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
# 리소스 설정 (서비스 이름 등)
resource = Resource.create({"service.name": "my-python-service"})
# TracerProvider 생성
tracer_provider = TracerProvider(resource=resource)
trace.set_tracer_provider(tracer_provider)
# OTLP 익스포터 설정 (Collector Agent의 주소)
# Kubernetes 내부에서 서비스 이름으로 접근 가능합니다.
# otel-collector는 'otel-collector-agent' 서비스로 노출됩니다.
exporter = OTLPSpanExporter(endpoint="otel-collector-agent.monitoring.svc.cluster.local:4317")
# SpanProcessor 등록
tracer_provider.add_span_processor(SimpleSpanProcessor(exporter))
# Tracer 가져오기
tracer = trace.get_tracer(__name__)
# 트레이스 생성 예시
with tracer.start_as_current_span("my-operation") as span:
span.set_attribute("http.method", "GET")
span.set_attribute("http.route", "/data")
# ... 여기에 실제 비즈니스 로직
print("Doing some work...")
with tracer.start_as_current_span("sub-operation"):
print("Doing sub-work...")
print("Trace sent!")
otel-collector-agent.monitoring.svc.cluster.local:4317이 여기서 핵심인데요, 이는 Kubernetes 서비스 디스커버리를 통해 monitoring 네임스페이스의 otel-collector-agent 서비스(OpenTelemetry Collector Agent가 노출하는 gRPC 포트)로 트레이스를 보낸다는 의미예요.
⚠️ 삽질 & 트러블슈팅 경험
제가 이 과정을 진행하면서 겪었던 몇 가지 삽질과 해결책을 공유해 드릴게요. 여러분은 저처럼 헤매지 마시길!
- Collector Agent 포드 상태 불량:
kubectl get pods -n monitoring으로 확인했을 때 Collector Agent 포드가CrashLoopBackOff상태인 경우가 있었어요.kubectl logs <pod-name> -n monitoring으로 로그를 확인해보니, 설정 파일에 오타가 있거나 리소스 부족으로 인해 시작되지 못하는 경우가 많더라고요. 특히config:아래 들여쓰기(indentation) 오류가 잦았습니다. - 네트워크 연결 문제: 애플리케이션에서 Collector로, Collector에서 Jaeger로 트레이스가 전송되지 않는 문제가 있었어요. 이럴 땐 다음을 확인해보세요.
- Kubernetes Service Name: 애플리케이션에서 Collector로 트레이스를 보낼 때,
otel-collector-agent.monitoring.svc.cluster.local:4317주소가 올바른지 확인해야 합니다. 서비스 이름이 잘못되었거나 네임스페이스가 다르면 연결이 안 되죠. - Port: OTLP gRPC 기본 포트인 4317이 맞는지, 또는 OTLP HTTP 기본 포트인 4318이 맞는지 확인하세요.
- Network Policy: 혹시 Kubernetes Network Policy(네트워크 정책)가 설정되어 있다면, 애플리케이션 파드에서 Collector 파드로의 4317/4318 포트 통신이 허용되어 있는지 확인해야 해요. 저도 이 정책 때문에 한참을 헤맸거든요.
- Firewall: 클러스터 외부와 통신하는 경우, 방화벽 규칙도 확인해야 합니다.
- Kubernetes Service Name: 애플리케이션에서 Collector로 트레이스를 보낼 때,
- Jaeger UI에서 트레이스 안 보임: Collector는 잘 동작하는데 Jaeger UI에서 아무것도 안 보이는 경우가 있었어요. 이건 주로 Collector의
exporters설정 문제이거나 Jaeger Collector 서비스 주소가 잘못된 경우였거든요. Jaeger Collector의 포트(gRPC는 14250, HTTP는 14268)가 맞는지, 서비스 이름이 정확한지 꼼꼼히 확인해야 해요. 💡
이런 문제들을 해결할 때 가장 중요한 건 로그(Logs)를 꼼꼼히 확인하고, kubectl describe pod 명령어로 파드의 이벤트와 환경 변수를 살펴보는 거예요. 그리고 curl이나 telnet 같은 간단한 도구로 포트 연결 테스트를 해보는 것도 큰 도움이 됩니다.
OpenTelemetry Collector 설정 중 문제가 발생하여 로그와 Kubernetes 이벤트를 분석하며 트러블슈팅하는 인프라 엔지니어의 모습입니다.
결과 확인 및 활용 사례
모든 설정이 완료되고 애플리케이션에서 트레이스를 보내기 시작하면, 이제 Jaeger UI 같은 백엔드에서 멋진 시각화된 트레이스를 확인할 수 있어요. 저도 처음 성공했을 때 '드디어 됐다!' 하고 외쳤던 기억이 나네요. 🎉
Jaeger UI에 접속해서 서비스 이름을 선택하고 검색하면, 아래와 같이 요청의 전체 흐름을 시각적으로 볼 수 있습니다. 각 스팬이 어떤 서비스에서 얼마나 시간을 소모했는지, 어떤 에러가 발생했는지 한눈에 파악할 수 있어요.
- 성능 병목 지점 파악: 특정 서비스나 함수 호출에서 유독 시간이 오래 걸린다면, 그 부분이 성능 병목 지점일 가능성이 높아요. 트레이스를 통해 정확히 어디서 시간이 지연되는지 찾아낼 수 있죠.
- 에러 원인 분석: 에러가 발생한 스팬을 클릭하면 관련 로그나 태그(tags) 정보를 확인하여 문제의 근본 원인을 빠르게 파악할 수 있어요. 수많은 로그를 뒤지는 것보다 훨씬 효율적이죠.
- 서비스 의존성 파악: 복잡한 마이크로서비스 간의 호출 관계를 시각적으로 이해하는 데 큰 도움이 돼요. 새로운 팀원이 합류했을 때 시스템 구조를 설명하는 자료로도 활용할 수 있거든요.
Jaeger UI에서 애플리케이션의 분산 추적 데이터가 성공적으로 수집되어 시각화된 대시보드 스크린샷입니다. 서비스 간 호출 흐름과 각 스팬의 지연 시간을 보여줍니다.
마무리하며: OpenTelemetry, 선택이 아닌 필수
OpenTelemetry를 Kubernetes 환경에서 설정하고 활용하는 과정이 처음엔 다소 복잡하게 느껴질 수 있어요. 하지만 한 번 구축하고 나면 얻을 수 있는 이점은 정말 상상 이상이라고 생각합니다. 특히 복잡한 마이크로서비스 아키텍처에서는 분산 추적이 선택이 아닌 필수가 되고 있거든요.
저도 13년 동안 수많은 인프라 환경을 운영하면서, 이렇게 시스템의 속을 들여다볼 수 있는 도구의 중요성을 절실히 느꼈어요. OpenTelemetry는 단순히 에러를 찾는 것을 넘어, 시스템의 건강 상태를 지속적으로 모니터링하고 최적화하는 데 큰 인사이트를 제공해줍니다.
다음 글에서는 OpenTelemetry를 활용해서 메트릭(Metrics)과 로그(Logs)를 수집하고 시각화하는 방법에 대해 좀 더 깊이 있게 다뤄볼 예정이에요. 궁금한 점이 있다면 언제든지 댓글로 남겨주세요!
OpenTelemetry가 제공하는 Observability의 세 가지 핵심 기둥인 Logs, Metrics, Traces를 시각적으로 요약한 인포그래픽입니다.
'IT > k8s' 카테고리의 다른 글
| [Kubernetes] External Secrets Operator 보안 강화 체크리스트 10가지 (0) | 2026.06.08 |
|---|---|
| [k8s] OpenShift 비용 최적화: 실제 청구서와 예상 비용 불일치 분석 (0) | 2026.06.04 |
| [K8s] 쿠버네티스 비용 최적화 5가지 핵심 전략 - 클라우드 비용 폭탄 방지 (0) | 2026.06.04 |
| [k8s] Calico 네트워크 정책 베스트 프랙티스: 프로덕션 환경 보안 강화 체크리스트 (1) | 2026.06.01 |
| [Kubernetes] Helm 차트 비용 최적화 전략: 클라우드 리소스 낭비 줄이기 (1) | 2026.05.31 |
| [k8s] GKE WireGuard 보안 취약점 분석: 매니지드 쿠버네티스 보안 강화 전략 (0) | 2026.05.31 |