콘텐츠로 이동

Logs / Metrics / Traces

분류: Layer 6 - 운영 심화: 관측성 & 복원력 | 선수지식: CloudWatch Basics

Logs, Metrics, Traces는 서비스가 실패하거나 느려질 때 각각 다른 질문에 답하는 관측성(Observability) 신호다.

  • Log(로그) 는 한 시점에 일어난 사건의 문맥을 남긴 기록이다.
  • Metric(메트릭, 지표) 은 시간에 따라 변하는 숫자를 모아 추세와 상태를 보여준다.
  • Trace(트레이스, 추적) 는 하나의 요청이 여러 서비스와 의존성을 지나간 경로를 연결한다.

짧게 말하면 Metrics는 감지하고, Logs는 설명하고, Traces는 위치를 좁힌다. 브라우저 Network 탭의 워터폴을 본 경험은 도움이 되지만, 여기서는 브라우저가 아니라 여러 서버와 의존성 사이의 요청 흐름을 본다.

2. 선행 한계 — 대시보드만으로 원인을 못 찾는 순간

섹션 제목: “2. 선행 한계 — 대시보드만으로 원인을 못 찾는 순간”

CloudWatch Basics에서는 로그 그룹, 지표, 알람, 대시보드의 기본 사용법을 배웠다. 그 다음 문제는 “알람이 울렸다”에서 끝나지 않는다. 운영자는 바로 다음 질문에 답해야 한다.

  • 정말 장애인가, 일시적인 흔들림인가?
  • 어느 서비스, 어느 API, 어느 사용자군에 영향이 있는가?
  • 코드, DB, 외부 API, Queue, 캐시 중 어디가 병목인가?
  • 지금 롤백해야 하는가, 확장해야 하는가, 특정 기능을 차단해야 하는가?

단일 서버 시대에는 로그 파일을 열어 위에서부터 읽어도 어느 정도 원인을 찾을 수 있었다. 하지만 Queue, Worker, 캐시, 외부 API, 마이크로서비스가 섞이면 하나의 사용자 요청이 여러 프로세스를 지나간다. 이때 로그만 보면 너무 많고, 지표만 보면 너무 요약되어 있고, 추적만 보면 전체 건강 상태가 보이지 않는다.

그래서 세 신호가 나뉘었다. Metrics는 “지금 평소와 다르다”를 빠르게 알려준다. Logs는 “어떤 사건과 에러 문맥이 반복되는가”를 보여준다. Traces는 “한 요청이 어디서 시간을 쓰거나 실패했는가”를 연결한다.

3. 관측성의 철학: 감지, 설명, 국소화

섹션 제목: “3. 관측성의 철학: 감지, 설명, 국소화”

관측성은 대시보드 수를 늘리는 일이 아니다. 서비스 내부를 직접 들여다볼 수 없을 때, 외부로 나온 신호를 조합해서 내부 상태를 추론할 수 있게 만드는 설계다.

Logs / Metrics / Traces의 사고 흐름
flowchart TD
UserImpact["사용자 영향: 느림, 실패, 타임아웃"] --> Detect["Metrics: 규모와 추세 감지"]
Detect --> Explain["Logs: 반복되는 사건과 에러 문맥 설명"]
Explain --> Localize["Traces: 요청 경로에서 병목 위치 국소화"]
Localize --> Decision["운영 판단: 롤백, 완화, 확장, 의존성 격리"]
Decision --> Verify["Metrics: 조치 후 정상화 확인"]
Verify --> Detect

이 순서는 절대 규칙이 아니라 기본 루프다. 예를 들어 특정 Trace ID를 먼저 받은 장애 제보라면 Trace에서 시작할 수 있다. 하지만 최종 판단은 다시 Metrics로 돌아와야 한다. 한 요청의 Trace가 느렸다는 사실만으로 전체 장애라고 말할 수 없기 때문이다.

세 신호의 역할 분리

Metrics

숫자와 추세로 장애의 규모, 시작 시점, 심각도를 감지한다.

에러율, p95/p99 지연, 처리량, 큐 적체, CPU/메모리 사용률을 볼 때

Logs

개별 사건의 문맥을 남겨 어떤 에러가 어떤 조건에서 반복되는지 설명한다.

특정 errorType, orderId, correlationId, 사용자 영향 범위를 찾을 때

Traces

한 요청의 서비스 호출, DB 쿼리, 외부 API 호출을 시간 순서로 묶어 병목 위치를 좁힌다.

API 서버, DB, 캐시, 외부 결제사 중 어느 구간이 느린지 확인할 때

이 문서는 도구 이름보다 용어 경계를 먼저 잡아야 읽기 쉽다.

