TT Lab
시작하기
배우기 러닝패스 코스

PCA — 프로메테우스 인증 어소시에이트

rate 는 왜 정수를 주지 않고, le 는 왜 버리면 안 되나

TT Lab 에서 이어서 보기

한 줄 요약

PromQL 에서 가장 많이 틀리는 두 가지는 rate 를 sum 뒤에 쓰는 것과 분위수 계산에서 le 를 버리는 것입니다. 둘 다 에러를 내지 않고 그럴듯한 숫자를 돌려주기 때문에 대시보드에 몇 달씩 살아남습니다.

왜 이게 필요했나

카운터는 그대로 그리면 우상향 직선이고 아무 정보가 없습니다. 그래서 rate 로 초당 증가율을 봅니다. 그런데 rate 는 단순한 나눗셈이 아니라 외삽(extrapolation) 을 합니다. 범위 안 첫 샘플과 마지막 샘플의 차이를 구한 뒤, 샘플이 범위 경계에 딱 붙어 있지 않은 만큼을 비례로 늘려 잡습니다. 그래서 increase 가 3.4 같은 값을 돌려줍니다. 에러가 정확히 3건이었어도 그렇습니다. 이것은 버그가 아니라 정의이고, "정확히 N건"이 필요한 계산에는 쓰지 않습니다.

카운터를 먼저 더하면 리셋을 가려낼 수 없다

파드 두 개의 요청 카운터를 라우트별로 합친다. 5분 창 안에서 한 파드가 재시작해 카운터가 0 으로 떨어졌다.

  • rate 를 먼저, sum 은 나중sum(rate(http_requests_total[5m])) by (route). 시계열마다 값이 줄어든 것을 리셋으로 알아보고 그 시계열 안에서 보정한 뒤 더한다.
  • sum 을 먼저, rate 는 나중rate(sum(http_requests_total) by (route)[5m:]). 리셋 보정이 합계에 걸린다. 재시작으로 합계가 줄면 줄기 직전 합계 전체를 증가분에 더해 요청률이 치솟고, 다른 파드의 증가에 가려 합계가 줄지 않으면 리셋을 놓쳐 그만큼 낮게 나온다.

여기서 구분할 것 두 식 모두 오류 없이 그럴듯한 숫자를 낸다. 틀린 쪽은 재시작이 없는 동안에는 맞는 값과 같아 보여서, 배포나 재시작이 있는 때에만 어긋난다.

잠깐, 예측해 보세요 요청률 그래프가 배포 시각마다 실제 트래픽과 어긋난다. 쿼리에서 무엇부터 확인하겠는가?

설명 확인 · 채점 없는 자가 점검

rate 와 sum 의 순서를 본다. rate 는 값이 줄어드는 모든 순간을 리셋으로 보고 보정하므로, 시계열마다 rate 를 먼저 구하고 집계는 그다음에 한다. 배포 시각에 맞춰 치솟는 급등은 이 순서가 뒤집혔다는 신호일 수 있다.

근거 문서

어떻게 동작하나

rate 의 세 가지 함정

첫째, 윈도가 스크레이프 간격에 비해 좁으면 안 됩니다. rate 는 윈도 안에 샘플이 최소 두 개 있어야 값을 냅니다. 스크레이프가 15초인데 윈도가 20초면 샘플이 하나뿐인 순간이 생겨 결과가 비어 버리고, 그래프에는 구멍으로, 알림에서는 "조건 불성립"으로 나타납니다. 경험칙은 윈도 ≥ 스크레이프 간격 × 4 이고 알림에는 5분 이상을 씁니다.

둘째, 순서입니다.

# 틀림 — 카운터를 먼저 더하면 파드 재시작(리셋)이 감지되지 않는다
rate(sum(http_requests_total) by (route)[5m:])

# 맞음 — rate 를 먼저, 집계는 그다음
sum(rate(http_requests_total[5m])) by (route)

카운터 리셋 보정은 시계열 하나하나에 걸어야 맞습니다. rate 와 increase 는 값이 직전 샘플보다 작아지는 순간을 모두 리셋으로 보고, 줄기 직전의 값을 증가분에 더합니다. 합계를 먼저 내면 이 규칙이 파드별 카운터가 아니라 합계에 걸리고, 결과는 경우에 따라 두 방향으로 틀어집니다.

셋째, irate 는 마지막 두 샘플만 봅니다. 대시보드에서 순간 반응을 볼 때는 쓸 만하지만 알림에는 절대 쓰지 않습니다. 노이즈 한 번에 발화합니다.

lookback delta 와 staleness

즉시 벡터 셀렉터는 평가 시점 T 에서 과거로 최대 5분(기본 lookback delta)까지 훑어 가장 최근 샘플을 씁니다. 정확히 T 시점에 샘플이 없어도 되는 이유가 이것입니다. 다만 그 사이에 stale marker 가 있으면 시계열은 결과에서 빠집니다.

histogram_quantile 의 선형 보간

