LabHub
배우기 러닝패스 코스

PCA — Prometheus Certified Associate

Metrics, Logs and Traces Answer Different Questions

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

메트릭은 언제, 얼마나에 답하고, 로그는 에, 트레이스는 어디에 답합니다. 셋은 서로의 상위 호환이 아니라 답할 수 있는 질문의 모양이 다른 도구입니다. 이 경계를 무시하고 메트릭에 요청 ID 를 붙이는 순간, 그것은 이미 메트릭이 아니라 압축률이 나쁜 이벤트 저장소가 됩니다.

Concept map: 언제, 얼마나 · 왜 · 어디 · 메트릭의 한계는 카디널리티가 정합니다.

왜 이게 필요했나

장애 대응 중에 대시보드를 엽니다. 패널이 40개 있습니다. CPU, 메모리, 스레드 수, GC 횟수, 커넥션 풀 크기, 힙 사용량, 초당 요청 수. 전부 그래프가 그려져 있습니다. 그런데 지금 알고 싶은 것은 하나입니다. "사용자가 실패를 겪고 있는가, 겪고 있다면 몇 퍼센트인가." 그 답이 40개 패널 어디에도 없습니다.

메트릭 설계의 문제는 데이터가 부족한 것이 아니라, 수집한 데이터로 답할 수 있는 질문과 답해야 하는 질문이 어긋나 있는 것입니다. 그래서 시작점은 메트릭 목록이 아니라 질문 목록입니다. 온콜이 새벽 3시에 던지는 질문 다섯 개를 먼저 적고, 그 다섯 개에 답하는 메트릭만 만듭니다.

어떻게 동작하나

질문 메트릭 로그 트레이스
언제부터 나빠졌는가 답한다 어렵다 못 한다
몇 퍼센트의 요청이 영향받았나 답한다 비싸다 못 한다
이 요청이 왜 실패했는가 못 한다 답한다 못 한다
느린 요청이 어느 서비스에서 시간을 썼나 못 한다 못 한다 답한다

메트릭이 못 하는 칸이 중요합니다. 그 칸을 메트릭으로 메우려고 레이블을 늘리는 순간 비용이 폭발합니다. 메트릭의 한계는 카디널리티가 정합니다.

그리고 시험에서 가장 자주 나오는 개념이 백분위입니다. 10,000건 중 9,900건이 50ms, 100건이 3,000ms 걸렸다고 하면 평균은 79.5ms 입니다. 실제로 79.5ms 에 응답받은 요청은 한 건도 없습니다. 더 나쁜 것은 민감도입니다. 느린 100건이 3,000ms 에서 6,000ms 로 두 배 나빠져도 평균은 79.5 에서 109.5 로 움직일 뿐이고, 30ms 상승은 어떤 알림 임계값도 넘지 않습니다.

여기서 두 가지가 따라옵니다.

첫째, p99 는 "사용자의 1%"가 아닙니다. 요청 100개 중 1개라는 뜻이므로 한 화면이 20개 API 를 부르면 그 화면이 p99 를 만날 확률은 18.2% 이고, 하루 200번 요청하는 사용자는 86.6% 확률로 하루 한 번은 최악 구간을 겪습니다.

둘째, 백분위는 합칠 수 없습니다. 서버 A 가 9,900건 전부 50ms, 서버 B 가 100건 전부 3,000ms 라면 p99 의 평균은 1,525ms, 요청 수 가중 평균은 79.5ms, 두 서버를 한 줄로 세운 진짜 p99 는 3,000ms 입니다. 세 숫자가 전부 다르고 앞의 둘은 아무 의미가 없습니다. 카운터는 더할 수 있고 백분위는 더할 수 없다 — 히스토그램이 존재하는 이유가 정확히 이것입니다.

SLI 는 측정 지표, SLO 는 그 목표값, SLA 는 법적 계약입니다. 30일 99.9% 목표라면 에러 버짓은 0.1%, 43,200분의 0.1% 인 43.2분입니다. 번 레이트는 그 예산을 태우는 속도의 배수라서, 14.4로 계속 태우면 50시간 만에 30일치 예산이 사라집니다.