로그는 “어떤 일이 있었다”는 사건 기록이다. 좋은 로그는 사람에게 읽히는 문장만이 아니라 기계가 필터링할 수 있는 필드를 가진다. 예를 들어 payment_failed라는 이벤트 이름, orderId, durationMs, errorType이 함께 있으면 장애 시 같은 종류의 실패를 묶을 수 있다.

구조화 로그(Structured Log)는 로그 한 줄을 JSON 같은 구조로 출력하는 방식이다. 문자열 한 덩어리 대신 필드가 분리되어 있으므로 Log Insights, Elasticsearch, Loki 같은 도구가 level = "ERROR"orderId = "..." 조건으로 검색할 수 있다. 단, 비밀번호, 토큰, 주민번호 같은 민감 정보는 구조화하면 더 쉽게 퍼지므로 처음부터 제외해야 한다.

메트릭은 시간 축에 찍히는 숫자다. request_count, error_rate, p99_latency_ms, queue_depth, cpu_utilization처럼 상태를 숫자로 압축한다. 로그가 사건의 문장이라면, 메트릭은 사건들을 집계한 계기판이다.

Trace는 하나의 요청 전체 경로다. Span은 Trace 안에서 나뉜 하나의 작업 단위다. 컨트롤러 처리, DB 쿼리, Redis 조회, 외부 API 호출이 각각 Span이 될 수 있다.

Trace: GET /orders/9821 요청 1건
├── Span: API Gateway (3ms)
├── Span: OrderService controller (12ms)
├── Span: Redis GET order:9821 (2ms)
├── Span: RDS SELECT orders (38ms)
└── Span: Payment API status check (420ms)

이 예시에서 총 지연이 크다면 Payment API Span을 먼저 의심한다. 반대로 RDS Span이 900ms라면 DB 쿼리나 커넥션 풀을 봐야 한다.

Trace ID는 분산 추적 시스템이 하나의 요청 전체를 묶기 위해 쓰는 식별자다. W3C TraceContext 표준에서는 traceparent 헤더에 Trace ID와 Span ID를 담아 다음 서비스로 전달한다.

Correlation ID는 여러 로그 줄을 같은 업무 흐름으로 묶기 위해 애플리케이션이 붙이는 식별자다. Trace ID와 같은 값을 써도 되지만 항상 같은 개념은 아니다. 중요한 것은 로그에 Trace ID나 Correlation ID 중 하나가 남아 있어야 Metrics, Logs, Traces 사이를 오갈 수 있다는 점이다.

카디널리티(Cardinality)는 값의 종류 수다. 메트릭 레이블에 붙은 값 조합이 많아질수록 시계열 수가 늘어난다. status=500은 값 종류가 적지만, userId=12345는 사용자 수만큼 값이 늘어난다.

Sampling은 모든 Trace를 저장하지 않고 일부만 고르는 방식이다. 트래픽이 많으면 Trace를 100% 저장하는 비용이 빠르게 커지기 때문이다. Head-based sampling은 요청 시작 시점에 저장 여부를 결정하고, tail-based sampling은 요청이 끝난 뒤 에러나 고지연 여부를 보고 결정한다.

OpenTelemetry(OTel)는 로그, 메트릭, 트레이스를 수집하고 내보내기 위한 벤더 중립 표준이다. Instrumentation(계측)은 코드와 런타임에 관측 신호를 심는 작업이다. Collector는 애플리케이션과 백엔드 사이에서 신호를 받아 배칭, 필터링, 샘플링, 라우팅을 수행하는 중간 프로세스다.

Application + OTel SDK
-> OTLP(OpenTelemetry Protocol)
-> OpenTelemetry Collector
-> CloudWatch / X-Ray / Prometheus / Grafana / Jaeger / Datadog

Collector를 두면 애플리케이션은 “신호를 보낸다”에 집중하고, 어디로 보낼지와 얼마나 줄일지는 Collector 설정으로 바꿀 수 있다.

5. Worked Incident: 결제 API가 갑자기 느려졌다

섹션 제목: “5. Worked Incident: 결제 API가 갑자기 느려졌다”

아래 예시는 세 신호가 왜 함께 필요한지 보여준다. 상황은 온라인 주문 서비스의 POST /checkout API가 느려진 경우다.

대시보드에 다음 변화가 보인다.

지표평소현재해석
Request rate800 rpm820 rpm트래픽 폭증은 아니다
5xx error rate0.1%4.8%사용자 실패가 급증했다
p95 latency420ms1.6s느린 요청이 넓게 퍼졌다
p99 latency900ms4.2s최악 구간은 사용자 체감 크다
Queue depth120 jobs130 jobsWorker 적체는 주원인 아님
DB connection usage45%48%DB 풀 포화도 아님