le="1"     누적 9812
le="2.5"   누적 9993
목표 = 0.99 × 10000 = 9900

추정 = 1.0 + (9900 - 9812) / (9993 - 9812) × (2.5 - 1.0) = 1.729초

1.729초라는 정밀해 보이는 숫자가 나오지만, 이 버킷 안의 181건이 실제로 어디 있는지는 아무도 모릅니다. 전부 1.05초일 수도, 전부 2.4초일 수도 있습니다. 여기서 실무 규칙 세 개가 나옵니다. SLO 임계값과 정확히 같은 버킷 경계를 넣을 것, 관심 구간을 촘촘히 둘 것, 최상단 유한 버킷을 실제 타임아웃보다 크게 잡을 것. p99 가 마지막 유한 경계를 넘으면 함수는 그 경계값에 붙어 버려서 얼마나 나쁜지 알 수 없게 됩니다.

그리고 집계할 때 le 를 반드시 남깁니다.

# 틀림 — le 를 버리면 버킷이 뭉개져 의미 없는 숫자가 나온다
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (route))

# 맞음
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, route))

같은 이유로 avg(histogram_quantile(...)) 도 틀립니다. 분위수를 평균 내는 대신 버킷 카운터를 먼저 합치고 그 위에서 분위수를 구합니다.

시험이 좋아하는 나머지들

요소 핵심
offset / @ 상대 이동 / 절대 에포크 시점 지정
bool 비교 결과를 필터링 대신 0·1 값으로
on / ignoring 이항 연산의 매칭 레이블 지정
group_left / group_right 다대일·일대다 매칭 허용
unless 오른쪽과 매칭되는 시계열을 왼쪽에서 제거
서브쿼리 expr[30m:1m] — 범위:해상도
predict_linear 게이지의 선형 회귀 외삽, 용량 알림용
absent 시계열이 없을 때 1 을 반환

현장에서 만나는 모습

홈랩 대시보드에서 가장 오래 살아 있던 버그가 by (le 누락이었습니다. 값이 나오고, 그래프도 그려지고, 심지어 그럴듯하게 움직입니다. 잡아내는 방법은 하나뿐입니다 — histogram_quantile 이 들어간 쿼리를 전부 검색해서 le 가 집계 절에 있는지 눈으로 확인하는 것입니다. 대부분의 팀에서 하나쯤은 빠져 있습니다.

카디널리티 사고는 대체로 배포 직후에 계단식으로 나타납니다. prometheus_tsdb_head_series 를 그래프로 띄워 두고 배포 시각과 겹쳐 보면 원인 커밋이 바로 나옵니다. 어떤 메트릭이 범인인지는 topk(10, count by (__name__)({__name__=~".+"})) 한 줄이면 끝납니다.

p99 1.729초가 만들어지는 과정

요청 10,000건, le=1 버킷 누적 9,812건, le=2.5 버킷 누적 9,993건인 히스토그램에서 99번째 백분위를 구한다.

  1. 목표 순위를 정한다0.99 곱하기 10,000 이므로 9,900번째 요청이 답이다.
  2. 그 순위가 든 버킷을 찾는다9,900 은 9,812 와 9,993 사이이므로 1초에서 2.5초 사이 버킷에 있다. 이 버킷에 181건이 들어 있다.
  3. 버킷 안을 고르게 가정한다181건 중 88번째 자리이므로 1.0 에 1.5 의 88/181 을 더해 1.729초를 낸다. 181건이 실제로 어디 몰려 있는지는 모른다.

여기서 구분할 것 정밀해 보이는 소수점은 보간의 결과다. SLO 임계값과 같은 버킷 경계를 넣고 관심 구간을 촘촘히 두어야 이 추정을 믿을 수 있다. 집계할 때 le 를 버리면 둘째 단계부터 성립하지 않는다.

잠깐, 예측해 보세요 p99 가 가장 큰 유한 버킷 경계인 2.5초에 딱 붙어 몇 시간째 움직이지 않는다. 지연이 안정됐다고 봐도 될까?

설명 확인 · 채점 없는 자가 점검

아니다. 목표 순위가 마지막 유한 버킷 위로 넘어가면 함수는 그 경계값을 돌려준다. 실제로 얼마나 나쁜지 알 수 없는 상태이므로 최상단 유한 버킷을 실제 타임아웃보다 크게 잡아야 한다.

근거 문서

다음 실습에서 할 것

/root/pca-promql/ 아래에 쿼리 여덟 개를 파일로 씁니다. 라우트별 요청률과 에러율, le 를 지킨 p99, 카디널리티 진단, predict_linear 디스크 예측, 저트래픽에서 유령 알림을 막는 AND 게이트, up 과 absent 를 묶은 스크레이프 건강 확인, 그리고 마지막으로 두 창을 AND 로 묶은 번 레이트 표현식까지. 채점은 문자열 매칭이므로 지시한 메트릭 이름과 윈도를 정확히 쓰세요.