8 min read

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

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

이전글을 통해 auto instrumentation 을 기반으로 트레이스를 수집해보았다

간단하게 대시보드를 만들고 시계열 패널을 추가해 Tempo 를 쿼리해보자

기본적으로 아무런 조건도 입력하지 않으면 모든 트레이스를 리턴한다
사실 모든 트레이스를 리턴하는건 아니고 Grafana 에서 자동으로 최근 N 개 까지만 짜른다.
(패널 하단에 Options-Limit 값을 통해 최대 1500개 까지 올릴 수 있다)

쿼리 해보면 아래처럼 자동으로 트레이스의 Duration 이 그래프로 표기되는걸 볼 수 있다.

시계열이 아닌 "Traces" 패널을 사용하면 아래처럼 특정 트레이스에 대한 상세내용도 패널에서 시각화 할 수 있다

조금 더 자세한 메트릭을 원한다면 Grafana 에서 제공하는 Transformation (패널의 쿼리 탭 옆에 있다) 이라는 강력한 기능을 사용해보자

쿼리의 결과를 조합해서 더 세밀한 메트릭들도 추출 할 수 있다

하지만 여기에는 맹점이 있는데, 당장 글 상단에 있는 Duration 을 시각화한 패널만 보아도 명백하다

단순히 Duration 이라는 지표를 얻기 위해서 수많은 데이터를 담고있는 트레이스를 Tempo 에서 쿼리해야 한다는 점이다

트레이스 자체가 개개별 스팬(트레이스 하위에 있는 막대기들) 의 집합이기 때문에 덩치도 크고 그로인해 쿼리에 대한 부담이 상당한 편이다

Grafana 로 시각화를 하게되면 보통은 10초, 30초 처럼 일정한 주기로 화면을 갱신해서 최신 데이터를 보여주는데
매번 Tempo 를 쿼리하게되면 그 비용은 고스란히 Tempo 가 구동중인 서버에 전가된다.

수집되는 트레이스가 많아질수록 그 비용도 비례해서 증가하니 초당 수십~수백건의 트레이스가 인입되는 시스템이라면 배보다 배꼽이 더 커질수도 있겠다

Tempo의 개별 트레이스에 대한 분석은 훌륭하지만 모니터링과 시각화는 매우 비효율적이다.

데이터는 이미 다 저장되어 있는데 꺼내지를 못한다니..
어떻게 해결해야 할까?


Metrics-Generator

OTel(오픈텔레메트리) 에서는 이런 문제를 해결하기 위해 Span Metrics Connector 라는 기능을 제공한다

트레이스를 쿼리해서 메트릭을 추출하는것이 아닌, 수집시점에 일련의 처리를 통해 메트릭을 추출하고 prometheus 같은 시계열 저장소에 별도로 전송하는 방법이다

Grafana 에서는 이미 시계열로 저장되어 있는 메트릭만 쿼리하면 되니 굳이 Tempo 를 직접 쿼리해야하는 부담도 줄어든다

Tempo도 마찬가지로 OTel 의 Span Metrics Connector 와 1:1 매칭되는 기능을 제공한다. (공식문서에서도 OTel 의 기능을 "Mirrored" 했다고 말한다)

Span metrics | Grafana Tempo documentation
The span metrics processor generates metrics from ingested tracing data, including request, error, and duration (RED) metrics.

Grafana Labs 는 이렇게 설명한다

You get out-of-the-box metrics from your tracing pipeline

귀찮은 작업 없이 트레이스의 메트릭을 바로 제공해준단다.

과연 얼마나 쉽게 추출해주는지 궁금하다. 바로 한번 사용해보자

Tempo 설정

앞선 블로그 글 "[Kubernetes] Grafana Tempo 를 구축해보자 - (1)"을 따라 Tempo 를 구축했다면 아래와 같은 옵션을 설정했을텐데
해당 설정으로 인해 Span Metrics 기능은 이미 활성화되어있다

tempo:
  metricsGenerator:
    # -- If true, enables Tempo's metrics generator (https://grafana.com/docs/tempo/next/metrics-generator/)
    enabled: true
    remoteWriteUrl: "http://prometheus-stack-kube-prom-prometheus.prometheus:9090/api/v1/write"

