[Observability] OpenTelemetry

Sunwu Park·2024년 12월 15일

Observability

목록 보기
3/3
post-thumbnail

서론

프로젝트를 진행하다 보면 Observability를 구현해야 할 순간이 온다. 이때 Prometheus, Loki, Grafana 같은 툴을 각각 설정하고 관리하는 건 꽤 번거롭고, 추후 확장성도 떨어질 수 있다. 그래서 데이터를 수집하고 변환한 뒤 백엔드로 전송하는 과정을 통합적으로 관리할 수 있는 방안이 필요했고, 이 과정에서 OpenTelemetry(OTel)를 알게 되었다.

OTel은 Traces, Metrics, Logs 같은 데이터를 모으고 이를 다양한 백엔드로 내보내는 데 최적화된 Observability 프레임워크다. 이번 글에서는 OpenTelemetry가 무엇인지, 그리고 어떻게 활용할 수 있는지 살펴보자.


OpenTelemetry란?

간단히 말해서, OpenTelemetry는 원격 측정 데이터를 수집, 변환, 전송하는 오픈소스 Observability 표준이다. 실제 데이터를 저장하거나 쿼리하는 역할은 하지 않으며, Prometheus나 Loki 같은 툴과 연동해 데이터를 처리하도록 돕는 중간 역할을 한다.

OTel의 주요 목표는 데이터를 효율적으로 처리하기 위한 표준화된 SDK, API, 그리고 OTel Collector를 제공하는 것이다. 이를 통해 다양한 데이터를 단일 통합 형식으로 관리할 수 있다.

왜 OpenTelemetry가 중요할까?

  • 벤더 중립성: 특정 벤더나 기존 기술에 종속되지 않고도 데이터를 수집하고 내보낼 수 있다.
  • 확장성: 클라우드 네이티브 애플리케이션에 적합하며, 미래의 데이터 수요에 유연하게 대응할 수 있다.
  • 효율성: 복잡한 설정 없이 Traces, Metrics, Logs를 한곳에서 관리할 수 있다.

OTel은 현재 클라우드 네이티브 애플리케이션에서 가장 널리 사용되는 Observability 표준 중 하나로 자리 잡고 있다. 데이터의 미래를 준비하려는 조직이라면 OTel을 도입하는 것이 매우 합리적이다.
(From: ElasticSearch, what is opentelemetry)


OpenTelemetry 아키텍처 및 구성 요소

OTel은 아래와 같은 여러 구성 요소로 이루어져 있다.

주요 구성 요소

1. OpenTelemetry Collector

OTel Collector는 데이터를 수신하고 처리한 뒤, 이를 다양한 백엔드로 내보낸다.
다양한 데이터 포맷을 지원하며, 필요에 따라 데이터를 변환하거나 필터링할 수도 있다.

2. 언어별 SDK

OTel은 각 언어별 SDK를 통해 애플리케이션에서 Traces, Metrics, Logs를 수집하고 처리할 수 있다.

Language APIs & SDKs - OpenTelemetry

3. 계측 라이브러리

OTel은 주요 프레임워크와 라이브러리에서 원격 측정 데이터를 자동으로 생성하는 기능을 제공한다.
예를 들어, Spring Boot 같은 프레임워크에서도 OTel을 쉽게 연동할 수 있다.

4. 자동 계측

코드 수정 없이도 애플리케이션을 계측할 수 있는 기능을 제공한다.
애플리케이션에 OTel 에이전트를 추가하는 것만으로 계측이 가능하다.

5. 내보내기 도구

OTel은 데이터를 백엔드 툴로 내보내는 과정을 표준화한다.
예를 들어, OTel을 통해 Prometheus로 메트릭을 보내거나, Jaeger로 트레이스를 전송할 수 있다.


OpenTelemetry의 종류

  • OpenTelemetry Collector: 기본적인 Observability 데이터를 수집, 처리 및 내보내기 위한 코어 구성 요소를 포함하는 콜렉터
  • Opentelemetry Contrib collector: 기본 OpenTelemetry Collector 기능에 더해 추가적으로 다양한 receiver, processor, exporter 기능이 포함된 확장 버전

OpenTelemetry 아키텍처

  1. 데이터 수집
    서비스에서 발생하는 Traces, Metrics, Logs를 SDK와 API를 통해 수집한다.

  2. 데이터 처리
    OTel Collector를 사용해 데이터를 필터링하거나 집계할 수 있다.

  3. 데이터 내보내기
    가공된 데이터를 Prometheus, Grafana 등의 백엔드로 전송한다.

