포스트

트레이스 샘플링 - 헤드와 테일 샘플링의 선택 기준

시리즈 APM과 관측 가능성 9편 중 3편 APM과 관측 가능성
  1. 1 메트릭, 로그, 트레이스 - 무엇을 어디에 남기고 카디널리티는 어디서 터지는가
  2. 2 RED와 USE - 어디에 무엇을 붙이는가
  3. 3 트레이스 샘플링 - 헤드와 테일 샘플링의 선택 기준
  4. 4 APM 도구 비교: Datadog부터 Pinpoint, OpenTelemetry까지
  5. 5 APM 용어 정리: Observability, Telemetry부터 Span, Exemplar까지
  6. 6 빅테크는 APM을 사지 않고 만들었다: Dapper에서 OpenTelemetry 졸업까지
  7. 7 APM 도구 여섯 개를 직접 붙여 봤다: 에이전트 비용은 날마다 9%와 19% 사이였다
  8. 8 빈 화면의 원인은 매번 달랐다: APM 여섯 개를 띄우며 막힌 자리들
  9. 9 Datadog 대안 10선을 전부 꽂아 봤다: 셋은 떴고 넷은 키만 남았고 둘은 축이 달랐다

분산 추적을 켜면 곧 비용 문제를 만난다. 요청 하나가 서비스 다섯 개를 지나면 스팬(트레이스를 이루는 작업 구간 하나)이 수십 개 생기고, 초당 수천 요청이면 저장해야 할 양이 로그를 넘어선다. 그래서 샘플링을 켜는데, 무엇을 버릴지 정하는 방식에 따라 정작 필요한 트레이스가 남기도 하고 사라지기도 한다.


샘플링이 필요한 이유

트레이스의 비용은 세 곳에 있다.

  • 애플리케이션: 스팬 생성과 컨텍스트 전파. 크지 않지만 0은 아니다.
  • 네트워크: 수집기로 보내는 양.
  • 저장소: 보존 기간 × 양. 대개 여기가 가장 크다.

전부 저장하는 선택지가 있고, 규모가 작으면 그것이 가장 단순하다. 샘플링은 규모가 커진 뒤의 문제다.


헤드 샘플링: 시작할 때 정한다

요청이 시작되는 지점에서 “이 트레이스를 남길지”를 정하고, 그 결정을 전파한다. OpenTelemetry 문서는 헤드 샘플링을 결정을 가능한 한 일찍 내리는 방식으로 정의한다(OpenTelemetry: Sampling). 결정은 W3C traceparent 헤더의 sampled 플래그에 실려 가고, 이 플래그는 호출한 쪽이 트레이스 데이터를 기록했을 수 있다는 뜻이다(W3C Trace Context). 하위 서비스는 그 결정을 따른다.

  • 장점: 결정이 즉시 내려지므로 버릴 스팬은 아예 만들지 않는다. 비용이 가장 적고 구현이 단순하다.
  • 단점: 무엇이 흥미로운지 모르는 시점에 정한다. 1%를 남긴다면 드물게 나는 오류 트레이스가 남을 확률도 1%다.

확률 기반이 기본이고(TraceIdRatioBased), 그 위에 규칙을 얹는 변형이 흔하다. 예를 들어 /health는 0%, 결제 경로는 100%, 나머지는 1%처럼 경로별로 다르게 둔다. 이렇게 차등을 두면 중요한 경로의 트레이스는 비율과 무관하게 남으므로, 헤드 샘플링의 단점이 그만큼 줄어든다.


테일 샘플링: 끝나고 나서 정한다

수집기가 트레이스의 모든 스팬을 일단 받아 버퍼에 모으고, 완료된 뒤 조건을 보고 남길지 정한다. OpenTelemetry 문서의 정의도 트레이스 안의 스팬 전부 또는 대부분을 보고 결정하는 방식이다.

  • 오류가 있으면 남긴다.
  • 지연이 임계값을 넘으면 남긴다.
  • 특정 속성(중요 고객, 결제 경로)이면 남긴다.
  • 나머지는 확률적으로.

필요한 것을 정확히 남길 수 있다는 것이 테일 샘플링을 쓰는 이유다. 드문 오류를 조사할 때 “그 트레이스가 샘플링에서 빠졌다”는 상황이 생기지 않는다.

