Jaeger·Tempo의 역할

1,857 단어·4 분·원문(.md)

Jaeger, Tempo는 msa 환경에서 하나의 사용자 요청이 여러 서비스들을 거쳐갈 때, 그 전체 경로와 흐름을 추적하고 기록하는 분산 추적 distributed tracing backend system이다.

Trace라는 개념은 사용자의 최초 요청부터 최종 응답까지의 전체 실행 궤적이다 (예시: 상품 결제 요청 전체)

Span이라는 개념은 Trace를 구성하는 개별 작업 단위다. 예를들어 인증서버 호출, db query 실행 이런것들. 각 span은 시작시간, 종료시간, 에러 여부, 태그등의 메타데이터를 갖는다.

Jaeger 예거라고 부르는데 제거가 아님 ㅋㅋ. Uber에서 개발한 오픈소스 추적 시스템으로 수집된 스팬들을 자체적인 데이터베이스 카산드라나 엘라스틱서치에 인덱싱해 특정 태그나 실행 시간으로 트레이스를 검색하는데 강력한 성능을 보여준다.

Tempo는 템포로 Grafana Labs에서 개발했고 비교적 최신 시스템이다 Jaeger와 달리 방대한 인덱스를 생성하지 않고 저렴한 오브젝트 스토리지에 트레이스를 통째로 압축 저장한다. 대신 로그나 메트릭에서 발췌한 trace_id를 통해 트레이스를 정확히 짚어내는 point-lookup 방식에 최적화 되어 압도적인 가성비와 확장성을 자랑한다.

Problem Definition #

당리 서버 환경과 달리 수십, 수백개의 마이크로서비스로 쪼개진 현대의 인프라에서 기존의 메트릭과 로그만으로 해결할 수 없는 가시성의 사각지대가 발생하는데

  • 요청 흐름의 파편화 context loss: 클라이언트가 api gateway를 호출하고 gateway가 a b c서비스를 순차/병렬적으로 호출할때 에러가 발생하면 어느 서비스 구간에서 터진건지 한눈에 파악할 수 없고 서버마다 남겨진 로그들은 서로 연결고리가 없어 파편화가 된다.
  • 지연시간 병목 구간 탐색 불가능: 결제 api가 5초나 걸려서 타임아웃이 났다는 메트릭 알람이 울리고 하지만, 5초중 인증 서버에서 1초, 재고 서버에서 3초, 결제나 pg사 연동에서는 1초가 걸렸다는 구간별 소요시간은 메트릭 시스템이 알려주지않는다. 엔지니어들은 각 시스템의 로그를 일일이 뒤지며 시간을 허비한다.
  • 책임 소재 불분명: 장애 발생시 서로 우리 서버 리소스는 정상이다라고 주장할수있다 db문제아닌가요? 같은 주장으로 각자 팀의 대시보드만 바라보게 되어 소통의 병목이 있을수있다.

Example #

정상적인 상황을 먼저 예시로 들어보면 모놀리식 환경에서 사용자가 주문 버튼을 누른다. 하나의 was 톰캣 서버에서 인증 재고확인 결제 db저장 모두 일어난다. 장애가나도 catalina.out 로그 파일 하나만 열어보면 아래로 실행 흐름 stack trace가 보이게 된다.

**장애를 유발하고 디버깅 미궁에 빠지는 상황 (분산추적부재)**을 생각해보자 블랙프라이데이이벤트로 사용주가 주문을 누른다. API Gateway -> Auth Service -> Order Service -> Inventory Service (Kafka 발행) -> DB 이 복잡한 과정중에 사용자는 500을 받는다.

  • Gateway log: Timeout wating for Order Service
  • OrderService log: Connection refused to Inventory Service
  • InvestoryService log: 에러 없음(요청 자체가 큐에 쌓여서 처리되지않음)

위의 상황처럼 로그만으로 어떤 특정 사용자의 request가 어느 노드를 거치다가 정확히 어디서 죽었는지 연결흐름을 그릴 수 없다. 수백만건의 에러로그가 발생하면 진짜 원인 root cause의 서비스를 찾기위해 시간을 쓸거고 골든타임을 날릴수가 있는것이다.

Solution #

모든 서비스의 코드에 OpenTelemetry(OTel)와 같은 표준 라이브러리를 적용하여 Instrumentation, HTTP Hedader를 통해 traceparant(trace id)를 다음 서비스로 전파 context propagation하게 만든다. 수집된 데이터는 Jaeger, Tempo로 전송된다.

애플리케이션 로그에서 trace id 추출 및 연관 분석 #

분산추적이 적용되면 모든 서버의 로그에 동일한 trace_id가 찍힌다. 운영 서버 터미널에서 에러로그를 발견했다면 해당 trace_id를 추출한다.

# Order 서비스 로그에서 특정 주문 실패 에러 검색하여 trace_id 확인
$ grep "Order failed" /var/log/order_service.log | tail -n 1
{"timestamp":"2026-03-02T21:35:10Z","level":"ERROR","message":"Order failed for item 99","trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7"}

추출한 traceid로 jaeger/tempo api 쿼리하여 병목 스팬 분석을 한다.

확인된 traceid 4bf92f3577b34da6a3ce929d0e0e4736 를 가지고 Jaeger api에 질의하여 어느 서비스에서 가장 오랜시간을 잡아먹었는지 혹은 에러를 뱉었는지 즉시 확인한다.

jq 도구를 이용해 소요 시간이 1초 이상 걸린 병목 구간만 필터링해보자 (마이크로초로 1,000,000)

# Jaeger API를 호출하여 해당 Trace ID를 구성하는 스팬 중 소요시간(duration)이 1초 이상인 것만 추출
$ curl -s "http://jaeger-query:16686/api/traces/4bf92f3577b34da6a3ce929d0e0e4736" | jq '.data[0].spans[] | select(.duration > 1000000) | {service: .processID, operation: .operationName, duration_ms: (.duration / 1000)}'
{
  "service": "p3",
  "operation": "HTTP GET /api/v1/inventory/check",
  "duration_ms": 5012.5
}
{
  "service": "p5",
  "operation": "SELECT * FROM inventory WHERE item_id = ?",
  "duration_ms": 4980.1
}

터미널 결과 한 번으로 5초 5012ms의 지연시간이 발생한 근본 원인이 inventory service에 p3가 호출한 데이터베이스 select query(p5)가 원인임을 파악할 수 있다.

발생한 락이나 슬로우쿼리로 예측해볼 수 있고 이를 개선해보는 방향으로 잡아갈 수 있게 되었다.

이처럼 jaeger, tempo는 막연한 추측과 부서간의 책임전가를 없애고 데이터를 기반으로 정확히 어느 코드, 어느 쿼리가 시스템 전체의 퍼포먼스를 갉아먹고 있는지

명확한 증거 flaeme graph 형태의 시각화 및 json 데이터를 제공하는 핵심 역할을 수행한다.

SRE/question/q_31.md