여기서 Metrics가 알려주는 것은 원인이 아니라 우선순위다. 트래픽이 평소와 비슷한데 에러율과 tail latency가 오른다면 용량 부족보다 특정 의존성 실패를 의심한다. p95p99의 정확한 의미는 7.2절에서 다시 보지만, 여기서는 “대부분의 요청”과 “가장 느린 꼬리 구간”을 나누어 보는 숫자로 읽으면 된다. p99 = 4.2s는 100개 중 대략 1개가 4초대 이상으로 밀린다는 뜻이다. 분당 820건이라면 매분 약 8건이 심각하게 느릴 수 있다.

Log Insights나 로그 검색에서 에러 로그를 묶어 본다.

filter level = "ERROR" and event = "checkout_failed"
| stats count(*) as cnt by errorType, dependency, bin(5m)
| sort cnt desc

반복 패턴이 다음처럼 나온다고 하자.

{
"timestamp": "2026-04-07T12:03:22.391Z",
"level": "ERROR",
"event": "checkout_failed",
"orderId": "order-9821",
"correlationId": "req-7f2a",
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"dependency": "payment-gateway",
"errorType": "TimeoutException",
"durationMs": 3000
}

이 로그는 “결제 게이트웨이 timeout이 반복된다”는 원인 후보를 준다. 하지만 아직 결제 게이트웨이가 실제 병목인지, 우리 코드의 retry가 시간을 늘린 것인지, 네트워크 경계에서 막힌 것인지는 알 수 없다. 그래서 Trace로 넘어간다.

느린 Trace 하나를 보면 다음과 같다.

Trace: POST /checkout (4,230ms)
├── Span: validate cart (18ms)
├── Span: RDS SELECT cart_items (32ms)
├── Span: reserve inventory (65ms)
├── Span: payment-gateway authorize (3,000ms timeout)
├── Span: retry payment-gateway authorize (1,000ms timeout)
└── Span: write failure audit log (12ms)

Trace는 결제 게이트웨이 호출과 retry가 총 지연의 대부분이라는 위치 정보를 준다. 이제 운영 판단은 더 구체적이다.

  • 결제 승인 기능만 영향이 크면 결제 기능 degraded mode를 검토한다.
  • retry가 두 번 이상 겹쳐 tail latency를 키운다면 retry budget을 줄인다.
  • 외부 결제사 장애라면 circuit breaker를 열고 사용자 메시지를 바꾼다.
  • 조치 뒤에는 다시 Metrics에서 p95/p99와 에러율이 내려가는지 확인한다.

Metrics만 봤다면 “checkout이 느리다”까지만 알 수 있다. Logs만 봤다면 “TimeoutException이 많다”까지만 알 수 있다. Traces만 봤다면 “어떤 요청 하나가 결제 게이트웨이에서 느렸다”까지만 알 수 있다.

세 신호를 함께 보면 “트래픽 폭증이 아니라 결제 의존성 timeout이 checkout tail latency와 5xx를 만들고 있다”라고 말할 수 있다. 이 문장이 운영 의사결정의 최소 단위다.

로그는 가장 친숙하지만 가장 쉽게 망가지는 신호다. 무엇이든 로그로 남길 수 있기 때문에, 기준 없이 늘리면 비용과 노이즈가 같이 증가한다.

좋은 운영 로그는 다음 필드를 기본으로 가진다.

필드의미예시
timestamp사건 시각2026-04-07T12:03:22.391Z
level심각도INFO, WARN, ERROR
event기계가 묶을 수 있는 사건 이름checkout_failed, order_created
service로그를 남긴 서비스order-api
correlationId업무 흐름이나 요청을 묶는 IDreq-7f2a
traceId분산 추적과 연결되는 ID4bf92f3577b34da6a3ce929d0e0e4736
errorType분류 가능한 실패 유형TimeoutException
durationMs사건 처리 시간3000
dependency실패한 외부 의존성payment-gateway, rds, redis

예시는 다음처럼 한 줄 JSON으로 남기는 것이 좋다.

{
"timestamp": "2026-04-07T12:03:22.391Z",
"level": "ERROR",
"event": "checkout_failed",
"service": "order-api",
"orderId": "order-9821",
"correlationId": "req-7f2a",
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"errorType": "TimeoutException",
"dependency": "payment-gateway",
"durationMs": 3000
}

반대로 아래 로그는 사람이 보기에는 익숙하지만 기계 집계가 어렵다.

2026-04-07 12:03:22 ERROR checkout failed for order order-9821 because payment timed out after 3000ms

문자열 검색은 할 수 있지만 dependency별 에러율, errorType별 추세, traceId 기반 연결이 모두 불안정해진다.

로그는 사건 설명에 강하다. 사용자별 상세 원인, 외부 API 응답 코드, 어떤 입력이 실패했는지는 로그에 잘 맞는다.

