7 min read

[Kubernetes] Grafana Tempo 를 구축해보자 - (2)

ㅁㄴㅇㄹ

이전 글을 통해 Single-Binary 형태의 Grafana Tempo 를 배포해 보았다
하지만 안타깝게도 Tempo 는 단순히 저장하는 목적이고 수집을 해주지는 않는다

보통 trace를 수집할 때는 두 가지 방법이 있다

  1. 코드에 직접 어떤 메소드를 수집 할 지 지정하는 방법
  2. Bytecode 에 수집기를 주입하는 방법

이 글에서는 2번(Auto Instrumentation 이라고 부른다) 을 이용해 코드 수정 없이 trace를 수집해보기로 하자
(1번은 구체적으로 어떤 메소드를 수집할 지 알고있을 때 좋은 방법이다)

그래서 어떻게 수집하는데??

OpenTelemetry 의 등장

OpenTelemetry
High-quality, ubiquitous, and portable telemetry to enable effective observability

MSA 에서는 tracing 이 모니터링에 있어서 매우 중요한 축을 차지하고 있기때문에
정형화된 방법들이 존재한다

OpenTelemetry(줄여서 OPTL 이라고도 한다) 는 CNCF 인큐베이팅 프로젝트이긴 하지만 사실 완전히 새로운 툴은 아니다.

기존에 존재하던 OpenTracing 과 OpenCensus 가 2019년 병합되어 만들어진 통합 툴로 운영환경에서도 충분히 검증되었다

OpenTracing 만 해도 2015년 릴리즈 되었으니 벌써 10년이 되어가니
Zipkin, Jaeger 와 더불어 사실상 tracing 계의 De-facto 표준이라고 볼 수 있겠다

어떻게 동작할까??

https://cloud.google.com/blog/ja/topics/developers-practitioners/easy-telemetry-instrumentation-gke-opentelemetry-operator?hl=ja

OpenTelemetry 는 보통 3가지 요소로 구성된다

  1. Operator
  2. Collector
  3. Instrumentation
Operator

Collector, Instrumentation 같은 리소스들을 관리해주는 쿠버네티스 Operator 이다
Operator 자체는 쿠버네티스의 속성이니 익숙하지 않다면 따로 살펴보는걸 추천한다

Collector

Instrument 에서 추출한 각종 trace, span 들을 모아주는 일종의 Proxy 역할을 한다
Tempo 로 데이터가 넘어가기 전 교통정리를 해주는 녀석이다

fluentd 나 logstash 같은 곳에서 많이 보이는 "파이프라인" 이라는 개념을 사용하는데
아래에서 다루겠지만 매우 직관적인 설정으로 구성되어 있다

Instrumentation

실제로 trace, span 을 수집한다
Bytecode 인젝션이 일어나는 주체이자 어플리케이션과 같이 실행되서
일거일수투족을 모니터링하는 감시자로 볼 수 있겠다

OPTL Operator 배포

먼저 OPTL 과 관련된 각종 리소스들을 관리해 줄 Operator 를 설치하자

opentelemetry-operator 0.75.0 · opentelemetry/opentelemetry-helm
OpenTelemetry Operator Helm chart for Kubernetes

Tempo 설치때와 마찬가지로
사이트의 오른쪽 INSTALLDEFAULT VALUES 를 활용하자

Values 수정

배포하기 전에 한가지 수정이 필요한 사항이 있다
Collector 의 이미지를 지정해야 한다

Operator 가 Collector 를 관리하기 때문에 어떤 이미지를 기반으로 만들지 정해주는 과정이다
otel/opentelemetry-contrib 이미지를 사용해야 일부 베타 또는 알파 기능도 사용해볼 수 있다

아래와 같이 값을 추가해주자

manager:
  collectorImage:
    repository: "otel/opentelemetry-collector-contrib"
OPTL 을 위한 네임스페이스 만들기
kubectl create namespace opentelemetry
클러스터에 릴리즈
helm repo add opentelemetry-helm https://open-telemetry.github.io/opentelemetry-helm-charts
helm install opentelemetry-operator opentelemetry-helm/opentelemetry-operator --version 0.75.0 -f values.yaml
배포결과 확인
[root@k3s-server1 opentelemetry]# helm list
NAME                    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                           APP VERSION
opentelemetry-operator  opentelemetry   1               2024-12-07 23:05:49.798488945 +0900 KST deployed        opentelemetry-operator-0.75.0   0.114.1 