OTel은 데이터를 수집하는 데 초점을 맞추며, 시각화나 통계 처리는 백엔드 툴에게 맡긴다.
=> 표준화된 방식으로 데이터를 처리 + 다양한 툴과의 호환성을 확보


OTel Collector Architecture

OpenTelemetry Collector는 텔레메트리 데이터를 수집, 처리, 그리고 전송하는 역할을 수행하는 실행 파일

1. Pipelines

Pipeline은 Collector 내에서 데이터를 수집(Receivers), 처리(Processors), 그리고 전송(Exporters)하는 경로를 정의


Pipeline의 특징

  1. 지원하는 데이터 유형 : Traces, Metrics, Logs

    • 구성 파일에서 Pipeline의 Data Type 을 정의.
    • Receivers, Processors, Exporters는 해당 Data Type을 지원해야 하며, 그렇지 않으면 pipeline.ErrSignalNotSupported 오류가 발생
  2. 구성 요소의 조합

    • 하나의 Pipeline에는 여러 Receivers, Processors, Exporters를 포함
    • 동일한 Receiver와 Exporter는 여러 Pipeline에서 재사용이 가능
  3. 데이터 흐름

    • Receivers는 데이터를 수집
    • Processors는 데이터를 처리하거나 필터링하며, 필요 시 일부 데이터를 삭제
    • Exporters는 처리된 데이터를 외부 시스템(예: Prometheus, Tempo 등)으로 전송

Fan-Out 방식?
Pipeline은 하나 이상의 Receiver를 포함할 수 있다.
모든 Receiver에서 수집된 데이터는 첫 번째 Processor로 전달되며, Processor는 데이터를 처리한 후 다음 Processor로 넘긴다.
이 과정에서 Processor는 샘플링 또는 필터링을 통해 일부 데이터를 삭제할 수도 있다.
마지막 Processor는 처리된 데이터를 Exporter로 전달하며,
각 Exporter는 데이터 복사본을 받아 여러 대상에 동시에 전송한다. 이를 fan-out 방식이라고 한다.

Pipeline 구성 예시

service:
  pipelines:
    traces: # Pipeline의 데이터 유형 (traces)
      receivers: [otlp, zipkin] # 데이터를 수집할 Receivers
      processors: [memory_limiter, batch] # 데이터를 처리할 Processors
      exporters: [otlp, zipkin] # 처리된 데이터를 전송할 Exporters

2. Receivers

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가 위의 설정을 로드하면 다음과 같은 구조가 만들어집니다:

Fan-Out 동작

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가 비동기적으로 작동하거나, 데이터를 효율적으로 처리할 수 있도록 주의

3. Exporters

Exporters는 데이터를 네트워크 상의 목적지로 전송하거나 다른 위치로 전달하는 역할
예) debug exporter는 수집된 텔레메트리 데이터를 로그로 출력

OpenTelemetry Collector에서 Exporter는 같은 타입의 Exporter를 하나의 Pipeline에 여러 개 설정할 수 있다. 이 방식으로 서로 다른 OTLP 엔드포인트로 데이터를 전송

Exporter 설정 예시

exporters:
  otlp/1:
    endpoint: example.com:4317
  otlp/2:
    endpoint: localhost:14317

여기서 otlp/1은 example.com:4317로 데이터를 전송, otlp/2는 localhost:14317로 데이터를 전송

Exporters와 Pipeline의 관계

보통 하나의 Exporter는 하나의 Pipeline에서 데이터를 전달받지만, 여러 Pipeline이 같은 Exporter로 데이터를 보낼 수도 있습니다.

다중 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으로부터 데이터를 받아서 처리

Exporters의 데이터 흐름 구조


4. Processors

Processors는 OpenTelemetry Collector에서 데이터의 전처리, 변환, 필터링 등을 수행


Processors의 동작 방식

Pipeline은 Processors를 순차적으로 연결하여 구성:
1. 첫 번째 Processor는 Pipeline에 설정된 하나 이상의 Receiver로부터 데이터를 받음
2. 마지막 Processor는 데이터를 하나 이상의 Exporter로 전달
3. 중간 Processor들은 바로 이전 Processor로부터 데이터를 받아 변환 또는 처리한 후 다음 Processor로 전달


Processors의 주요 기능

  1. 데이터 변환: 데이터 속성을 추가하거나 제거
  2. 데이터 필터링: 특정 데이터를 드롭하거나 전달하지 않음.
    예: probabilisticsampler Processor.
  3. 새로운 데이터 생성: 기존 데이터를 바탕으로 새로운 데이터를 생성

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는 자체 상태를 유지

