Jaeger

개요

|500
Go놈의 캐릭터 그만 좀 쓰면 안 될까
예거는 관측 가능성 중 트레이싱 지표를 시각화해주는 도구이다.
Uber Technologies에서 개발됐으며 2016년에 CNCF에 기증되어 지금은 졸업했다.

기능


트레이싱 시각화 맛집이다.
그러나 사실 단순한 시각화 툴이 아니라, 트레이스를 위한 컬렉터와 데이저 저장까지 아우르는 아키텍쳐를 가지고 있다.
즉 예거라는 툴을 하나로 보았을 때 예거는 다음의 기능을 지원한다.

더 자제한 내용은 아래 아키텍처 부분 참고.

기본적으로는 OTel의 전신이 OpenTracing의 설정을 따르는 방식에 대해 트레이싱을 지원했으나, 이제는 OTLP도 지원한다.
24년 말 V2로 올라오면서 기본 베이스 설정도 오텔 기반으로 바뀌었다.[1]

아키텍처

문서 맛도리
위에서 언급한 대로, 예거는 트레이스 시그널을 생성하는 라이브러리, 이를 수집하는 수집기, 해당 데이터 저장소와 시각화 툴의 구조로 이뤄져 있다.
그래서 이전에는 어플리케이션 코드에 예거 라이브러리를 넣으면 통신 간 트레이싱을 위한 컨텍스트를 생성하는 트레이스 생성기도 가지고 있었다.
아예 예거 전용의 uber-trace-id라는 헤더까지 있었다고 한다(우버에서 만들어서 그런지 우버를 접두사로 붙였다).
(그렇지만 오텔의 물보라에 전부 휩쓸려가 이제 라이브러리는 지원되지 않는다..[2])

예거에만 초점을 맞춰 구성되는 컴포넌트를 정리하자면 다음과 같다.


컬렉터와 저장소, 쿼리의 관계를 도식화한 그림.
V2 기준 아키텍쳐로, 완전히 오텔에서 제시하는 표준 아키텍쳐 방식으로 가공됐다.
보다시피 예거 전용, 혹은 OTLP(오픈텔레메트리 프로토콜), 짚킨 방식 헤더를 전부 받고, 프로세싱을 거쳐 노출한다.
그리고 이것들을 하나의 스토리지에 저장한다.
시각화를 할 땐 그 스토리지에서 하나의 트레이스로 묶이는 것들을 한번에 조회하는 식이다.

