기준: DVA-C02 시험 가이드오류 제보
모놀리식 애플리케이션에서 "왜 느린가"는 프로파일러 하나로 답할 수 있었다. 한 프로세스 안에서 함수 호출 스택을 따라 내려가며 어느 함수가 시간을 잡아먹는지 보면 됐다. 그런데 요청 하나가 API Gateway → Lambda → DynamoDB → 외부 API → ElastiCache를 거치는 마이크로서비스 구조에서는 이 방법이 통하지 않는다. 각 서비스는 자기 안에서만 일어난 일을 알 뿐, 요청 전체가 어디서 시간을 흘렸는지는 아무도 갖고 있지 않다. 로그를 다 모아도 "이 요청"에 속한 로그들을 어떻게 묶을지부터 막힌다. AWS X-Ray는 이 "분산된 요청을 하나로 꿰는" 문제를 푸는 분산 추적(distributed tracing) 서비스다.
DVA-C02에서 X-Ray는 "지표는 무엇이 느린지 알려주지만 어디서 느린지는 못 알려준다"는 한계의 답으로 등장한다. Lambda에서는 토글 하나, EC2/ECS에서는 데몬 설치라는 활성화 방식의 차이, Annotation과 Metadata의 인덱싱 차이, 샘플링의 작동 원리가 시험에서 묻기 쉬운 포인트다. 이번 글은 분산 추적이라는 분야가 어디서 왔는지(Google Dapper), Trace ID가 어떻게 서비스 경계를 넘어 전파되는지, X-Ray가 왜 "모든 요청을 추적하지 않는가"를 다룬다.
X-Ray의 개념은 AWS의 발명이 아니다. 2010년 Google이 발표한 논문 "Dapper, a Large-Scale Distributed Systems Tracing Infrastructure" 가 현대 분산 추적의 직접적 조상이다. Dapper가 제시한 데이터 모델, 곧 trace(요청 전체), span(각 작업 단위), 그리고 span들이 부모-자식으로 이루는 트리가 거의 그대로 X-Ray의 Trace/Segment/Subsegment로 이어진다.
Dapper가 풀려던 문제는 명확했다. 구글 검색 한 번이 수천 대의 서버를 거치는데, 어떤 요청이 느릴 때 그 책임이 어느 서버·어느 단계에 있는지를 알아내야 했다. 해법은 두 가지였다.
이 두 가지가 있으면 흩어진 기록들을 trace ID로 모으고 부모-자식 관계로 재구성해 요청 전체의 타임라인을 그릴 수 있다.
Dapper의 통찰은 추적이 본질적으로 인과 관계(causality) 추적이라는 점이다. 분산 시스템에서 "A가 B를 일으켰다"를 아는 데는 단순 타임스탬프 비교로 부족하다. 서로 다른 머신의 시계는 미세하게 어긋나기 때문이다(시계 동기화 문제). 그래서 span의 부모-자식 관계는 시간 순서가 아니라 호출 관계로 정의된다. X-Ray가 Subsegment를 부모 Segment 아래 트리로 구성하는 것도 시간이 아니라 인과를 표현하기 위해서다.
Dapper 논문이 강조한 또 하나는 "추적은 반드시 저오버헤드여야 한다"는 제약이다. 모든 요청을 추적하면 추적 데이터를 만들고 전송하는 비용이 요청 처리 비용에 더해져 추적 자체가 시스템을 느리게 만든다.
분산 추적의 심장은 컨텍스트 전파(context propagation) 다. 요청이 서비스 A에서 서비스 B로 넘어갈 때, A가 갖고 있던 trace ID가 B에게 전달되지 않으면 두 서비스의 기록은 영영 따로 논다. X-Ray는 이를 HTTP 헤더 하나로 해결한다.
X-Amzn-Trace-Id: Root=1-5e1b4151-5ac6c58dc39a6d6c2c8e4b21;Parent=53995c3f42cd8ad8;Sampled=1
Root은 이 요청 전체의 trace ID, Parent는 직전 span의 ID, Sampled는 "이 요청을 추적할지" 여부다. 서비스 A가 B를 호출할 때 이 헤더를 같이 보내면, B는 자기 span의 부모가 A의 어느 span인지 알고 같은 trace에 자기 기록을 붙인다. Sampled=1도 함께 전파되므로 한 요청은 처음 진입점에서 추적 여부가 결정되면 그 결정이 끝까지 일관되게 유지된다. 절반만 추적된 반쪽짜리 trace가 생기지 않는다.
patch_all()이 하는 일이 바로 이 전파의 자동화다. X-Ray SDK는 boto3·requests 같은 라이브러리를 몽키패칭해, 애플리케이션이 외부 호출을 할 때마다 trace 헤더를 자동으로 주입하고 subsegment를 연다. 개발자가 모든 호출마다 수동으로 헤더를 넣지 않아도 되는 이유다. 이 "라이브러리 자동 계측(auto-instrumentation)"은 분산 추적 도입의 가장 큰 실용적 장벽인 "수천 개의 호출 지점을 일일이 고칠 수 없다"는 문제를 우회한다. OpenTelemetry도 같은 방식의 자동 계측 에이전트를 제공한다.
⚠️ 함정: ALB는 X-Ray를 지원하지 않는다. ALB는 trace 헤더를 통과(pass-through) 시킬 뿐 자기 span을 만들지 않으므로 서비스 맵에 노드로 나타나지 않는다. 추적이 의미 있게 시작되는 진입점은 API Gateway(스테이지에서 X-Ray 활성화)부터다. 시험에서 "ALB 뒤 요청 지연을 X-Ray로 추적"이 보이면, ALB 자체는 추적 대상이 아니라는 점을 함정으로 깐다.
X-Ray 활성화 방법이 서비스마다 다른 것은 이유를 알면 외울 필요가 없다. 차이의 본질은 "추적 데이터를 누가 X-Ray 서비스로 보내는가"다.
X-Ray SDK는 생성한 span을 곧장 X-Ray API로 보내지 않고 로컬 UDP 포트 2000으로 쏜다. 이를 받아서 모아 배치로 X-Ray 서비스에 HTTPS 전송하는 것이 X-Ray 데몬이다. SDK와 데몬을 분리한 이유는 SDK가 매 span마다 X-Ray API를 동기 호출하면 애플리케이션이 느려지기 때문이다. UDP는 "보내고 잊는(fire-and-forget)" 방식이라 애플리케이션을 블로킹하지 않고, 실제 전송·배치·재시도는 데몬이 백그라운드에서 담당한다.
EC2·ECS·EKS에서는 이 데몬을 사용자가 직접 띄워야 한다(프로세스로 또는 ECS 사이드카 컨테이너로). 반면 Lambda는 이 데몬이 실행 환경에 이미 내장돼 있다. 그래서 Lambda는 Active Tracing 토글 하나만 켜면 AWS가 데몬 역할을 대신한다. 사용자가 데몬을 띄울 인프라 자체가 없는 서버리스 환경이라 AWS가 대신 품고 있는 것이다.
| 서비스 | 활성화 | 데몬 |
|---|---|---|
| Lambda | Active Tracing 토글 | AWS가 내장 (불필요) |
| API Gateway | 스테이지에서 활성화 | 해당 없음 |
| EC2 / ECS / EKS | SDK + 데몬 직접 실행 | 사용자 책임 (UDP 2000) |
| Beanstalk | 환경 설정에서 활성화 | 플랫폼이 데몬 포함 |
ECS에서 X-Ray를 쓸 때는 데몬 사이드카를 빠뜨리기 쉽다. SDK는 UDP 2000으로 span을 계속 쏘지만 받는 데몬이 없으면 그냥 허공에 사라지고, 에러도 나지 않아 추적이 "조용히" 안 된다. 표준 패턴은 태스크 정의에 amazon/aws-xray-daemon 컨테이너를 사이드카로 추가하고, 애플리케이션 컨테이너가 같은 태스크 네트워크의 2000번 포트로 보내게 하는 것이다. "추적이 안 보인다"면 IAM 권한 누락이나 이 데몬 부재를 먼저 의심한다.
trace에 부가 정보를 붙이는 방법은 두 가지이며, 차이는 인덱싱되는가 하나다.
@xray_recorder.capture('process_order')
def process_order(order_id):
with xray_recorder.in_subsegment('validate_order') as subseg:
subseg.put_annotation('orderId', order_id) # 인덱싱 O → 검색/필터 가능
subseg.put_metadata('orderDetails', {...}) # 인덱싱 X → 보기만 가능Annotation은 인덱싱되어 X-Ray 콘솔에서 필터 표현식으로 검색할 수 있다(annotation.orderId = "O-123" 같은 식으로). 대신 trace당 최대 50개, 값은 문자열·숫자·불린만 가능하다. Metadata는 인덱싱되지 않아 검색에는 못 쓰지만 크기 제한 없이 임의의 JSON 객체를 담을 수 있어 디버깅용 큰 데이터에 적합하다.
이 구분은 데이터베이스의 인덱스 설계와 같은 트레이드오프다. 인덱스는 검색을 빠르게 하지만 저장·쓰기 비용을 늘리므로 모든 컬럼에 인덱스를 걸지 않는다. X-Ray가 Annotation을 50개로 제한하는 것도 같은 이유다. 모든 부가 정보를 인덱싱하면 trace 검색 인프라가 감당할 수 없다. 그래서 "검색·필터에 쓸 식별자(userId, orderId, 결과 코드)는 Annotation, 보기만 할 큰 객체(요청 본문, 응답 페이로드)는 Metadata"라는 역할 분담이 생긴다.
⚠️ 함정: "userId로 특정 사용자의 모든 trace를 찾고 싶다"면 반드시 Annotation이다. Metadata에 userId를 넣으면 trace에 데이터로 붙긴 하지만 검색이 안 돼 무용지물이다. 시험에서 "필터링/검색 가능한 데이터" 키워드가 보이면 Annotation, "큰 객체/디버깅 정보"면 Metadata로 갈린다.
X-Ray의 기본 샘플링은 "매 초 처음 1개 요청은 무조건 추적하고, 그 이후로는 5%만 추적"이다. 이 숫자 조합(fixed_target: 1, rate: 0.05)에는 분산 추적의 설계 의도가 담겨 있다.
{
"rules": [
{ "description": "헬스체크 제외", "http_method": "GET",
"url_path": "/health", "fixed_target": 0, "rate": 0 },
{ "description": "결제 API 전수 추적",
"url_path": "/payment/*", "fixed_target": 1, "rate": 1.0 }
],
"default": { "fixed_target": 1, "rate": 0.05 }
}fixed_target(초당 보장 추적 수)과 rate(그 이후 비율)를 분리한 이유가 중요하다. 트래픽이 적은 서비스(초당 1건)는 5%만 추적하면 평균 20초에 한 번꼴이라 사실상 아무것도 못 본다. fixed_target: 1은 트래픽이 아무리 적어도 초당 최소 1건을 보장해, 한산한 서비스에서도 표본이 끊기지 않게 한다. 반대로 초당 수만 건이 쏟아지는 서비스에서는 그 1건 보장분 위에 5%만 더해 추적량이 폭증하지 않게 막는다.
샘플링 결정은 요청의 진입점에서 한 번만 내려지고 Sampled 헤더로 전체 경로에 전파된다(앞서 본 컨텍스트 전파). 각 서비스가 독립적으로 샘플링을 결정하면 어떤 요청은 A에서만 추적되고 B에서는 추적되지 않아 trace가 군데군데 비어 버린다. "추적 여부는 한 번 정하면 끝까지 일관"이라는 원칙이 trace의 완결성을 보장한다. 결제 API에 rate: 1.0(100%)을 거는 것처럼 비즈니스적으로 중요하거나 에러율이 높은 경로는 샘플링을 높이고, 헬스체크 같은 노이즈는 0으로 두는 것이 표준 튜닝 패턴이다.
X-Ray Insights는 샘플링된 데이터 위에서 머신러닝으로 비정상 패턴(응답 시간 급증, 에러율 상승)을 자동 감지해 EventBridge로 알림을 보낸다. 일부만 추적하는데도 이상 탐지가 작동하는 것은 통계적으로 표본만으로도 분포의 변화(평소 대비 지연 증가)를 감지할 수 있기 때문이다. "전수 조사 없이 표본으로 모집단을 추정"하는 통계학의 기본 원리가 운영 모니터링에 적용된 예다.
X-Ray는 "지표는 무엇이 느린지만 알려준다"는 한계를, 요청 하나에 trace ID를 부여해 분산된 기록을 인과 관계로 재구성하는 것으로 넘어선다. 그 모델은 2010년 Google Dapper에서 왔고, 컨텍스트 전파(X-Amzn-Trace-Id 헤더)가 서비스 경계를 잇는다. Lambda가 토글이고 EC2가 데몬인 것은 "누가 UDP 2000의 span을 X-Ray로 보내는가"의 차이이고, Annotation과 Metadata는 인덱싱 여부로 갈린다. 샘플링은 "관측이 대상을 교란한다"는 근본 제약에 대한 답이며, fixed_target + rate의 분리에 그 균형이 담겨 있다.
다음 글에서는 시간 순서대로 일어난 일을 추적하는 것을 넘어, 누가 무엇을 했는지를 감사하고 그 사건에 자동으로 반응하는 CloudTrail과 EventBridge로 넘어간다.
선택지를 클릭하면 정답·해설이 펼쳐집니다.
문제 1
X-Ray에서 "특정 userId를 가진 모든 trace를 콘솔에서 검색"하려 한다. userId를 어디에 넣어야 하는가?
문제 2
ECS Fargate에서 애플리케이션에 X-Ray SDK를 통합했는데 콘솔에 trace가 전혀 나타나지 않는다. 에러 로그도 없다. 가장 가능성 높은 원인은?
문제 3
Lambda 함수에서 X-Ray를 활성화하는 가장 적절한 방법과, EC2와의 차이를 옳게 설명한 것은?
문제 4
X-Ray 기본 샘플링 규칙 fixed_target: 1, rate: 0.05에서 fixed_target의 역할은?
문제 5
ALB 뒤에 있는 백엔드 요청의 지연을 X-Ray로 추적하려 한다. 추적이 의미 있게 시작되는 지점은?
문제 6
마이크로서비스 A가 B를 호출할 때 두 서비스의 추적 기록이 하나의 trace로 묶이게 하는 메커니즘은?
문제 7
X-Ray에 디버깅용으로 수 KB 크기의 요청/응답 전체 페이로드를 첨부하려 한다. 검색에는 쓰지 않는다. 적절한 것은?