Grafana 에서 remoteWriteUrl 에 적힌 Prometheus 를 쿼리해보면 아래처럼 메트릭들이 이미 수집되고 있는것을 확인 할 수 있다

간단하게 "traces_spanmetrics_calls_total" 메트릭에서 irate 로 변화량을 쿼리해보면

이렇게 트레이스의 각종 속성들이 Prometheus labels 으로 정리되어서 깔끔하게 수집되고 있는것을 볼 수 있다

이 메트릭 외에도

  • Request / Error : traces_spanmetrics_calls_total
  • Duration : traces_spanmetrics_latency_sum

메트릭들 처럼 마이크로서비스의 핵심이 되는 R.E.D (Request / Error / Duration) 들이 차곡차곡 수집되고 있음을 볼 수 있다

심지어 request 처리시간 중 하위 5%, 1% 저럼 P95, P99 같은 메트릭을 위해 Bucket 처리 된 메트릭도 있어서 복잡한 설정 없이 히스토그램을 뽑아 볼 수도 있다

이렇게 트레이스와 메트릭이 잘 정리되서 수집되니 대시보드만 깔끔하게 만들면 마이크로서비스 모니터링도 별게 없어 보인다.

하지만 과연 그럴까?


Cardinality

위의 메트릭 중 traces_spanmetrics_calls_total 을 다시 쿼리해보자

수집된 메트릭의 Labels 를 유의해서 살펴보면

이미지처럼 label 에 다양한 속성들이 포함되어 있는것을 볼 수 있다

Span Metrics 는 기본적으로 수집되는 트레이스의 응답결과, URL, User-Agent 등 각종 속성들을 label 으로 첨부하는데 바로 여기서 문제가 발생한다

예를들어 Kubernetes 에 아래와 같은 형태의 어플리케이션을 배포했다고 가정해보자

  • 서비스 갯수 : 70 개
  • 서비스 당 URL 갯수 : 50 개
  • User-Agent 갯수 : 30개
  • Pod 갯수 : 10개

이 어플리케이션에 Span Metrics 을 이용해 메트릭을 수집한다면 prometheus 는 최대 70 * 50 * 30 * 10 = 1,050,000 개나 되는 고유한 메트릭들을 관리해야 한다

여기에 kubernetes 에서 많이 사용하는 워커노드, Pod IP 같은 label 이 추가된다고 하면 수천만 단위는 우스울 정도로 빠르게 증가한다

이렇게 고유한 메트릭들을 prometheus 에서는 "Active Time Series" 라고 부르는데 이걸 다른말로 Cardinality 라고 한다

Why does Prometheus use so much RAM? – Robust Perception | Prometheus Monitoring Experts

위 글에 따르면 대략 천만개 정도의 cardinality 를 가진 메트릭들을 처리하는데 30GB 정도의 메모리가 필요하다고 하니

무턱대고 Span Metrics 를 이용해 메트릭을 prometheus 에 저장하다간 의미있는 모니터링을 하기도 전에 메모리가 바닥날 수도 있겠다

또 높은 cardinality 는 메모리 사용량 뿐만 아니라 churn rate 증가, slow inserts 등 각종 머리아픈 부작용들을 유발한다


그럼 어떻게 하라고?!?

다행히도 Grafana Labs 팀도 이런 문제를 알고 있었는지 2023년 12월에 "Ad hoc" RED 메트릭을 릴리즈했다

공식 블로그에서는 prometheus 의 높은 cardinality 이슈를 직접 언급하며 "Aggregate by" 쿼리를 이용해서 Tempo 에서 직접 메트릭을 추출하는 방법을 제안한다

Traces to metrics: Perform ad hoc queries in Grafana Tempo
Looking to get more value out of tracing? We’ve added the ability to compute RED metrics from recent trace data grouped by any attribute of your choice.

2024년에 들어서 릴리즈 된 Grafana 11.3 이상 버전부터는 Explore Traces 메뉴를 통해 GUI 를 통해 트레이스 메트릭을 추출할 수 있는 방법을 제공하고

원한다면 직접 TraceQL 을 짜서 추출할 수도 있단다.

다음 글에서 TraceQL 의 Ad hoc 기능을 이용해서 직접 트레이스에서 메트릭을 추출해보자.