하지만 고빈도 카운터를 로그로만 남기면 비용이 커진다. 헬스체크가 초당 100번 들어오는데 매번 INFO 로그를 남기면 하루 수백만 줄이 된다. 이런 것은 Metric으로 집계하고, 실패나 샘플만 로그로 남기는 편이 낫다.

민감 정보는 “나중에 마스킹”이 아니라 “처음부터 남기지 않기”가 원칙이다. 구조화 로그는 검색하기 쉬운 만큼 유출되면 피해도 커진다.

6.3 Correlation ID가 없을 때 생기는 문제

섹션 제목: “6.3 Correlation ID가 없을 때 생기는 문제”

결제 장애 로그가 1,000줄 있어도 같은 요청에서 나온 로그를 묶을 수 없다면 조사 시간이 길어진다. correlationIdtraceId가 있으면 한 요청의 로그를 다음처럼 좁힐 수 있다.

filter traceId = "4bf92f3577b34da6a3ce929d0e0e4736"
| sort @timestamp asc
| fields @timestamp, service, event, level, durationMs, errorType

이 쿼리의 목적은 “전체 장애 규모”가 아니다. 한 요청에서 어떤 사건 순서가 있었는지 설명하는 것이다. 규모 판단은 Metrics로 돌아가야 한다.

7. Metrics: 건강 상태를 숫자로 감지하는 신호

섹션 제목: “7. Metrics: 건강 상태를 숫자로 감지하는 신호”

Metric은 로그를 집계한 숫자처럼 보이지만, 설계 기준이 다르다. Metric은 빠르게 집계되고 비교되어야 하므로 값 종류가 제한적이어야 한다.

서비스 요청을 볼 때는 RED 신호가 기본이다.

  • Rate: 초당 또는 분당 요청 수다.
  • Errors: 실패 비율이나 실패 건수다.
  • Duration: 요청 처리 시간이다. 평균보다 p95, p99가 사용자 체감에 더 가깝다.

인프라 자원을 볼 때는 USE 신호가 유용하다.

  • Utilization: CPU, 메모리, 디스크, 네트워크를 얼마나 쓰는가.
  • Saturation: 대기열, 큐 적체, 커넥션 풀 대기처럼 한계에 얼마나 가까운가.
  • Errors: 자원 사용 중 실패가 발생하는가.

RED는 “사용자 요청이 괜찮은가”에 가깝고, USE는 “자원이 한계에 가까운가”에 가깝다. API p99가 오르는데 CPU가 낮다면 코드나 외부 의존성을 의심할 수 있다. API p99와 DB connection usage가 함께 오른다면 DB 풀이나 쿼리 병목을 더 먼저 본다.

7.2 p95와 p99를 평균보다 먼저 보는 이유

섹션 제목: “7.2 p95와 p99를 평균보다 먼저 보는 이유”

평균은 느린 요청이 일부 있을 때 문제를 숨긴다. 100개 요청 중 99개가 100ms이고 1개가 5,000ms라면 평균은 약 149ms다. 대시보드 평균만 보면 괜찮아 보이지만, 그 1개 사용자는 5초를 기다렸다.

p95 = 700ms는 100개 중 95개가 700ms 이하라는 뜻이다. p99 = 2s는 상위 1% 느린 요청이 2초 근처 이상이라는 뜻이다. 트래픽이 분당 10,000건이면 1%는 분당 100건이다. 작은 비율처럼 보여도 실제 사용자 수로 바꾸면 무시하기 어렵다.

메트릭의 비용과 메모리는 보통 “메트릭 이름 × 레이블 조합 수”로 커진다.

설계시계열 수 감각평가
endpoint 20개 × status 5개 × region 3개300대체로 관리 가능
위 조합 × method 4개1,200여전히 의미 있는 분류
위 조합 × userId 100,000개120,000,000메트릭으로는 거의 항상 실패

userId, orderId, sessionId, traceId는 Metric label로 넣지 않는다. 이 값들은 Logs나 Traces에서 찾는다. Metric label은 endpoint, status, region, service, method처럼 값 종류가 제한된 분류에 적합하다.

Metric은 “checkout 5xx가 4.8%“라고 말할 수 있다. 하지만 어떤 주문이 실패했는지, 외부 결제사의 어떤 응답 코드가 반복되는지, 특정 Trace에서 어느 Span이 느렸는지는 말하지 못한다. Metric은 감지 신호이기 때문에 원인 설명까지 맡기면 안 된다.

8. Traces: 요청 경로를 국소화하는 신호

섹션 제목: “8. Traces: 요청 경로를 국소화하는 신호”

Trace는 분산 시스템에서 특히 중요하다. 하나의 요청이 API 서버, Redis, DB, 외부 API, Worker를 지나면 어느 구간이 느린지 로그만으로 재구성하기 어렵다.

