
프로젝트를 진행하다 보면 Observability를 구현해야 할 순간이 온다. 이때 Prometheus, Loki, Grafana 같은 툴을 각각 설정하고 관리하는 건 꽤 번거롭고, 추후 확장성도 떨어질 수 있다. 그래서 데이터를 수집하고 변환한 뒤 백엔드로 전송하는 과정을 통합적으로 관리할 수 있는 방안이 필요했고, 이 과정에서 OpenTelemetry(OTel)를 알게 되었다.
OTel은 Traces, Metrics, Logs 같은 데이터를 모으고 이를 다양한 백엔드로 내보내는 데 최적화된 Observability 프레임워크다. 이번 글에서는 OpenTelemetry가 무엇인지, 그리고 어떻게 활용할 수 있는지 살펴보자.
간단히 말해서, OpenTelemetry는 원격 측정 데이터를 수집, 변환, 전송하는 오픈소스 Observability 표준이다. 실제 데이터를 저장하거나 쿼리하는 역할은 하지 않으며, Prometheus나 Loki 같은 툴과 연동해 데이터를 처리하도록 돕는 중간 역할을 한다.
OTel의 주요 목표는 데이터를 효율적으로 처리하기 위한 표준화된 SDK, API, 그리고 OTel Collector를 제공하는 것이다. 이를 통해 다양한 데이터를 단일 통합 형식으로 관리할 수 있다.
OTel은 현재 클라우드 네이티브 애플리케이션에서 가장 널리 사용되는 Observability 표준 중 하나로 자리 잡고 있다. 데이터의 미래를 준비하려는 조직이라면 OTel을 도입하는 것이 매우 합리적이다.
(From: ElasticSearch, what is opentelemetry)
OTel은 아래와 같은 여러 구성 요소로 이루어져 있다.
OTel Collector는 데이터를 수신하고 처리한 뒤, 이를 다양한 백엔드로 내보낸다.
다양한 데이터 포맷을 지원하며, 필요에 따라 데이터를 변환하거나 필터링할 수도 있다.
OTel은 각 언어별 SDK를 통해 애플리케이션에서 Traces, Metrics, Logs를 수집하고 처리할 수 있다. 
Language APIs & SDKs - OpenTelemetry
OTel은 주요 프레임워크와 라이브러리에서 원격 측정 데이터를 자동으로 생성하는 기능을 제공한다.
예를 들어, Spring Boot 같은 프레임워크에서도 OTel을 쉽게 연동할 수 있다.
코드 수정 없이도 애플리케이션을 계측할 수 있는 기능을 제공한다.
애플리케이션에 OTel 에이전트를 추가하는 것만으로 계측이 가능하다.
OTel은 데이터를 백엔드 툴로 내보내는 과정을 표준화한다.
예를 들어, OTel을 통해 Prometheus로 메트릭을 보내거나, Jaeger로 트레이스를 전송할 수 있다.

데이터 수집
서비스에서 발생하는 Traces, Metrics, Logs를 SDK와 API를 통해 수집한다.
데이터 처리
OTel Collector를 사용해 데이터를 필터링하거나 집계할 수 있다.
데이터 내보내기
가공된 데이터를 Prometheus, Grafana 등의 백엔드로 전송한다.
OTel은 데이터를 수집하는 데 초점을 맞추며, 시각화나 통계 처리는 백엔드 툴에게 맡긴다.
=> 표준화된 방식으로 데이터를 처리 + 다양한 툴과의 호환성을 확보
OpenTelemetry Collector는 텔레메트리 데이터를 수집, 처리, 그리고 전송하는 역할을 수행하는 실행 파일