이 그림은 오텔 트레이스를 기반으로 카프카를 거쳐서 시각화를 하는 아키텍쳐를 나타낸다.
예거 프로그램 자체는 다양한 역할을 수행할 수 있도록 설계됐다.
그래서 위 그림에서 각각 컬렉터, 인제스터, 쿼리 역할을 하는 3개의 예거를 돌리고 있는 것을 볼 수 있다.
([[#설치]]부분을 보면 알 수 있듯, 이 기능을 하나의 프로세스로 전부 실행하는 올인원 방식도 가능하긴 하다.)

컬렉터로 트레이스가 전송되는 과정을 조금 더 세분화하면, 먼저 어플리케이션 쪽에서 트레이스 데이터가 뽑혀 나오는 것을 각 담당 에이전트가 수집한다.
그 다음에 이를 중앙 집중 처리 장치인 컬렉터로 보낸다.
이때 에이전트를 오텔 컬렉터로 대체해 세팅하는 것이 가능하다.[4]
아예 라이브러리에서 바로 컬렉터로 데이터를 쏘도록 하는 것도 물론 가능하다.[5]

예거 클라이언트 라이브러리


이제는 쓰지 않지만, 예거 클라이언트 라이브러리는 이런 식으로 동작했다.
요청이 들어올 때 핸들러 구현체가 먼저 이 요청을 받아 컨텍스트 정보를 붙이고, 나가는 요청에 대해서는 클라이언트 구현체가 컨텍스트를 전파하는 역할을 맡는다.
(이 방식은 실질적으로 오텔의 다양한 제로코드 인스트루멘탈도 비슷할 것이다.)
그리고 해당 라이브러리가 모인 데이터를 컬렉터로 보낸다.

설치

docker run --rm --name jaeger \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 5778:5778 \
  -p 9411:9411 \
  jaegertracing/jaeger:2.5.0

V2부터는 올인원 이미지를 이용해 편하게 컨테이너 하나로 띄울 수 있다.

오퍼레이터

kubectl create namespace observability
# 커스텀 리소스 설치
kubectl create -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/main/deploy/crds/jaegertracing.io_jaegers_crd.yaml 

kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/main/deploy/service_account.yaml
kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/main/deploy/role.yaml
kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/main/deploy/role_binding.yaml
kubectl create -n observability -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/main/deploy/operator.yaml

예거 역시 오퍼레이터를 제공한다.[6]

apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: my-jaeger
spec:
  strategy: allInOne # 세 가지 모드 가능
  # 올인원 모드일 때
  allInOne:
    image: jaegertracing/all-in-one:latest # <2>
    options: # <3>
      log-level: debug # <4>
  # 프로덕션 모드일 때, 올인원과 양립 불가
  collector:
    maxReplicas: 5
    resources:
      limits:
        cpu: 100m
        memory: 128Mi
  # 스트리밍 모드일 때 추가 가능한 필드
  ingester:
    options:
      kafka: # <1>
        consumer:
          topic: jaeger-spans
          brokers: my-cluster-kafka-brokers.kafka:9092
      ingester:
        deadlockInterval: 5s # <2>
  # 스토리지 세팅
  storage:
    type: memory # <5>
    options: # <6>
      memory: # <7>
        max-traces: 100000
  ingress:
    enabled: false # <8>
  # 에이전트는 데몬셋으로 배포
  agent:
    strategy: DaemonSet # <9>
  annotations:
    scheduler.alpha.kubernetes.io/critical-pod: "" # <10>

strategy 필드에서는 배포할 예거의 모드를 지정할 수 있다.
allInOne모드는 위에서 말한 데모를 위한 방식으로, 컬렉터와 인메모리 저장소, ui가 한 프로세스에 들어간다.
production는 운영 환경에서 쓸 만한 예거 세팅이다.

streaming 모드는 스토리지에 저장하기 이전에 스트리밍을하는 injester를 추가해 스토리지의 압박을 줄이면서 데이터를 안전하게 저장하기 위한 모드이다.

에이전트 배치

에이전트는 두 가지 방식으로 배치가 가능하다.

  annotations:
    "sidecar.jaegertracing.io/inject": "true" # <1>

첫번째는 위 어노테이션을 워크로드에 넣어서 사이드카로 에이전트를 주입하는 방식이다.

  agent:
    volumeMounts:
    - name: agent-volume
      mountPath: /tmp/agent
      readOnly: true
    volumes:
      - name: agent-volume
        secret:
          secretName: agent-secret

예거 양식에서 agent 필드를 세팅해서 주입될 사이드카에 대한 상세 설정을 넣을 수 있다.

  agent:
    strategy: DaemonSet

이렇게 설정하면 데몬셋으로 에이전트가 호스트 노드의 포트 하나를 물고 배치된다.
(이때 호스트 노드가 예거가 사용할 포트를 이미 사용 중이면 문제가 발생한다.)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: acme/myapp:myversion
		# 데몬셋의 경로를 노출시킨다.
        env:
        - name: JAEGER_AGENT_HOST
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP

이 상태에서 어플리케이션에 위와 같은 방식으로 JAEGER_AGENT_HOST 경로를 환경 변수로 넣어주면 된다.

apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:디스커버리
  name: simple-prod
spec:
  strategy: production
    storage:
      type: elasticsearch
      options:
        es:
          server-urls: http://elasticsearch:9200

apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: simple-streaming
spec:
  strategy: streaming
  collector:
    options:
      kafka: # <1>
        producer:
          topic: jaeger-spans
          brokers: my-cluster-kafka-brokers.kafka:9092
  storage:
    type: elasticsearch
    options:
      es:
        server-urls: http://elasticsearch:9200

오픈텔레메트리 오퍼레이터 활용

# 서트 매니저와 오픈텔레메트리 오퍼레이터 설치
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.1/cert-manager.yaml
kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml

V2 부터는 거의 오텔 따까리가 된 관계로 아예 OpenTelemtry Operator에 내장된 형태로도 배포가 가능하다.[7]
위 예시 파일에서는 서트 매니저를 같이 설치해야 하는데, 어드미션 웹훅에서 api 서버와 통신에 사용할 인증서를 세팅하기 위함이다.
헬름으로 설치한다면 설정을 바꾸면 돼서 굳이 이렇게 할 필요는 없다.

kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
  name: jaeger-inmemory-instance
spec:
  image: jaegertracing/jaeger:latest
  ports:
  - name: jaeger
    port: 16686
  config:
    service:
      extensions: [jaeger_storage, jaeger_query]
      pipelines:
        traces:
          receivers: [otlp]    
          exporters: [jaeger_storage_exporter]
    extensions:
      jaeger_query:
        storage:
          traces: memstore
      jaeger_storage:
        backends:
          memstore:
            memory:
              max_traces: 100000
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
    exporters:
      jaeger_storage_exporter:
        trace_storage: memstore
EOF

관련 문서

이름 noteType created
4W - 오픈텔레메트리 기반 트레이싱 예거 시각화, 키알리 시각화 published 2025-05-03

참고


  1. https://medium.com/jaegertracing/jaeger-v2-released-09a6033d1b10 ↩︎

  2. https://www.jaegertracing.io/docs/1.22/client-libraries/ ↩︎

  3. https://www.jaegertracing.io/docs/1.23/faq/#what-is-the-recommended-storage-backend ↩︎

  4. https://www.jaegertracing.io/docs/2.5/architecture/ ↩︎

  5. https://www.jaegertracing.io/docs/1.23/faq/#do-i-need-to-run-jaeger-agent ↩︎

  6. https://github.com/jaegertracing/jaeger-operator#jager-v2-operator ↩︎

  7. https://github.com/jaegertracing/jaeger-operator?tab=readme-ov-file#jager-v2-operator ↩︎