LabHub
배우기 러닝패스 코스

분산 트레이싱이 끊기는 자리 · 스팬 경계 — 어디서 끊을 것인가 · 실습

스팬이 하나면 아무것도 모르고, 서른 개면 아무도 안 본다

LabHub 에서 이어서 보기

목표

계측이 한 줄도 없는 주문 처리 코드에 스팬을 직접 넣으면서, 경계를 어디에 그을지 숫자로 정하는 법을 익힙니다. 계측 공백을 재고, 반복을 속성으로 접고, 그 판단을 규칙 파일로 적어 두 번째 핸들러에 그대로 적용합니다.

왜 중요한가

스팬을 하나만 두면 트레이스는 '211밀리초 걸렸다' 말고 아무 말도 하지 않는다. 반복문마다 스팬을 만들면 이번에는 같은 모양 막대가 서른 개 이어져 아무도 끝까지 보지 않는다. 두 실패는 같은 질문을 건너뛴 결과다 — 나중에 이 트레이스로 무엇을 물을 것인가. 그 질문에 답이 되는 자리를 찾는 도구가 계측 공백이다. 부모 구간 중 어느 자식도 덮지 않은 시간이 곧 '아직 모르는 시간' 이고, 그 자리가 다음 스팬을 그을 곳이다. 반대로 반복은 스팬이 아니라 횟수·총시간·최댓값으로 접어야 트레이스가 읽을 수 있는 크기로 남는다. 마지막으로 이 판단을 사람의 취향으로 두면 리뷰마다 다시 싸우게 되므로, 요청당 스팬 상한과 허용 공백을 파일로 적어 기계가 읽게 한다.

단계