Context propagation은 Trace ID와 Span 정보를 다음 서비스로 전달하는 과정이다. HTTP에서는 보통 헤더를 통해 전달한다. W3C TraceContext의 traceparent 헤더는 다음과 같은 모양이다.

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
-- -------------------------------- ---------------- --
version trace id span id flags

서비스 A가 서비스 B를 호출할 때 이 헤더를 전달하지 않으면 Trace가 끊긴다. 그 결과 A와 B의 Span이 같은 요청으로 묶이지 않고, “중간에서 사라진 요청”처럼 보인다.

8.2 Auto instrumentation과 manual instrumentation

섹션 제목: “8.2 Auto instrumentation과 manual instrumentation”

자동 계측(auto instrumentation)은 HTTP 서버, HTTP 클라이언트, DB 드라이버 같은 흔한 라이브러리를 자동으로 Span으로 만든다. 처음 도입할 때는 자동 계측만으로도 “어느 서비스와 DB 호출이 느린가”를 보기 쉽다.

수동 계측(manual instrumentation)은 비즈니스 의미가 있는 구간에 직접 Span을 추가하는 방식이다. 예를 들어 reserve_inventory, authorize_payment, publish_order_created 같은 Span은 자동 계측만으로는 이름이 잘 드러나지 않을 수 있다.

좋은 시작점은 자동 계측으로 큰 흐름을 만든 뒤, 장애 판단에 필요한 핵심 업무 단계만 수동 Span으로 보강하는 것이다. 단, 자동 계측은 앱과 라이브러리가 초기화되는 순서의 영향을 받는다. HTTP 서버나 DB 클라이언트가 먼저 만들어진 뒤 계측을 켜면 일부 Span이 빠져서 “Trace가 있는데 DB 호출이 안 보이는” 상태가 될 수 있다.

초당 10,000 요청을 처리하는 서비스가 있고, 요청 하나가 평균 5개 Span을 만든다고 하자. 100% Trace를 저장하면 초당 50,000 Span이 저장 대상이 된다. 1% head-based sampling을 적용하면 초당 100 Trace, 약 500 Span으로 줄어든다.

문제는 무작위 1%가 꼭 중요한 1%는 아니라는 점이다. 에러율이 0.1%인 서비스에서 head-based 1% sampling을 쓰면 에러 Trace가 충분히 남지 않을 수 있다. 이때 tail-based sampling은 요청 완료 후 에러나 고지연 Trace를 보존할 수 있어 유리하다. 대신 Collector가 완료 전 Span을 잠시 들고 있어야 하므로 메모리와 복잡도가 늘어난다.

Trace는 개별 요청에는 강하지만 전체 추세에는 약하다. 느린 Trace 하나를 보고 전체 장애라고 판단하면 과잉 대응할 수 있다. 반대로 sampling 때문에 느린 Trace가 일부만 남을 수도 있다. Trace에서 원인 후보를 찾았으면 Metric으로 얼마나 넓게 퍼졌는지 다시 확인해야 한다.

9. 수집 메커니즘: 애플리케이션에서 백엔드까지

섹션 제목: “9. 수집 메커니즘: 애플리케이션에서 백엔드까지”

관측 신호는 보통 다음 경로로 이동한다.

OpenTelemetry Collector 기반 수집 흐름
flowchart LR
App["Application"] --> SDK["OTel SDK / Instrumentation"]
SDK --> Collector["Collector"]
Collector --> Logs["Logs backend"]
Collector --> Metrics["Metrics backend"]
Collector --> Traces["Traces backend"]
Logs --> Query["검색과 분석"]
Metrics --> Alert["알람과 대시보드"]
Traces --> Timeline["요청 타임라인"]

애플리케이션에 SDK와 계측을 넣으면 신호가 만들어진다. Collector는 신호를 받아 배칭, 재시도, 샘플링, 필터링, 민감 정보 제거, 백엔드 라우팅을 담당한다. 백엔드는 CloudWatch, X-Ray, Prometheus, Grafana, Jaeger, Datadog처럼 조직이 선택한 도구가 될 수 있다.

Collector가 없으면 애플리케이션이 백엔드별 전송, 재시도, 필터링 부담을 직접 가진다. 작은 실험에서는 괜찮지만, 프로덕션에서는 Collector를 두는 쪽이 운영 변경에 더 강하다.

10. 도구 선택은 체크리스트가 아니라 기준 문제다

섹션 제목: “10. 도구 선택은 체크리스트가 아니라 기준 문제다”

도구 이름을 외우는 것보다 “어떤 제약 때문에 이 선택을 하는가”가 중요하다.

OpenTelemetry는 벤더 중립 계측 표준이다. X-Ray는 AWS에서 Trace를 보고 분석하는 백엔드와 관련 SDK 생태계다.