Pipeline은 Collector 내에서 데이터를 수집(Receivers), 처리(Processors), 그리고 전송(Exporters)하는 경로를 정의
지원하는 데이터 유형 : Traces, Metrics, Logs
pipeline.ErrSignalNotSupported 오류가 발생구성 요소의 조합
데이터 흐름
Fan-Out 방식?
Pipeline은 하나 이상의 Receiver를 포함할 수 있다.
모든 Receiver에서 수집된 데이터는 첫 번째 Processor로 전달되며, Processor는 데이터를 처리한 후 다음 Processor로 넘긴다.
이 과정에서 Processor는 샘플링 또는 필터링을 통해 일부 데이터를 삭제할 수도 있다.
마지막 Processor는 처리된 데이터를 Exporter로 전달하며,
각 Exporter는 데이터 복사본을 받아 여러 대상에 동시에 전송한다. 이를 fan-out 방식이라고 한다.
service:
pipelines:
traces: # Pipeline의 데이터 유형 (traces)
receivers: [otlp, zipkin] # 데이터를 수집할 Receivers
processors: [memory_limiter, batch] # 데이터를 처리할 Processors
exporters: [otlp, zipkin] # 처리된 데이터를 전송할 Exporters
Receivers는 데이터를 수신하거나, 수집하는 역할 수행 (scraper처럼 데이터를 직접 가져올 수도 있다).
일반적으로 하나의 Receiver는 하나의 Pipeline에 데이터를 전달하도록 구성됨.
그러나 동일한 Receiver가 수집한 데이터를 여러 Pipeline에 전달하도록 설정할 수도 있다.
다음은 동일한 Receiver를 여러 Pipeline에 연결하는 설정 예시:
receivers:
otlp:
protocols:
grpc:
endpoint: localhost:4317
service:
pipelines:
traces: # "traces" 타입의 첫 번째 Pipeline
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp]
traces/2: # "traces" 타입의 두 번째 Pipeline
receivers: [otlp]
processors: [transform]
exporters: [otlp]
위 예제에서는 otlp Receiver가 동일한 데이터를 traces Pipeline과 traces/2 Pipeline에 모두 전달합니다.
OpenTelemetry Collector 설정에서 Pipeline은 type[/name] 형식의 키 이름을 사용합니다. 예를 들어, traces와 traces/2는 각각 고유한 Pipeline을 나타냅니다. Collector가 위의 설정을 로드하면 다음과 같은 구조가 만들어집니다:

Receiver가 여러 Pipeline에 참조될 경우, Collector는 실행 시점에 하나의 Receiver 인스턴스만 생성합니다. 이 Receiver는 데이터를 fan-out consumer로 전달하며, fan-out consumer는 데이터를 각 Pipeline의 첫 번째 Processor로 보냅니다.
주의 사항
- Receiver에서 fan-out consumer로 데이터를 전달한 후, 각 Pipeline의 Processor로 데이터를 동기적으로 전달한다.
- 이로 인해 하나의 Processor가 데이터 처리를 블록(block)할 경우 다음과 같은 문제가 발생할 수 있습니다:
- 동일한 Receiver에 연결된 다른 Pipeline도 데이터 처리가 지연
- Receiver 자체가 새로 수신한 데이터를 처리하고 전달하지 못함
=> 각 Pipeline의 Processor가 비동기적으로 작동하거나, 데이터를 효율적으로 처리할 수 있도록 주의
Exporters는 데이터를 네트워크 상의 목적지로 전송하거나 다른 위치로 전달하는 역할
예) debug exporter는 수집된 텔레메트리 데이터를 로그로 출력
OpenTelemetry Collector에서 Exporter는 같은 타입의 Exporter를 하나의 Pipeline에 여러 개 설정할 수 있다. 이 방식으로 서로 다른 OTLP 엔드포인트로 데이터를 전송
exporters:
otlp/1:
endpoint: example.com:4317
otlp/2:
endpoint: localhost:14317
여기서 otlp/1은 example.com:4317로 데이터를 전송, otlp/2는 localhost:14317로 데이터를 전송
보통 하나의 Exporter는 하나의 Pipeline에서 데이터를 전달받지만, 여러 Pipeline이 같은 Exporter로 데이터를 보낼 수도 있습니다.
exporters:
otlp:
protocols:
grpc:
endpoint: localhost:14250
service:
pipelines:
traces: # 첫 번째 "traces" Pipeline
receivers: [zipkin]
processors: [memory_limiter]
exporters: [otlp]
traces/2: # 두 번째 "traces" Pipeline
receivers: [otlp]
processors: [transform]
exporters: [otlp]
otlp Exporter는 traces Pipeline과 traces/2 Pipeline으로부터 데이터를 받아서 처리