1. /root/tp-boundary/01_root.py 를 만드세요. /opt/app/tracelab/dump.pyprovider 로 서비스 이름 shop-api 짜리 프로바이더를 만들고, 덤프 경로는 환경변수 TRACELAB_OUT 에서 먼저 읽되 없으면 /root/tp-boundary/01-root.jsonl 을 씁니다. POST /checkout 이라는 이름의 SERVER 스팬 하나 안에서 tracelab.tp_boundary.shopvalidateload_cartprice_of(전 상품) → chargewrite_receipt 를 차례로 부르고, 끝에 flush() 를 부릅니다. 그런 다음 /opt/otel-lab/bin/python 로 돌려 덤프를 만들고, /root/tp-boundary/01-root.txtspans=root_ms= 두 줄을 적으세요(덤프에서 읽은 값 그대로).
2. /root/tp-boundary/02_split.py 를 만드세요. 1단계와 같되, 데이터베이스 왕복 load_cartcart.load 스팬으로, 밖으로 나가는 chargepayment.charge 스팬으로 감쌉니다(자동 계측이 대신 만들어 주는 자리가 이 둘입니다). 덤프 기본 경로는 /root/tp-boundary/02-split.jsonl 입니다. 돌린 뒤 /root/tp-boundary/02-gap.txt 에 네 줄 root_ms= · covered_ms= · gap_ms= · gap_ratio= 을 적으세요. covered_ms 는 루트의 자식 구간을 합집합으로 더한 길이이고, gap_ratio 는 (루트 − 덮인 시간) ÷ 루트 를 소수 넷째 자리까지 적습니다.
3. /root/tp-boundary/03_close.py 를 만드세요. 2단계에서 남은 공백을 없애도록 validateorder.validate, 상품 값 조회 반복 전체는 price.lookup, write_receiptreceipt.write 스팬으로 감쌉니다. 스팬 이름은 이 다섯 개(order.validate·cart.load·price.lookup·payment.charge·receipt.write)와 루트 POST /checkout 로 정확히 맞추세요. 덤프 기본 경로는 /root/tp-boundary/03-close.jsonl 입니다. 돌린 뒤 /root/tp-boundary/03-gap.txt 에 2단계와 같은 네 줄을 적고, gap_ratio 가 0.05 미만이 되게 하세요.
4. /root/tp-boundary/04_peritem.py 를 만드세요. 3단계와 같되 price.lookup 안에서 상품 하나를 조회할 때마다 price.item 스팬을 하나씩 만듭니다. 덤프 기본 경로는 /root/tp-boundary/04-peritem.jsonl 입니다. 돌린 뒤 /root/tp-boundary/04-count.txt 에 세 줄 spans_per_request= · item_spans= · spans_per_1000_requests= 를 적으세요. 마지막 줄은 요청당 스팬 수에 1000 을 곱한 정수입니다.
5. /root/tp-boundary/05_fold.py 를 만드세요. price.item 스팬을 없애고, 대신 price.lookup 스팬에 세 속성 price.lookup.count(조회 횟수) · price.lookup.total_ms(합계 밀리초) · price.lookup.max_ms(가장 오래 걸린 한 건의 밀리초)를 붙입니다. 그리고 가장 오래 걸린 상품을 price.lookup.slowest 라는 이벤트로 남기되 이벤트 속성에 sku 를 넣으세요. 덤프 기본 경로는 /root/tp-boundary/05-fold.jsonl 이고, 전체 스팬 수는 8개를 넘지 않아야 합니다.
6. /root/tp-boundary/06_kind.py 를 만드세요. 5단계와 같되 모든 스팬에 SpanKind 를 명시합니다 — 들어온 요청은 SERVER, 프로세스 밖으로 나가는 호출(cart.load·payment.charge·receipt.write)은 CLIENT, 프로세스 안에서 나눈 구간(order.validate·price.lookup)은 INTERNAL 입니다. 덤프 기본 경로는 /root/tp-boundary/06-kind.jsonl 이고, 돌린 뒤 /root/tp-boundary/06-kinds.tsv 에 스팬마다 한 줄씩 <스팬이름><탭><kind> 를 시작 시각 순서로 적으세요(머리글 없이 여섯 줄).
7. /root/tp-boundary/budget.txt 에 세 줄을 적으세요 — max_spans_per_request=8, max_gap_ratio=0.10, root_kind=SERVER. 그리고 /root/tp-boundary/check_budget.py 를 만드세요. python3 check_budget.py <규칙파일> <덤프> 로 부르면 규칙을 어긴 것마다 VIOLATION <규칙키> <지금 값> 을 한 줄씩 찍고 종료 코드 1, 다 지켰으면 OK 로 시작하는 한 줄과 종료 코드 0 을 냅니다. 마지막으로 /root/tp-boundary/07-verdict.tsv 에 세 줄을 적으세요 — 각 줄은 <덤프파일이름><탭><pass|fail><탭><깨진 규칙키 또는 -> 이고, 02-split.jsonl · 04-peritem.jsonl · 06-kind.jsonl 을 이 순서로 검사한 결과입니다.
8. /root/tp-boundary/08_search.py 를 만들어 GET /search 핸들러를 처음부터 계측하세요. shop.parse_queryshop.search_index → 결과마다 shop.hydrateshop.render 순서로 부르고, 스팬은 루트 GET /search(SERVER) 아래 query.parse(INTERNAL) · index.search(CLIENT) · result.hydrate(INTERNAL) · response.render(INTERNAL) 네 개입니다. 반복은 접어서 result.hydrateresult.hydrate.count · result.hydrate.total_ms · result.hydrate.max_ms 를 속성으로 답니다. 덤프 기본 경로는 /root/tp-boundary/08-search.jsonl 이고, 7단계의 검사기를 이 덤프에 돌렸을 때 OK 가 나와야 합니다.

참고

단계 8개

  1. 스팬 하나짜리 트레이스로 시작한다
  2. 자동 계측이 주는 두 스팬만 넣고 공백을 잰다
  3. 공백이 큰 구간을 갈라 5% 아래로 내린다
  4. 반복마다 스팬을 만들면 몇 개가 되는지 세어 본다
  5. 반복을 스팬 대신 속성과 이벤트로 접는다
  6. SpanKind 로 경계를 표시한다
  7. 경계 규칙을 파일로 적고 검사기를 만든다
  8. 같은 규칙으로 두 번째 핸들러를 계측한다