traceparent一行の構造
한국어 원문으로 표시합니다.
한 줄 요약
분산 트레이싱의 전부는 traceparent 헤더를 다음 호출로 넘기는 것이다. 자동 계측이 그걸 대신해 주지만, 코드가 요청 객체를 새로 만드는 순간 끊긴다.
왜 이게 필요했나
"Istio 를 깔았으니 트레이싱이 된다" 는 말이 흔한데 틀렸다. 사이드카는 자기가 본 요청에 대해 스팬을 만들 수 있지만, A 서비스가 받은 요청과 A 가 B 로 보낸 요청이 같은 트레이스라는 것을 사이드카는 모른다. 그걸 잇는 것은 애플리케이션이다.
W3C 표준 헤더는 이렇게 생겼다.
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
^^ ^------------ trace-id ---------^ ^-- span-id --^ ^^
버전 32자리(트레이스 전체) 16자리(이 구간) 플래그
마지막 01 이 샘플링 플래그다. 1이면 "이 트레이스는 기록한다", 0이면 "기록하지 않는다". 이 결정은 맨 앞에서 한 번 내리고 뒤로 전파된다 — 그래야 트레이스가 반쪽만 남는 일이 없다.
어떻게 동작하나
서비스 A 가 B 를 부를 때 해야 하는 일은 딱 하나다.
- 들어온 요청의
traceparent를 읽는다 - 새 span-id 를 만들고, trace-id 는 그대로 두고, 나가는 요청에 붙인다
자동 계측(auto-instrumentation)이 이걸 대신한다. 그런데 다음 경우에 끊긴다.
| 끊기는 자리 | 왜 |
|---|---|
| 새 HTTP 클라이언트를 코드에서 직접 만듦 | 계측 라이브러리가 감싸지 않은 경로 |
| 큐·메시지로 넘김 | HTTP 헤더가 없다 — 메시지 속성에 직접 넣어야 한다 |
| 스레드/코루틴을 새로 띄움 | 컨텍스트가 스레드 로컬이라 안 따라간다 |
| 배치 처리 | 요청 하나 : 트레이스 하나가 아니다 |
특히 큐가 흔하다. Kafka 로 넘길 때는 메시지 헤더에 traceparent 를 담고, 컨슈머가 그걸 꺼내 컨텍스트를 복원해야 한다. 안 하면 프로듀서 쪽 트레이스와 컨슈머 쪽 트레이스가 서로 남남이 된다.
샘플링
전부 기록하면 비싸다. 그래서 샘플링한다. 방식이 둘이다.
헤드 샘플링 — 맨 앞에서 "이건 기록" 을 정한다. 싸고 단순하지만, 느린 요청만 골라 볼 수 없다. 시작할 때는 그게 느릴지 모르기 때문이다.
테일 샘플링 — 트레이스가 끝난 뒤 판단한다. "오류가 있거나 1초 넘은 것만 저장". 원하는 것을 정확히 고르지만, 컬렉터가 트레이스 전체를 메모리에 모아 두었다가 판단해야 해서 비싸고, 같은 트레이스의 스팬이 같은 컬렉터로 가도록 라우팅해야 한다.
실무 조합은 대개 이렇다 — 헤드에서 10~20% 로 줄이고, 오류는 100% 강제하고, 중요한 엔드포인트만 테일 샘플링을 건다.
흔한 착각
"스팬이 많을수록 좋다" — 함수마다 스팬을 만들면 트레이스 하나가 수천 스팬이 되고, UI 에서 읽을 수 없고, 저장 비용이 폭발한다. 스팬은 네트워크 경계와 느린 작업에 둔다.
속성에 개인정보를 넣는 것. 스팬 속성은 그대로 저장되고 검색된다. 사용자 이메일이나 토큰을 넣으면 그게 트레이싱 백엔드에 평문으로 쌓인다.
스팬에 무엇을 적을 것인가
스팬은 이름과 시간만으로는 조사에 별 도움이 안 된다. 그 스팬이 무엇을 했는지 구별할 수 있는 속성이 붙어 있어야 느린 것들 사이의 공통점을 찾을 수 있다. 다만 아무거나 넣으면 저장 비용과 개인정보 문제가 함께 오므로, 기준이 필요하다.
넣을 만한 것은 대체로 그 값으로 결과를 갈라 볼 수 있는 것이다.
- 호출 대상(서비스 이름, 정규화한 경로, 데이터베이스 이름)
- 결과(상태 코드, 오류 종류)
- 규모(가져온 행 수, 본문 크기, 배치 크기)
- 조건(캐시 적중 여부, 어떤 경로를 탔는지, 재시도 횟수)
규모와 조건이 특히 값집니다. "이 질의가 느리다" 보다 "이 질의가 결과 행이 만 건일 때만 느리다" 가 훨씬 빨리 원인에 닿고, 그 구분은 행 수를 속성으로 적어 두어야만 가능합니다.
반대로 넣지 말아야 할 것도 분명하다. 개인정보와 자격 증명은 물론이고, 값의 종류가 사실상 무한한 것도 조심해야 한다. 트레이싱 백엔드는 속성으로 검색할 수 있게 색인을 만들므로, 요청 본문을 통째로 넣거나 정규화하지 않은 경로를 넣으면 색인이 폭발한다. 앞에서 지표의 라벨에 대해 말한 것과 같은 이야기다.
오류를 기록하는 방식도 정해 두면 좋다. 스팬에는 성공·실패를 나타내는 상태가 따로 있으므로 그것을 정확히 설정하는 것이 먼저다. 이 값이 없으면 "실패한 트레이스만 보기" 가 동작하지 않는다. 예외의 내용은 스팬 이벤트로 남기되, 스택 트레이스 전체를 넣을지는 저장 비용과 함께 판단한다. 대개는 오류 종류와 메시지 한 줄이면 충분하고, 자세한 것은 로그에 있으면 된다. 앞에서 말한 대로 로그와 트레이스가 이어져 있다면 그쪽으로 건너가면 되기 때문이다.
실무에서 진짜 중요한 것
트레이스와 로그를 잇는 것이 실제 효용의 대부분이다. 로그 한 줄에 trace_id 를 넣어 두면, 느린 트레이스를 발견했을 때 그 요청이 남긴 로그를 한 번에 찾을 수 있다. 이 연결이 없으면 트레이싱은 예쁜 그림에 그친다.