Processors는 OpenTelemetry Collector에서 데이터의 전처리, 변환, 필터링 등을 수행
Pipeline은 Processors를 순차적으로 연결하여 구성:
1. 첫 번째 Processor는 Pipeline에 설정된 하나 이상의 Receiver로부터 데이터를 받음
2. 마지막 Processor는 데이터를 하나 이상의 Exporter로 전달
3. 중간 Processor들은 바로 이전 Processor로부터 데이터를 받아 변환 또는 처리한 후 다음 Processor로 전달
probabilisticsampler Processor.같은 Processor 이름을 여러 Pipeline에서 참조할 수 있습니다. 하지만 다음과 같은 규칙이 적용됩니다:
1. 독립적인 인스턴스 생성: 각 Pipeline은 동일한 Processor를 독립적으로 사용하며, 각 Processor는 자체 상태를 유지합니다.
2. 동일한 설정 공유: 같은 이름의 Processor는 모든 Pipeline에서 동일한 설정을 사용합니다.
batch Processor다음은 batch Processor를 두 개의 Pipeline에서 사용하는 예시입니다:
processors:
batch:
send_batch_size: 10000
timeout: 10s
service:
pipelines:
traces: # 첫 번째 "traces" Pipeline
receivers: [zipkin]
processors: [batch]
exporters: [otlp]
traces/2: # 두 번째 "traces/2" Pipeline
receivers: [otlp]
processors: [batch]
exporters: [otlp]

중요: 두 Pipeline은 독립적인 batch Processor 인스턴스를 사용하며, 각각의 Processor는 자체 상태를 유지
extensions:
basicauth/client:
client_auth:
username: {{username}}
password: {{password}}
receivers:
otlp:
protocols:
http:
endpoint: {{otlp endpoint}}
processors:
batch:
exporters:
debug:
verbosity: detailed
otlp/trace:
endpoint: {{trace endpoint}}
tls:
insecure: true
otlp/logs:
endpoint: {{logs endpoint}}
tls:
insecure: true
otlp/metrics:
endpoint: {{metrics endpoint}}
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/trace]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlp/metrics]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlp/logs]
extensions: [basicauth/client]
extensions:
basicauth/client:
client_auth:
username: {{username}}
password: {{password}}
receivers:
otlp:
protocols:
http:
endpoint: {{otlp endpoint}}
processors:
batch:
exporters:
debug:
verbosity: detailed
otlp/trace:
endpoint: {{trace endpoint}}
tls:
insecure: true
otlp/logs:
endpoint: {{logs endpoint}}
tls:
insecure: true
otlp/metrics:
endpoint: {{metrics endpoint}}
tls:
insecure: true
pipelines: 데이터 처리 파이프라인을 정의하며, traces, metrics, logs 데이터 유형별로 각 파이프라인을 설정.
각 파이프라인은 동일한 otlp receiver, batch processor, 그리고 각기 다른 otlp exporter를 사용
extensions: basicauth/client를 확장으로 추가하여 인증 설정을 적용
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/trace]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlp/metrics]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlp/logs]
extensions: [basicauth/client]
쿠버네티스는 컨테이너를 빠르게 생성하고 제거하는 특성상 매우 동적인 환경을 제공
하지만 이러한 동적 특성은 Observability 시스템, 특히 프로메테우스(Prometheus)와 같은 툴에는 어려움
Pull 메커니즘 기반의 프로메테우스:
확장성 문제:
OTel 컬렉터를 확장하는 과정에서 샤딩문제를 해결하기 위해 Target Allocator가 도입
OTel 컬렉터의 한계:
Target Allocator의 역할:
스크레이핑 대상 분배:
프로메테우스 구성 관리:
확장성 확보:
쿠버네티스의 동적 환경은 기존 Observability 툴의 한계를 드러내지만, OpenTelemetry Collector와 타깃 얼로케이터를 활용하면 이러한 문제를 해결할 수 있습니다. OTel 컬렉터는 프로메테우스와 유사한 방식으로 메트릭을 수집하면서도 더 유연하고 중앙화된 접근 방식을 제공합니다. 타깃 얼로케이터는 OTel 컬렉터를 수평 확장 가능하게 만들어 대규모 쿠버네티스 클러스터에서도 효율적인 메트릭 관리와 수집을 가능하게 합니다.