신규 서비스에서 장기적으로 백엔드 교체 가능성, 멀티클라우드, 표준 계측을 중시한다면 OTel SDK와 Collector를 기본으로 잡는 편이 낫다. 이미 X-Ray SDK가 깊게 들어간 레거시 서비스라면 바로 교체보다 Trace ID 연결, sampling, Collector 전환 경로를 먼저 확인한다.

판단 질문은 다음과 같다.

  • 계측 코드를 특정 벤더 API에 묶어도 되는가?
  • Trace를 X-Ray 외 다른 백엔드로도 보낼 가능성이 있는가?
  • 팀이 Collector를 운영할 수 있는가?
  • 기존 로그와 Trace ID 연결이 끊기지 않는가?

CloudWatch Metrics는 AWS 리소스와 관리형 서비스 지표를 바로 보기 쉽다. Prometheus는 애플리케이션 커스텀 지표, Kubernetes 워크로드, PromQL 기반 분석에 강하다.

조건더 자연스러운 선택
AWS 관리형 리소스 상태를 빠르게 보고 싶다CloudWatch Metrics
애플리케이션별 커스텀 지표가 많다Prometheus 계열
PromQL과 Grafana 대시보드가 이미 있다Prometheus 계열
운영 팀이 AWS 콘솔 중심으로 움직인다CloudWatch Metrics
label cardinality를 세밀하게 관리해야 한다Prometheus 설계 검토가 중요

어느 쪽이든 userId 같은 고카디널리티 값을 Metric label로 넣으면 실패한다. 도구 선택보다 먼저 지표 설계가 맞아야 한다.

10.3 Collector 배포: 사이드카, DaemonSet, 게이트웨이

섹션 제목: “10.3 Collector 배포: 사이드카, DaemonSet, 게이트웨이”

Collector는 여러 위치에 둘 수 있다.

방식장점비용 또는 경계
Sidecar서비스별 격리와 설정 분리가 쉽다Pod마다 Collector가 떠서 리소스가 든다
DaemonSet노드당 하나라 리소스 효율이 좋다서비스별 격리와 설정 분리가 약하다
Gateway중앙 정책과 라우팅 관리가 쉽다네트워크 경로와 병목 지점이 생긴다

보안 민감 서비스나 서로 다른 sampling 정책이 필요하면 sidecar가 낫다. 일반 워크로드에서 리소스 효율이 중요하면 DaemonSet이 낫다. 여러 클러스터와 백엔드 라우팅을 중앙에서 관리해야 하면 gateway Collector를 추가한다.

Sampling은 “얼마나 줄일까”가 아니라 “무엇을 남길까”의 문제다.

조건기준
트래픽이 낮고 비용 부담이 작다높은 sampling 비율 또는 100%
트래픽이 높고 요청이 균일하다head-based sampling
에러와 SLO 위반 Trace를 꼭 남겨야 한다tail-based sampling
Collector 메모리 여유가 적다tail-based 비율과 보관 시간 주의
규제나 보안상 payload 노출이 위험하다attribute 필터링과 redaction 우선

예산으로 단순 계산하면 감각을 잡을 수 있다. 월 1억 요청 중 10%를 남기면 1,000만 Trace가 저장 대상이다. 요청당 평균 6개 Span이면 6,000만 Span이다. Trace 비용은 요청 수가 아니라 Span 수, 보존 기간, 조회량과 함께 커질 수 있으므로 Span 수까지 계산해야 한다.

11.1 “로그를 많이 남기면 관측성이 좋아진다”

섹션 제목: “11.1 “로그를 많이 남기면 관측성이 좋아진다””

아니다. 필드가 없는 로그, 중복 로그, 민감 정보가 섞인 로그는 관측성이 아니라 비용과 위험을 늘린다. 좋은 로그는 적절한 사건 이름, 식별자, 실패 유형, duration, dependency를 가진다.

11.2 “Metric label에 userId를 넣으면 사용자별 대시보드가 쉬워진다”

섹션 제목: “11.2 “Metric label에 userId를 넣으면 사용자별 대시보드가 쉬워진다””

사용자별 분석 욕구는 이해할 수 있지만 Metric label로는 위험하다. 사용자가 100,000명이고 endpoint, status, region까지 곱하면 시계열이 폭발한다. 사용자별 원인 조사는 Logs나 Traces에서 하고, Metric은 제한된 분류로 집계한다.

11.3 “Trace가 있으면 로그가 필요 없다”

섹션 제목: “11.3 “Trace가 있으면 로그가 필요 없다””

Trace는 경로와 시간을 잘 보여주지만 모든 사건 설명을 담지는 않는다. 비즈니스 실패 이유, 외부 API 응답 코드, 검증 실패 필드는 로그가 더 적합하다. Trace Span attribute에 너무 많은 정보를 넣으면 Trace 저장 비용과 보안 위험이 커진다.