Collector & Instrumentation 배포

Operator 가 배포되었으니 이제 CRD 를 이용해서
공장에서 찍어내듯이 Collector 와 Instrumentation 을 만들어 볼 수 있다

Collector 배포
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
  name: global-optl
spec:
  managementState: managed
  config:
  
  # 트레이스 정보를 받기 위한 입구를 지정한다
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
  
  # Tempo 로 최종 저장하기 전에 간단한 교통정리를 한다
    processors:
    
    # 메모리 사용량 제한도 해주고
      memory_limiter:
        check_interval: 1s
        limit_percentage: 75
        spike_limit_percentage: 15
        
    # 트레이스를 잠깐 모아뒀다가 한번에 보내기도 한다 
      batch:
        send_batch_size: 10000
        timeout: 10s

  # 트레이스 정보를 내보내기 위한 출구를 지정한다
    exporters:
      otlp: 
        endpoint: "tempo.tempo:4317" 
        tls: 
          insecure: true 
      
  # 여기서 파이프라인이 정의된다
    service:
      pipelines:
        traces:
        # 수신은 어디서?
          receivers: [otlp]
        # 교통정리는 어떻게?
          processors: [memory_limiter, batch]
        # 최종 트레이스는 어디로?
          exporters: [otlp]
Instrumentation 배포

어플리케이션에 OPTL 에이전트가 주입되면 아래의 설정을 기준으로 데이터를 전송한다

apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: global-optl-instrumentation
spec:

# Collector 주소를 지정해주자
  exporter:
    endpoint: http://global-optl.opentelemetry:4317
  propagators:
    - tracecontext
    - baggage
    - b3
    
# 테스트를 위해 샘플링은 1 = 100% 로 설정하자
# 운영환경에서는 데이터가 많이 발생할 수 있으니 적절하게 조정하자
  sampler:
    type: parentbased_traceidratio
    argument: "1"

어플리케이션에 적용

자, 이제 App에 OPTL 에이전트를 주입하기 위한 과정은 끝났다
예제로 App을 하나 올리고 주입해보자

테스트를 위해 아래 링크를 통해 bookinfo 샘플 App 을 하나 올렸다

kubernetes-sample-apps/bookinfo-example at master · digitalocean/kubernetes-sample-apps
Example DigitalOcean Kubernetes workload with service exposed through a DO load-balancer. - digitalocean/kubernetes-sample-apps
NAME                                  READY   STATUS    RESTARTS   AGE
pod/details-v1-5578dbf5c6-tkdkt       1/1     Running   0          100m
pod/productpage-v1-64c658687d-bm7kl   1/1     Running   0          100m
pod/ratings-v1-5949d767d4-2d9b2       1/1     Running   0          100m
pod/reviews-v1-699b8b7d4d-756n7       1/1     Running   0          100m
pod/reviews-v2-5955795d6b-ghwpn       1/1     Running   0          100m
pod/reviews-v3-748575f88d-hfplr       1/1     Running   0          100m

이렇게 App 이 구성되어 있다면 OPTL 에이전트 주입은 딱 한줄! 만 추가해주면 된다

링크에서 제공되는 kustomization.yaml 파일을 오픈하고
아래와 같이 annotation 을 추가해주자

추가 전
commonAnnotations:
  provider: kubernetes-sample-apps
추가 후
commonAnnotations:
  provider: kubernetes-sample-apps
  instrumentation.opentelemetry.io/inject-java: "opentelemetry/global-optl-instrumentation"

수정이 완료되었다면 App 을 다시 배포해주자
에이전트가 주입되면서 App Pod 들이 모두 재시작된다

이후 Pod 를 살펴보면 아래와 같이 Init Container 가 주입되어 있는걸 볼 수 있다!!

그리고 이전에 구축한 Grafana 로 접속해서 Tempo 를 살펴보면

이렇게 트레이스들이 저장되어 있는걸 볼 수 있다

트레이스를 하나 눌러보면

요청에 대한 상세 span 들을 확인해 볼 수 있다


이번 글에서는 OpenTelemetry Auto Instrumentation 을 이용해서
코드 한 줄 수정도 없이 트레이싱을 적용해 보았다.

다음 글에서는 이 수집된 트레이스를 이용해서 의미있는 메트릭을 추출하고
대시보드를 만들어보자