현장에서 만나는 모습

저자의 7노드 홈랩(컨트롤 플레인 3 + GPU 워커 4)에는 kube-prometheus-stack 이 올라가 있고 Grafana 가 MetalLB 풀 10.0.0.200–215 안의 10.0.0.203 을 잡고 있습니다. 여기서 히스토그램 하나가 얼마나 비싼지를 계산해 보면 감이 옵니다.

http_request_duration_seconds_bucket
  route 120 × method 5 × le 11 × pod 40 = 264,000 시계열
  여기에 _sum 과 _count: 120 × 5 × 40 × 2  = 48,000
  ------------------------------------------------
  합계 약 312,000 시계열 — 메트릭 하나에서

메트릭 하나가 30만 시계열을 씁니다. 시계열당 대략 8KB 로 잡으면 1,000만 시계열은 80GB 입니다. "일단 다 넣고 나중에 줄이자"가 왜 위험한지는 이 곱셈 하나로 끝납니다.

같은 클러스터에서 배운 교훈 하나를 덧붙이면 — KubeVirt 를 올렸을 때 컴포넌트 상태는 전부 AllComponentsReady 였는데 VM 은 뜨지 않았습니다. "상태가 Ready"와 "실제로 동작한다"는 다른 명제입니다. 관측 가능성은 Ready 라벨이 아니라 사용자가 겪는 증상을 재료로 삼아야 합니다.

프로메테우스가 세상을 보는 방식

프로메테우스를 쓰다 보면 "왜 이렇게 동작하지" 싶은 것이 몇 가지 있는데, 대부분은 끌어오기(pull) 모델과 시계열 저장 구조에서 나온다. 그 둘을 알면 나머지가 설명된다.

대상이 사라지면 지표도 사라진다. 파드가 죽으면 그 시계열은 더 이상 안 들어온다. 그래서 up == 0 으로는 사라진 대상을 잡을 수 없다up 자체가 없기 때문이다. 사라지는 것을 잡으려면 absent() 나 서비스 디스커버리의 기대 개수와 비교한다.

값은 스크레이프 시점의 스냅샷이다. 15초마다 긁는다면 그사이의 순간적인 폭증은 안 보인다. 카운터는 누적이라 놓치지 않지만, 게이지는 긁은 순간의 값만 남는다. 순간 최대를 알아야 한다면 애플리케이션이 최대값을 카운터나 히스토그램으로 직접 남겨야 한다.

rate() 는 카운터에만 쓴다. 게이지에 쓰면 뜻이 없고, 반대로 카운터를 그냥 그리면 계속 올라가는 선만 보인다. 카운터가 재시작으로 0이 되는 것은 rate 가 알아서 보정한다.

범위 벡터의 창은 스크레이프 간격의 4배 이상. rate(x[1m]) 인데 간격이 30초면 점이 두 개뿐이라 값이 불안정하고, 하나라도 놓치면 결과가 비어 버린다. [5m] 정도가 안전한 기본값이다.

라벨이 하나라도 다르면 다른 시계열이다. 그래서 라벨을 바꾸는 배포는 그래프에 끊김을 만든다. 옛 시계열은 그 시점에 끝나고 새 시계열이 시작한다. 대시보드에서 합계를 볼 때 sum by () 로 묶어 두면 이 끊김이 덜 보인다.

저장은 로컬이고, 오래 두려면 다른 장치가 필요하다. 프로메테우스 자체는 장기 보관과 고가용성을 목표로 하지 않는다. 그 자리는 원격 쓰기와 별도의 장기 저장소가 맡는다.

다음 확인에서 볼 것

이 모듈은 개념 모듈이라 실습이 없습니다. 퀴즈로 세 신호의 경계와 백분위의 성질을 확인한 뒤, 다음 모듈에서 prometheus.yml 을 직접 쓰면서 "어떤 타깃이 살아남고 어떤 메트릭이 버려지는가"를 손으로 결정하게 됩니다.