트레이스
고친 사람 github-actions[bot]
트레이스는 요청 하나가 시스템을 지나간 길을 처음부터 끝까지 이어 붙여 남기는 기록입니다. 요청이 여러 서비스를 거치면 어디서 시간이 걸렸고 어디서 실패했는지가 서비스마다 흩어집니다. 트레이스는 그 조각을 한 줄기로 묶어 보여 줍니다. 개발 현장에서는 스택 트레이스나 실행 추적도 트레이스라고 부릅니다. 이 항목은 관측성에서 말하는 트레이스를 다룹니다. 관측성은 돌아가는 시스템의 속사정을 밖에서 들여다볼 수 있도록 기록을 남기고 살피는 일입니다.
쉽고 빠른 이해
트레이스는 요청 하나의 여행 기록입니다. 주문 요청이 주문 서비스, 재고 서비스, 결제 서비스를 차례로 들렀다면 그 전부가 기록 한 벌에 담깁니다.
이게 없으면 서비스마다 따로 남긴 로그를 사람이 손으로 맞춰 봐야 합니다. 동시에 요청 수천 개가 섞여 흐르니 어느 줄이 같은 요청인지 가려내기 어렵습니다.
어떻게 도는가:
- 요청이 처음 들어올 때 이 요청만의 번호를 하나 붙입니다
- 서비스가 다음 서비스를 부를 때 그 번호를 요청에 실어 넘깁니다
- 서비스마다 「내가 맡은 일이 언제 시작해 얼마나 걸렸나」를 번호와 함께 남깁니다
- 나중에 번호로 모아 한 그림으로 봅니다
대가도 있습니다. 모든 요청을 남기면 저장할 양이 크게 불어나서 대개 일부만 골라 남깁니다. 번호를 넘기지 않는 서비스가 하나라도 끼면 기록이 거기서 끊깁니다.
트레이스는 요청이 서비스 여러 개를 거칠 때 값이 큽니다. 요청이 한 프로세스 안에서 끝나는 시스템이라면 로그로도 대개 충분합니다.
상세
택배 송장 조회를 떠올려 봅시다. 송장 번호 하나로 조회하면 물건이 어느 터미널에 언제 들어가 언제 나왔는지가 한 화면에 이어집니다.
트레이스는 요청 하나가 시스템 안에서 거친 작업들을 한데 모은 기록입니다. 서비스를 여러 개로 나눈 마이크로서비스 구조에서 요청 하나가 느려졌다고 해 봅시다. 사용자는 응답이 늦다는 것만 압니다. 트레이스를 열면 그 늦음이 요청을 맨 처음 받아 안쪽으로 넘기는 게이트웨이, 주문 서비스, 데이터베이스 조회 가운데 어디에서 생겼는지가 보입니다.
스팬
트레이스를 이루는 조각을 스팬이라고 부릅니다. 스팬은 「한 가지 일을 한 번 한 것」의 기록입니다. HTTP(HyperText Transfer Protocol) 요청 하나를 처리한 것, 데이터베이스 쿼리 하나를 보낸 것이 각각 스팬 하나가 됩니다.
스팬 하나에는 대개 이런 것이 담깁니다.
| 담는 것 | 무엇인가 |
|---|---|
| 이름 | 무슨 일이었나. GET /orders 나 재고 조회 같은 이름 |
| 시작 시각과 걸린 시간 | 언제 시작해 얼마나 걸렸나 |
| 트레이스 ID | 이 스팬이 어느 요청의 기록에 속하나 |
| 스팬 ID | 이 스팬 자신의 번호 |
| 부모 스팬 ID | 이 일을 시킨 바로 위 스팬의 번호 |
| 속성 | 응답 코드, 호출한 주소 같은 덧붙임 정보 |
| 상태 | 성공했나 실패했나 |
표에서 뼈대를 이루는 것은 번호 셋입니다. 트레이스 ID 는 같은 요청에 속한 스팬이 모두 똑같이 나눠 가집니다. 스팬 ID 는 스팬마다 따로 붙어서 자식 스팬이 부모를 가리킬 때 쓰는 대상이 됩니다. 부모 스팬 ID 는 그 번호를 적어 누가 누구를 불렀는지를 잇습니다. 이 셋만 있으면 흩어진 스팬을 모아 한 요청의 모양을 다시 세울 수 있습니다.
스팬이 이루는 나무
부모가 없는 첫 스팬을 루트 스팬이라고 합니다. 요청이 시스템에 처음 들어온 지점에서 생깁니다. 나머지 스팬은 모두 부모를 하나씩 가리키므로 스팬들은 루트에서 뻗어 나가는 나무 모양이 됩니다.
주문 요청 하나를 예로 들면 이런 나무가 됩니다. 주문 서비스가 재고 서비스와 결제 서비스를 부릅니다. 재고 서비스는 다시 데이터베이스를 조회합니다.
flowchart TD
R["루트 스팬 · 게이트웨이 POST /orders"]
R --> O["주문 서비스 · 주문 처리"]
O --> I["재고 서비스 · 재고 확인"]
O --> P["결제 서비스 · 결제 요청"]
I --> D["데이터베이스 · 재고 조회 쿼리"]
같은 나무를 시간 축에 눕혀 보면 어디가 느렸는지가 드러납니다. 아래는 설명을 위해 만든 가상의 값입니다.
| 스팬 | 시작 | 걸린 시간 |
|---|---|---|
| 게이트웨이 POST /orders | 0ms | 480ms |
| 주문 서비스 · 주문 처리 | 5ms | 470ms |
| 재고 서비스 · 재고 확인 | 10ms | 60ms |
| 데이터베이스 · 재고 조회 쿼리 | 15ms | 40ms |
| 결제 서비스 · 결제 요청 | 75ms | 390ms |
전체 480ms 가운데 390ms 를 결제 요청이 차지합니다. 재고 쪽은 60ms 로 끝났습니다. 로그만 봤다면 서비스 넷의 기록을 시각으로 맞춰 봐야 알 수 있는 사실입니다. 트레이스 도구는 이 표를 막대그래프처럼 겹쳐 그린 화면으로 보여 줍니다. 이 화면을 흔히 폭포 그림이라고 부릅니다.
컨텍스트 전파
스팬을 나무로 이으려면 서비스가 다음 서비스를 부를 때 트레이스 ID 와 자기 스팬 ID 를 넘겨야 합니다. 이 일을 컨텍스트 전파라고 부릅니다. 받는 쪽은 넘어온 트레이스 ID 를 그대로 씁니다. 넘어온 스팬 ID 는 자기 스팬의 부모로 적습니다.
sequenceDiagram
participant 게이트웨이
participant 주문
participant 결제
게이트웨이->>주문: 요청 + 트레이스 ID + 스팬 ID
Note over 주문: 같은 트레이스 ID 로<br/>새 스팬을 연다
주문->>결제: 요청 + 트레이스 ID + 주문의 스팬 ID
Note over 결제: 부모는 주문의 스팬이다
결제-->>주문: 응답
주문-->>게이트웨이: 응답
HTTP 로 부를 때는 이 번호들을 요청 헤더에 싣습니다. 서비스마다 다른 도구를 써도 서로 읽을 수 있도록 헤더 모양을 정한 표준이 W3C(World Wide Web Consortium, 웹 표준을 정하는 단체) 의 Trace Context 입니다.
이 표준은 traceparent 라는 헤더 하나에 번호를 담습니다.
traceparent: 00-a0892f3577b34da6a3ce929d0e0e4736-f03067aa0ba902b7-01
위 헤더는 줄표로 나뉜 네 토막으로 되어 있습니다.
| 토막 | 값 | 뜻 |
|---|---|---|
| 버전 | 00 |
헤더 형식의 판. 16진수 두 글자 |
| 트레이스 ID | a0892f35…0e4736 |
요청 전체의 번호. 16진수 32글자 |
| 부모 ID | f03067aa0ba902b7 |
이 요청을 보낸 쪽 스팬의 번호. 보내는 쪽에서는 자기 스팬 ID, 받는 쪽에서는 부모 스팬 ID 가 되는 값이다. 16진수 16글자 |
| 플래그 | 01 |
이 요청을 기록으로 남기기로 했나. 요청은 일부만 골라 남긴다(아래 샘플링) |
샘플링
모든 요청의 스팬을 남기면 저장할 양이 요청 수에 비례해 불어납니다. 그래서 요청 가운데 일부만 골라 남깁니다. 이것을 샘플링이라고 합니다.
고르는 시점은 크게 둘입니다. 요청이 처음 들어올 때 정하는 방식은 가볍습니다. 대신 나중에 실패하거나 느려진 요청을 놓칠 수 있습니다. 요청이 다 끝난 뒤 스팬을 모아 보고 정하는 방식은 느린 요청과 실패한 요청을 골라 남길 수 있습니다. 대신 끝날 때까지 스팬을 전부 들고 있어야 합니다.
처음에 정한 결정은 뒤따르는 서비스에도 같이 넘어가야 합니다. 앞 서비스는 남기고 뒤 서비스는 버리면 나무가 중간에 잘리기 때문입니다. 위 헤더의 플래그 토막이 이 결정을 싣고 다닙니다.
로그 · 메트릭과 갈리는 대목
트레이스는 로그, 메트릭과 함께 텔레메트리 데이터로 묶여 불립니다. 텔레메트리는 시스템이 돌아가면서 스스로 내보내는 관찰 기록을 통틀어 부르는 말입니다. 셋은 같은 시스템을 보되 답하는 질문이 다릅니다.
| 무엇을 남기나 | 답하는 질문 | |
|---|---|---|
| 로그 | 그때 일어난 일 한 건 | 그 순간 무슨 일이 있었나 |
| 메트릭 | 시간에 따라 모은 숫자 | 전체적으로 얼마나 자주, 얼마나 느린가 |
| 트레이스 | 요청 하나가 거친 길 | 이 요청은 어디를 거쳐 어디서 막혔나 |
셋은 함께 쓸 때 힘이 커집니다. 메트릭으로 응답이 느려진 시각을 찾고, 그 시각의 트레이스로 느린 구간을 찾습니다. 로그 한 줄마다 트레이스 ID 를 같이 적어 두면 그 구간에서 무슨 일이 있었는지 로그로 바로 넘어갈 수 있습니다.
트레이스를 쓸 때 드는 비용
트레이스를 남기려면 코드가 스팬을 열고 닫아야 합니다. 이렇게 신호를 내보내도록 코드를 심는 일을 계측이라고 부릅니다. 계측한 만큼 요청마다 조금씩 일이 늘어납니다.
모은 스팬을 받아 두는 곳도 따로 있어야 합니다. 서비스들이 보낸 스팬을 모아 저장하고 화면으로 보여 주는 서버를 트레이스 백엔드라고 부릅니다. 내 애플리케이션 서버와는 별개로 띄우는 서버입니다.
기록은 가장 약한 고리에서 끊깁니다. 헤더를 넘기지 않는 서비스가 하나 끼면 그 뒤의 스팬은 새 트레이스로 시작해 버립니다. 메시지 큐를 거쳐 나중에 처리되는 일도 그렇습니다. 메시지에 번호를 따로 실어 보내지 않으면 보낸 쪽과 받은 쪽이 이어지지 않습니다.
서비스가 하나뿐인 시스템에서는 트레이스가 주는 것이 줄어듭니다. 요청이 한 프로세스 안에서 끝나면 로그와 프로파일러로도 느린 곳을 찾을 수 있습니다. 트레이스는 요청이 경계를 여러 번 넘을수록 값이 커집니다.
관련 항목
트레이스를 이루는 구성 요소
스팬 · 루트 스팬 · 트레이스 ID · 스팬 ID · 스팬 속성 · 스팬 이벤트 · 스팬 링크
트레이스와 함께 쓰이는 텔레메트리 신호
트레이스를 이어 붙이는 전파 수단
컨텍스트 전파 · Trace Context · traceparent · tracestate · 배기지 · B3 전파
트레이스를 만들고 모으는 도구와 표준
OpenTelemetry · Jaeger · Zipkin · OpenTracing · OpenCensus · Dapper
트레이스를 남기고 줄이는 처리 단계
계측 · 자동 계측 · 샘플링 · 헤드 샘플링 · 테일 샘플링 · 익스포터 · 수집기
트레이스가 속하는 상위 분류
트레이스로 찾는 문제
지연 · 병목 · 연쇄 장애 · 타임아웃 · N+1 문제
트레이스를 가리키는 다른 이름과 헷갈리는 이웃
분산 추적 · 스택 트레이스 · strace · 실행 추적 · 감사 로그
트레이스가 거치는 시스템 구조
다른 이름: trace · 추적