p99는 tail latency를 보이게 하지만 원인은 아니다. p99가 올랐을 때 request rate, error rate, saturation, 배포 시점, dependency 상태를 같이 봐야 한다. 또 트래픽이 매우 낮은 서비스에서는 p99가 샘플 수 부족으로 튈 수 있다.

11.5 “Observability는 SRE 문서에서 다룰 운영 문화다”

섹션 제목: “11.5 “Observability는 SRE 문서에서 다룰 운영 문화다””

이 문서는 신호의 의미와 연결 방법을 다룬다. 다음 SRE Practices 문서는 이 신호를 SLI, SLO, error budget, incident response 같은 의사결정 체계로 바꾼다. 즉 Logs/Metrics/Traces는 SRE 판단의 입력이다.

긴 절차를 외우기보다 증상에서 어떤 개념이 깨졌는지 연결하는 연습이 중요하다.

증상의미하는 개념왜 문제인가첫 확인
p99는 급등했는데 평균은 거의 그대로다tail latency일부 사용자가 매우 느린 경험을 한다endpoint별 p95/p99, Trace 샘플
5xx가 늘었는데 트래픽은 평소와 비슷하다의존성 실패 또는 코드 회귀용량보다 특정 경로 실패일 가능성이 크다errorType별 로그, 배포 시점
Prometheus/Collector 메모리가 급증한다metric cardinality explosionlabel 조합이 폭발해 시계열 저장 비용이 커진다label별 시계열 수, userId 여부
Trace는 있는데 관련 로그를 못 찾는다trace/correlation 연결 실패신호 사이를 오갈 수 없어 조사 시간이 늘어난다로그에 traceId 필드 존재 여부
특정 서비스 뒤로 Trace가 끊긴다context propagation 실패헤더가 전달되지 않아 요청 경로가 분리된다HTTP client header propagation
에러 Trace가 거의 남지 않는다sampling 정책 부적합중요한 실패가 무작위 sampling에서 빠질 수 있다error trace 보존 규칙
로그 비용이 갑자기 커진다로그 레벨 또는 고빈도 이벤트 설계 문제INFO/DEBUG나 healthcheck 로그가 비용을 만든다log group별 ingestion volume
알람은 울리는데 조치 후 정상화가 불명확하다Metric 검증 루프 부재완화가 실제 사용자 영향을 줄였는지 알 수 없다조치 전후 SLI 지표

이 표는 운영 절차가 아니라 사고의 출발점이다. 증상 하나를 보면 “어떤 신호가 과하거나 부족한가”를 먼저 묻는다.

과제 1: 서비스 하나를 세 신호로 나누어 보기

섹션 제목: “과제 1: 서비스 하나를 세 신호로 나누어 보기”

현재 팀 서비스나 개인 프로젝트의 API 하나를 고른다. 그리고 아래 질문에 답한다.

  • Metrics: 이 API의 request rate, error rate, p95/p99 latency를 볼 수 있는가?
  • Logs: 실패했을 때 event, errorType, correlationId 또는 traceId가 남는가?
  • Traces: DB, Redis, 외부 API 호출이 Span으로 분리되어 보이는가?

세 질문 중 하나라도 답할 수 없다면 그 지점이 관측성 개선 후보가 된다.

새 메트릭 checkout_requests_total을 만든다고 하자. label 후보가 endpoint, status, region, paymentProvider, userId라면 조합 수를 손으로 계산한다.

endpoint 8개 × status 5개 × region 3개 × paymentProvider 4개 = 480 series
480 series × userId 100,000개 = 48,000,000 series

이 계산 하나만으로도 userId는 Metric label이 아니라 Log/Trace 필드여야 한다는 결론이 나온다.

과제 3: Trace ID가 로그에 남는지 확인하기

섹션 제목: “과제 3: Trace ID가 로그에 남는지 확인하기”

Trace 화면에서 Trace ID 하나를 고른다. 그 ID로 로그 검색이 되는지 확인한다. 검색이 안 된다면 계측이 없는 것이 아니라 연결 필드가 없는 문제일 수 있다.

filter traceId = "4bf92f3577b34da6a3ce929d0e0e4736"
| sort @timestamp asc
| fields @timestamp, service, event, level, message

14. 선택 부록: 절차는 짧게, 원리는 본문에

섹션 제목: “14. 선택 부록: 절차는 짧게, 원리는 본문에”

아래 절차는 실제 on-call에서 출발점으로 쓸 수 있지만, 본문 이해에 필수는 아니다.