그 대신 대가가 크다.

  • 모든 스팬을 일단 전송하고 버퍼링해야 한다. 그래서 애플리케이션과 수집기 사이의 네트워크 비용은 줄지 않는다.
  • 수집기가 상태를 갖는다. OpenTelemetry 문서도 테일 샘플링을 구현하는 구성 요소는 “must be stateful systems that can accept and store a large amount of data”(많은 데이터를 받아 저장할 수 있는 상태 있는 시스템이어야 한다)라고 적는다. 트레이스 단위로 모아야 하므로, 같은 트레이스의 스팬이 같은 수집기 인스턴스로 가야 한다. 로드 밸런싱을 트레이스 ID 기준으로 해야 하고, 수집기가 확장의 병목이 된다.
  • 완료 판정이 어렵다. 트레이스가 끝났는지 알 방법이 없으므로 타임아웃으로 결정한다. 그 시간만큼 메모리를 쓰고, 늦게 오는 스팬은 놓친다.

고르는 기준

상황선택
규모가 작다샘플링 없음. 가장 단순하다
비용만 줄이면 된다헤드 샘플링 + 경로별 차등
드문 오류 조사가 목적이다테일 샘플링
둘 다헤드에서 1차로 줄이고 테일에서 선별

실무에서 가장 흔한 것은 마지막이다. 헤드에서 명백히 불필요한 것(헬스 체크, 정적 리소스)을 걷어내고, 나머지를 테일에서 선별한다.

샘플링과 무관하게 메트릭은 전부 집계해야 한다. 트레이스를 1% 남기더라도 오류율과 지연 분포는 100% 기준으로 계산해야 한다. OpenTelemetry의 span metrics처럼 샘플링 전에 메트릭을 뽑는 구성이 이 때문에 쓰인다. 샘플링된 트레이스로 비율을 계산하면 틀린다.


이 설명이 깨지는 곳

  • 샘플링 결정은 전파되어야 한다. 중간 서비스가 컨텍스트를 잃으면 트레이스가 쪼개진다. 비동기 경계(스레드 풀, 메시지 큐)에서 자주 끊긴다.
  • 트레이스가 없어도 로그는 있다. trace_id를 로그에 남겨 두면 샘플링에서 빠진 요청도 로그로 추적할 수 있다. 둘을 함께 설계해야 하는 이유다(메트릭·로그·트레이스).
  • 1%가 얼마인지는 트래픽에 달렸다. 초당 10건이면 1%는 6분에 4건이고, 그것으로는 아무것도 조사할 수 없다. 낮은 트래픽 서비스는 샘플링하지 않는 편이 낫다.
  • 속성에 개인정보를 넣지 않는다. 트레이스는 보존 기간이 길고 여러 시스템을 지난다.

무엇을 재면 확인되는가

  1. 샘플링 비율을 바꿔 가며 “특정 오류의 트레이스를 찾을 확률”을 센다. 조사 가능성이 숫자로 나온다.
  2. 테일 샘플링 수집기의 메모리 사용량을 트레이스 수와 버퍼 타임아웃의 함수로 본다.
  3. 샘플링된 트레이스로 계산한 오류율과 전량 메트릭의 오류율을 비교한다. 차이가 나면 전자를 지표로 쓰고 있었다는 뜻이다.

실무와의 접점

monticker의 OpenTelemetry 도입에서 sampling.probability: 1.0을 개발 환경 값으로 두고 “운영에서는 0.1 이하로 줄여야 한다”고 적었다. 지금 보면 그 문장에는 “무엇을 남길 것인가”가 빠져 있다. 비율만 줄이면 조사에 필요한 것도 같은 비율로 사라진다. 운영으로 가져간다면 헤드에서 헬스 체크를 걷어내고, 오류와 느린 요청은 테일에서 남기는 구성이 필요하다.


정리

  • 흔한 구성은 헤드에서 헬스 체크 같은 명백한 잡음을 걷어내고, 오류와 느린 요청은 테일에서 남기는 것이다.
  • 오류율과 지연 분포는 샘플링 전 전량 메트릭으로 계산한다.
  • 초당 10건 수준의 서비스는 샘플링하지 않는 편이 낫다.
참고한 자료외부 출처 3

외부 출처

장애 대응과 관측 아키텍처와 마이그레이션
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다