LabHub

PCA — 프로메테우스 인증 어소시에이트 · 프로메테우스 구조와 데이터 모델 · 이론

up 자체가 신호다 — pull 과 TSDB 의 설계

LabHub 에서 이어서 보기

한 줄 요약

프로메테우스가 pull 을 고른 이유는 성능이 아니라 관측 가능성 때문입니다. 서버가 직접 긁으러 가면 "응답이 없다"는 사실 자체가 데이터가 되고, 그것이 up 메트릭입니다. TSDB 는 그렇게 들어온 샘플을 WAL 에 먼저 순차 기록하고, 2시간짜리 Head Block 에 모았다가 영속 블록으로 컴팩션합니다.

왜 이게 필요했나

push 모델에서는 데이터가 안 오는 것과 프로세스가 죽은 것을 구분할 수 없습니다. 둘 다 "아무것도 없음"으로 보이기 때문입니다. pull 모델은 서버가 시도한 기록을 남기므로 실패가 값이 됩니다.

up{job="checkout-api"} == 0orabsent(up{job="checkout-api"})

두 조건이 다른 상황이라는 점이 시험에 자주 나옵니다. up == 0 은 타깃은 발견됐는데 스크레이프가 실패한 상태이고, absent(up{...}) 은 타깃이 디스커버리에서 통째로 사라져 up 시계열 자체가 없는 상태입니다. 후자를 빼먹으면 서비스가 지워졌을 때 알림이 조용히 죽습니다.

물론 pull 이 만능은 아닙니다. 프로메테우스가 긁으러 오기 전에 끝나 버리는 단기 배치 잡은 pull 로 잡을 수 없어서 Pushgateway 라는 예외 통로를 둡니다. 예외는 예외로 남겨 두는 것이 설계의 요점입니다.

어떻게 동작하나

저장 경로는 세 층입니다.

| 계층 | 위치 | 성격 |
| --- | --- | --- |
| WAL | wal/ 세그먼트(기본 128MB) | 순차 쓰기, 크래시 복구용 |
| Head Block | 메모리 + chunks_head/ mmap | 최근 약 2시간, 쓰기 가능 |
| 영속 블록 | ULID 디렉터리 | meta.json·index·chunks/·tombstones |

새 샘플은 WAL 에 먼저 적히고 그다음 Head Block 의 memSeries 에 붙습니다. 활성 청크가 120 샘플 또는 2시간에 도달하면 봉인되어 chunks_head/ 에 mmap 파일로 내려가고, OS 페이지 캐시가 메모리 관리를 대신합니다. 컴팩션은 레벨 기반이라 2시간 블록 여러 개가 6시간으로, 다시 18시간으로 묶입니다. 삭제는 즉시 일어나지 않고 tombstone 으로 표시했다가 컴팩션 때 실제로 빠집니다.

압축은 Gorilla 논문에서 왔습니다. 타임스탬프는 delta-of-delta 로 저장해서 스크레이프가 규칙적이면 대부분 1비트로 끝나고, 값은 이전 값과 XOR 해서 앞뒤 0 비트를 잘라냅니다. 그 결과가 샘플당 평균 1.37바이트입니다. 이 숫자를 외워 두면 용량 계산이 쉬워집니다.

검색은 역인덱스입니다. 레이블 이름-값 쌍마다 시계열 ID 목록(posting list)이 정렬된 상태로 저장되어 있어서, job="prometheus"instance="localhost:9090" 의 교집합을 선형 시간에 구합니다. 레이블 셀렉터가 빠른 이유이자, 카디널리티가 늘면 이 인덱스부터 무거워지는 이유입니다.

설정에서 시험이 노리는 지점은 세 개입니다.

그리고 staleness. 타깃이 사라지면 해당 시계열에 stale marker(특수 NaN)가 붙고, 쿼리는 lookback delta(기본 5분) 안에서 가장 최근 샘플을 찾다가 stale marker 를 만나면 그 시계열을 결과에서 뺍니다. "5분이 지나야 사라진다"가 아니라 "마커를 만나면 즉시 빠진다"가 정확한 서술입니다.

현장에서 만나는 모습

레코딩 룰 2,000개를 한 번에 배포한 적이 있습니다. 룰 하나가 라우트 수만큼 시계열을 만드니 결과는 약 100만 개의 새 시계열이었고, 전부 Head Block 으로 들어가면서 WAL 이 폭증했습니다. 룰 파일은 코드처럼 보이지만 비용 측면에서는 새 메트릭을 추가하는 것과 같습니다. 배포 전에 "라우트 120개 × 윈도 5개 × 룰 50개"를 곱해 보는 습관이 필요합니다.

수집 시점에서 막는 방법도 있습니다. sample_limit 을 걸면 폭발한 대상 하나가 프로메테우스 전체를 무너뜨리는 것을 막습니다. 걸리면 그 타깃의 스크레이프가 통째로 실패하므로 값은 넉넉히 잡되 반드시 걸어 둡니다. 상한이 없으면 한 서비스의 실수가 전체 모니터링을 중단시킵니다.

용량 감각도 함께 잡아 둡니다. 시계열당 대략 8KB 로 보면 1,000만 시계열은 80GB 입니다. 홈랩의 4C/14GB 미니 PC 컨트롤 플레인에서는 이 숫자가 곧 한계선입니다.

다음 실습에서 할 것

/root/pca-scrape/prometheus.yml 을 처음부터 씁니다. global 블록, static_configs 잡, kubernetes_sd 로 파드를 찾는 잡, relabel 로 타깃을 고르고 주소를 재조립하는 규칙, metric_relabel 로 필요 없는 메트릭을 버리는 규칙, 그리고 sample_limit 가드레일까지 넣습니다. 마지막에는 그 파일을 클러스터에 ConfigMap 으로 올리고, 방금 쓴 keep 규칙이 실제로 살려 낼 파드를 하나 만들어 확인합니다.