Processor 사용 시 유의 사항

  1. Processor 중복 참조 제한:
  • 동일한 Pipeline에서 같은 Processor를 여러 번 참조할 수 없다.
  1. 독립적인 상태 관리:
  • 각 Pipeline은 독립적인 Processor 인스턴스를 유지하므로 서로 간섭하지 않다.

OTel Collector 설정파일 예시

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]

1. Extensions

  • basicauth/client: 기본 인증 확장으로, client_auth 옵션을 통해 외부 서버에 인증할 때 사용할 사용자 이름과 비밀번호를 설정
extensions:
  basicauth/client:
    client_auth:
      username: {{username}}
      password: {{password}}

2. Receivers

  • otlp: OTLP 프로토콜을 사용하는 수신기(receiver)로, HTTP 프로토콜을 통해 {{otlp endpoint}}에서 데이터를 받음
receivers:
  otlp:
    protocols:
      http:
        endpoint: {{otlp endpoint}}

3. Processors

  • batch: 데이터를 일정 배치로 묶어 전송하는 프로세서로, 데이터 전송 효율을 높임
processors:
  batch:

4. Exporters

  • debug: 디버그 출력 설정으로, 데이터를 Collector 로그에 상세하게 출력
  • otlp/trace, otlp/logs, otlp/metrics: OTLP로 데이터를 내보내기 위한 각기 다른 exporter 설정.
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

5. Service

  • 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]

쿠버네테스에서의 OpenTelemetry

쿠버네티스와 프로메테우스의 한계

쿠버네티스는 컨테이너를 빠르게 생성하고 제거하는 특성상 매우 동적인 환경을 제공
하지만 이러한 동적 특성은 Observability 시스템, 특히 프로메테우스(Prometheus)와 같은 툴에는 어려움

  1. Pull 메커니즘 기반의 프로메테우스:

    • 프로메테우스는 풀링(pull) 방식으로 메트릭을 수집하기 때문에 사용자가 직접 스크레이핑할 엔드포인트를 정의해야 하며, 이는 동적 리소스가 지속적으로 변경되는 쿠버네티스 환경에서는 어려움
  2. 확장성 문제:

    • 클러스터 규모가 커질수록 프로메테우스 인스턴스 하나에 과부하가 발생하고 이를 해결하려면 여러 개의 프로메테우스 인스턴스를 배포해야 하지만, 동일한 엔드포인트를 여러 번 스크레이핑하게 되어 비효율이 발생

OpenTelemetry Target Allocator?

타깃 얼로케이터의 필요성

OTel 컬렉터를 확장하는 과정에서 샤딩문제를 해결하기 위해 Target Allocator가 도입

  1. OTel 컬렉터의 한계:

    • 기본적으로 OTel 컬렉터는 수평 확장이 불가능
    • 동일한 엔드포인트를 여러 인스턴스가 스크레이핑할 경우 중복된 메트릭 수집이 발생
  2. Target Allocator의 역할:

    • Target Allocator는 OTel 컬렉터 인스턴스 간에 스크레이핑 대상을 분산
    • 모든 엔드포인트가 한 번에 하나의 OTel 컬렉터 인스턴스에서만 스크레이핑되도록 관리

Target Allocator의 동작 방식

  1. 스크레이핑 대상 분배:

    • Target Allocator는 각 OTel 컬렉터 인스턴스가 처리할 엔드포인트를 결정
    • 이는 상태 저장(stateful) 방식으로 작동하여, 엔드포인트가 지속적으로 변경되는 환경에서도 효과적
  2. 프로메테우스 구성 관리:

    • Target Allocator는 각 OTel 컬렉터의 프로메테우스 구성을 동적으로 조작
    • 이를 통해, 분산된 OTel 컬렉터 인스턴스가 효율적으로 동작하도록 지원
  3. 확장성 확보:

    • 새로운 OTel 컬렉터 인스턴스가 추가되더라도 자동으로 엔드포인트가 다시 분배
    • 클러스터가 확장되더라도 안정적으로 메트릭을 수집

정리

쿠버네티스의 동적 환경은 기존 Observability 툴의 한계를 드러내지만, OpenTelemetry Collector와 타깃 얼로케이터를 활용하면 이러한 문제를 해결할 수 있습니다. OTel 컬렉터는 프로메테우스와 유사한 방식으로 메트릭을 수집하면서도 더 유연하고 중앙화된 접근 방식을 제공합니다. 타깃 얼로케이터는 OTel 컬렉터를 수평 확장 가능하게 만들어 대규모 쿠버네티스 클러스터에서도 효율적인 메트릭 관리와 수집을 가능하게 합니다.

출처

0개의 댓글