Foundry
성능 최적화
중급
핵심

APM과 분산 트레이싱

여러 서비스를 거치는 요청에서 느린 한 구간 찾기

APM과 분산 트레이싱

구조

Trace (요청 1건)
+- span: gateway        120ms
   +- span: order-svc    95ms
      +- span: SELECT     60ms  <- 병목
      +- span: payment    25ms
   +- span: cache GET      2ms
  • span은 하나의 작업 구간(이름, 시작과 종료 시각, 속성, 상태)
  • trace id와 span id를 요청 헤더로 전파해 서비스 경계를 넘어 하나의 흐름으로 잇는다

실무 포인트

  • 컨텍스트 전파가 끊기면 트레이스가 조각난다. 스레드 풀, 비동기 작업, 메시지 큐 경계에서 자주 끊긴다
  • 전량 수집은 비용이 크다. 확률 표본을 기본으로 하고 오류와 느린 요청은 강제로 수집하는 구성이 일반적이다
  • span 속성에 토큰이나 개인정보를 넣지 않는다. 트레이스는 넓게 공유된다
  • DB 호출은 쿼리 단위로 span을 남긴다. 대부분의 병목이 여기서 드러난다
  • 계측 자체도 오버헤드다. 초당 호출이 매우 많은 구간에 세밀한 span을 남기면 그 자체로 느려진다
면접에서 이렇게 나옵니다

Q.분산 트레이싱은 어떻게 여러 서비스의 요청을 하나로 잇나요?

요청마다 식별자를 만들어 호출을 따라 계속 전달합니다.

요소역할
trace id하나의 요청 전체를 식별한다. 끝까지 같은 값
span id구간 하나를 식별한다. 서비스마다 새로 생긴다
parent span id누가 나를 불렀는지. 이것으로 트리가 만들어진다

전달은 HTTP 헤더로 합니다. A 가 B 를 부를 때 헤더에 trace id 와 자기 span id 를 넣고, B 는 그것을 부모로 삼아 새 span 을 만듭니다. 각 서비스가 자기 span 만 보고하면 수집기가 trace id 로 묶어 트리를 복원합니다.

얻는 것내용
구간별 시간어느 서비스, 어느 호출에서 시간을 쓰는가
호출 관계실제로 무엇이 무엇을 부르는가
병렬과 직렬순차 호출을 병렬로 바꿀 여지가 보인다

흔한 실수: 각 서비스의 응답 시간 지표만으로 대체할 수 있다고 보는 것. 서비스별 평균은 알 수 있지만 한 요청 안에서 무엇을 기다렸는지는 알 수 없습니다. 순차 호출 10개가 각각 100밀리초인 것과, 하나가 1초인 것은 지표상 같아 보입니다.

Q.트레이스가 중간에서 끊기는 원인은 무엇인가요?

어딘가에서 문맥 전달이 끊긴 것입니다.

원인내용
헤더 전파 누락직접 만든 HTTP 클라이언트가 헤더를 복사하지 않는다
비동기 경계스레드나 이벤트 큐를 건너면서 문맥이 사라진다
메시지 큐발행할 때 메시지 헤더에 넣지 않으면 소비자 쪽에서 새 트레이스가 시작된다
계측 없는 구간에이전트가 지원하지 않는 라이브러리
프록시나 게이트웨이헤더를 지우거나 새로 만든다
표준 불일치서비스마다 다른 헤더 형식을 쓴다

비동기가 가장 흔합니다. 대부분의 계측이 스레드에 문맥을 붙여 두는데, 다른 스레드로 넘어가면 그 연결이 끊깁니다. 스레드풀에 작업을 넘길 때 문맥을 함께 전달하도록 감싸야 합니다.

메시지 큐도 자주 놓칩니다. 발행자와 소비자가 별개 트레이스로 보이면 비동기 흐름의 전체 시간을 알 수 없습니다.

흔한 실수: 헤더 형식을 통일하지 않는 것. 한 서비스가 다른 형식을 쓰면 그 지점에서 새 trace id 가 생겨 앞뒤가 분리됩니다.

Q.트레이스를 전량 수집하지 않는다면 어떤 기준으로 샘플링하나요?

문제 있는 요청은 전부, 정상 요청은 일부만 남깁니다.

방식판단 시점특징
앞단 샘플링요청 시작 시 무작위로 결정싸다. 느린 요청을 놓칠 수 있다
뒷단 샘플링요청이 끝난 뒤 결과를 보고 결정오류와 느린 것을 다 잡는다. 수집기가 일단 다 받아야 한다
규칙 기반엔드포인트나 사용자별로 비율을 다르게중요한 경로를 두껍게 본다
하한 보장드문 엔드포인트는 최소 몇 건 보장호출이 적은 경로가 사라지지 않게

앞단만 쓰면 1% 샘플링에서 느린 요청 대부분이 버려집니다. 그래서 뒷단 샘플링이나, 앞단에 오류와 지연 초과 시 강제 수집 규칙을 더합니다.

일관성도 중요합니다. 한 요청의 서비스들이 같은 결정을 내려야 트레이스가 온전합니다. 그래서 첫 서비스가 결정하고 그 값을 함께 전파합니다.

흔한 실수: 균일 샘플링만 두고 p99 를 조사하려 하는 것. 드문 사건을 균일하게 버리면 조사할 표본이 남지 않습니다.

Q.APM을 도입했을 때 성능 오버헤드는 어떻게 관리하나요?

계측에도 비용이 들어서 어디에 얼마나 붙일지를 정해야 합니다.

비용이 드는 자리내용
span 생성구간마다 시각 측정과 객체 생성
문맥 전파스레드 지역 저장소 접근
전송수집기로 보내는 네트워크와 직렬화
메모리전송 전 버퍼

관리 방법입니다.

방법내용
샘플링가장 효과가 크다. 수집량을 직접 줄인다
계측 범위 제한서비스 경계와 외부 호출만. 내부 함수마다 붙이지 않는다
비동기 전송요청 경로에서 전송을 분리한다. 버퍼에 넣고 뒤에서 보낸다
태그 절제span 에 붙이는 속성이 많으면 전송량이 는다
뜨거운 경로 제외초당 수십만 번 불리는 함수는 계측하지 않는다

두 번째가 흔한 실수의 반대입니다. 모든 함수에 span 을 만들면 트레이스가 수백 개의 span 이 되어 읽기도 어렵고 오버헤드도 커집니다. 서비스 경계, DB, 외부 호출, 큐 정도가 적당합니다.

흔한 실수: 오버헤드를 평균 응답 시간으로만 확인하는 것. 계측은 요청당 고정 비용에 가까워, 짧은 요청의 비율에서 훨씬 크게 드러납니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

성능 최적화 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.