분산 트레이싱이 끊기는 자리 · 샘플링과 비용 · 이론
무엇을 버릴지 정하는 일
한 줄 요약
샘플링은 비용을 줄이는 장치가 아니라 무엇을 볼지 정하는 설계다. 잘못 정하면 정작 필요한 트레이스가 없다.
왜 이게 필요했나
트레이스는 요청마다 스팬 수십 개를 만든다. 초당 1,000 요청이면 하루에 수십억 스팬이다. 전부 저장하면 저장 비용이 애플리케이션 비용을 넘는다.
그런데 무작정 1% 만 남기면, 장애가 났을 때 그 요청의 트레이스가 없을 확률이 99% 다. 그래서 샘플링은 "얼마나 줄일까" 가 아니라 "무엇을 반드시 남길까" 로 접근한다.
세 가지 방식
| 방식 | 언제 정하나 | 장점 | 대가 |
|---|---|---|---|
| 헤드 | 요청 시작 시 | 싸고 단순, 전파가 쉽다 | 느린 요청만 고를 수 없다 |
| 테일 | 트레이스 완료 후 | 오류·느린 것만 정확히 고른다 | 스팬을 모아 둬야 해 비싸다 |
| 레이트 리미팅 | 초당 N개 상한 | 폭주해도 비용이 안 튄다 | 폭주 구간의 표본이 적어진다 |
실무 조합은 대개 이렇다.
1. 헤드에서 10~20% 로 기본선을 줄인다
2. 오류는 100% 강제한다(traceparent 의 샘플 플래그를 켜서 전파)
3. 중요한 엔드포인트만 테일 샘플링으로 느린 것을 건진다
테일 샘플링의 숨은 비용
컬렉터가 트레이스 전체를 메모리에 모아 두었다가 판단한다. 그래서 두 가지가 따라온다.
- 메모리: 대기 시간(보통 5~30초) × 초당 트레이스 수만큼 쌓인다
- 라우팅: 같은 트레이스의 스팬이 같은 컬렉터로 가야 한다. 그래서 컬렉터 앞에
trace_id기준 로드밸런서를 둔다. 안 하면 트레이스가 조각나 판단이 틀린다
이 두 가지를 모르고 테일 샘플링을 켜면 컬렉터가 OOM 으로 죽고, 죽는 동안의 트레이스는 통째로 사라진다.
흔한 착각
"샘플링 비율을 올리면 더 정확해진다" — 지표는 샘플링과 무관해야 한다. 요청 수·오류율·지연은 메트릭으로 재고, 트레이스는 "그 요청이 어디서 시간을 썼나" 를 보는 도구다. 트레이스로 비율을 계산하면 샘플링 편향이 그대로 들어간다.
"오류만 남기면 된다" — 오류가 아닌 느린 요청이 더 많은 정보를 준다. 오류는 로그에도 남지만, 정상 응답인데 3초 걸린 요청은 트레이스가 아니면 설명할 수 없다.
실무에서 진짜 중요한 것
샘플링 결정은 맨 앞에서 한 번 내리고 끝까지 전파돼야 한다. 중간 서비스가 자기 판단으로 다시 정하면 트레이스가 반쪽만 남는다.
traceparent 의 마지막 플래그(01/00)가 그 결정을 실어 나른다. 자동 계측은 이걸 지켜 주지만, 직접 HTTP 클라이언트를 만드는 코드가 헤더를 새로 쓰면 그 지점에서 결정이 초기화된다.