API 에러율 알람이 울렸을 때 첫 10분
  1. Metrics에서 영향 범위를 본다. 에러율, request rate, p95/p99, saturation을 같은 시간축으로 본다.

  2. Logs에서 반복 에러 유형을 묶는다. errorType, dependency, service, endpoint별 count를 보고 가장 큰 실패 묶음을 찾는다.

  3. Traces에서 대표 느린 요청을 연다. 가장 긴 Span과 실패한 dependency를 확인한다.

  4. 운영 판단을 하나의 문장으로 만든다. 예: “트래픽 폭증이 아니라 payment-gateway timeout이 checkout p99와 5xx를 만들고 있다.”

  5. 완화 뒤 Metrics로 검증한다. 조치 후 p95/p99, 5xx, dependency error가 내려가는지 본다.

로그 비용이나 메트릭 폭발이 의심될 때
  1. Logs라면 log group별 ingestion volume을 먼저 본다. DEBUG/INFO, healthcheck, 루프 내부 로그, 중복 stack trace가 흔한 원인이다.

  2. Metrics라면 label 조합 수를 계산한다. userId, orderId, sessionId, traceId가 label에 들어갔는지 확인한다.

  3. 즉시 줄일 것은 줄이되 원인을 바꾼다. 로그 레벨만 낮추고 끝내면 다음 배포에서 재발한다. 고빈도 이벤트는 Metric으로, 개별 조사 값은 Log/Trace로 옮긴다.

  4. Collector가 메모리 때문에 죽는다면 queue, batch, sampling, tail decision window를 같이 본다. 단순 재시작은 유실과 반복 장애를 숨길 수 있다.

처음부터 모든 도구를 완벽히 도입하려고 하면 실패하기 쉽다. 작게 시작하는 순서는 다음이 현실적이다.

  1. 구조화 로그에 event, service, correlationId, traceId, errorType, durationMs를 넣는다.
  2. API별 RED Metric을 만든다.
  3. DB, 외부 API, Redis 같은 주요 의존성을 Span으로 분리한다.
  4. Metrics 알람에서 대표 로그 검색과 Trace 링크로 이동할 수 있게 연결한다.
  5. cardinality와 sampling 비용을 정기적으로 점검한다.

이 순서의 핵심은 “신호를 따로 모으는 것”이 아니라 “한 신호에서 다른 신호로 넘어갈 수 있게 만드는 것”이다.

개념 A개념 B차이점
LogsMetricsLogs는 개별 사건의 문맥, Metrics는 집계된 숫자와 추세
MetricsTracesMetrics는 전체 건강 상태, Traces는 개별 요청 경로
Trace IDCorrelation IDTrace ID는 분산 추적 표준 식별자, Correlation ID는 업무 흐름 연결 식별자
SpanLog eventSpan은 시간 구간, Log event는 특정 시점의 사건
MonitoringObservabilityMonitoring은 알려진 증상 감시, Observability는 신호 조합을 통한 추론
Head samplingTail samplingHead는 시작 때 선택, Tail은 완료 후 에러/지연을 보고 선택
Metric labelLog fieldlabel은 값 종류가 적어야 하고, log field는 개별 식별자에 적합
Auto instrumentationManual instrumentation자동은 기본 호출을 잡고, 수동은 비즈니스 의미를 보강한다

Logs / Metrics / Traces 학습 확인

  • Logs, Metrics, Traces가 각각 감지, 설명, 국소화 중 무엇에 강한지 설명할 수 있다.
  • Trace, Span, Trace ID, Correlation ID의 차이를 한 문장씩 말할 수 있다.
  • p95/p99가 평균보다 운영 판단에 중요한 이유를 수치 예시로 설명할 수 있다.
  • Metric label에 userId를 넣으면 왜 위험한지 cardinality 계산으로 설명할 수 있다.
  • Head-based sampling과 tail-based sampling의 선택 기준을 말할 수 있다.
  • OpenTelemetry SDK, instrumentation, Collector, backend의 역할을 구분할 수 있다.
  • 장애 상황을 Metrics -> Logs -> Traces -> Metrics 검증 루프로 설명할 수 있다.
  • OpenTelemetry Protocol(OTLP)
  • W3C TraceContext
  • Baggage
  • Exemplars
  • Service Level Indicator(SLI)
  • Service Level Objective(SLO)
  • Error Budget
  • RED / USE signals
  • Tail-based sampling
  • Metric cardinality
  • Log redaction
  1. Metrics는 장애의 규모와 추세를 감지하고, Logs는 사건의 문맥을 설명하고, Traces는 요청 경로의 병목을 좁힌다.
  2. 로그는 구조화 필드와 correlation/trace 연결이 있어야 장애 조사에 쓸 수 있다.
  3. 메트릭은 p95/p99와 cardinality 감각이 핵심이며, 개별 식별자는 label이 아니라 Log/Trace로 보낸다.
  4. 트레이스는 Trace ID, Span, context propagation, sampling이 맞아야 분산 요청 경로를 보여준다.
  5. 좋은 관측성은 세 신호를 따로 모으는 것이 아니라 한 신호에서 다음 신호로 이동해 운영 판단을 만들 수 있게 하는 것이다.