프로메테우스를 처음 올릴 때 scrape_interval은 15초로 할지 1분으로 할지, 디스크는 몇 GB를 잡을지, 보존 기간은 며칠로 할지부터 막히죠. 결론부터 말하면 디스크는 "활성 시계열 수 ÷ 간격 × 보존 기간(초) × 1~2바이트"로 어림하고, 간격보다 시계열 수를 먼저 줄이는 게 훨씬 효과가 커요. 50만 시계열을 15초 간격으로 15일 보관하면 블록만 40~81GiB이고, 디스크는 그보다 20% 넉넉하게 잡아요.
스크레이프·TSDB·rate()가 안에서 어떻게 도는지는 알고 있다고 보고, 이 글은 설정값을 고르는 데 필요한 숫자만 다뤄요. 시계열 수 세는 법, 간격·보존 기간 판단표, rate() 범위 고르기, 그래프가 비었을 때의 확인 순서, 도입 전에 따질 점 순서예요. 기준은 2026-10-26 현재 최신 안정판 Prometheus 3.15.1과 공식 문서 latest예요.
먼저 셀 것, 내 서버의 시계열 수
용량 계산의 첫 칸은 활성 시계열 수예요. 시계열 하나는 지표 이름과 라벨 값 조합 하나라서, 라벨이 붙은 지표의 시계열 수는 라벨마다 나올 수 있는 값 개수를 곱한 값이에요.
예시로 웹 서버 파드 20개가 요청 지연 히스토그램 하나를 노출한다고 해 볼게요. 라벨은 method 4종, code 5종, route 30종이에요.
- 라벨 조합: 4 × 5 × 30 = 600개
- 히스토그램은 조합마다
_bucket이 버킷 수만큼, 그리고_sum·_count가 하나씩 생겨요. 파이썬 공식 클라이언트의 기본 버킷은+Inf까지 15개라서 조합 하나에 17개예요. - 파드 하나: 600 × 17 = 10,200개, 파드 20개: 204,000개
같은 라벨로 카운터 하나만 두면 600 × 20 = 12,000개예요. 지표 하나가 히스토그램이냐 카운터냐에 따라 17배가 갈려요.
공식 계측 가이드는 지표 하나의 카디널리티를 10 아래로 두고, 100을 넘거나 그렇게 커질 수 있으면 차원을 줄이거나 분석을 범용 처리 시스템으로 옮기라고 해요. 대부분의 지표는 라벨이 없어야 한다고도 적었어요. 위 예시의 route 30종은 이미 이 기준을 넘어요. 그래서 라우트는 원문 경로(/users/1234) 대신 템플릿(/users/{id})으로 넣고, 히스토그램은 꼭 필요한 곳에만 붙여요.
운영 중인 서버라면 계산 대신 직접 물어보면 돼요. HTTP API 문서의 /api/v1/status/tsdb가 head 블록의 시계열 수와 지표별·라벨별 상위 목록을 줘요.
# 전체 활성 시계열 수와 시계열이 가장 많은 지표 상위 10개
curl -s localhost:9090/api/v1/status/tsdb | \
jq '.data.headStats.numSeries, .data.seriesCountByMetricName[:10]'
# 값 종류가 가장 많은 라벨 (user_id·path 같은 게 위에 있으면 의심)
curl -s localhost:9090/api/v1/status/tsdb | jq '.data.labelValueCountByLabelName[:10]'
응답의 minTime·maxTime은 밀리초 Unix 시각이라 타임스탬프 변환기에 넣으면 바로 읽혀요. 초로 착각했을 때 생기는 일은 초·밀리초 타임스탬프 혼동 버그에 있어요.
디스크 용량 계산, 시계열 수 × 간격 × 보존 기간
시계열 수가 나오면 나머지는 곱셈이에요.
- 초당 샘플 수 = 500,000 ÷ 15초 = 33,333
- 보존 15일 = 1,296,000초 → 샘플 432억 개
- 1바이트면 40.2GiB, 1.5바이트면 60.3GiB, 2바이트면 80.5GiB
아래는 같은 50만 시계열에서 간격과 보존 기간을 바꿨을 때 샘플당 1.5바이트로 계산한 값이에요.
간격을 15초에서 60초로 늘리면 계산상 4분의 1이지만, 이웃 샘플끼리 압축되는 구조라 샘플당 바이트가 오히려 늘 수 있어요. 반면 route 30종을 템플릿화해 10종으로 줄이면 그 지표가 정확히 3분의 1이 되고 해상도는 그대로예요.
GiB와 GB 단위가 헷갈릴 때는 데이터 용량 변환기로 1000·1024 기준을 나란히 보세요. 프로메테우스의 retention.size는 문서에 "2의 거듭제곱 기준, 1KB = 1024B"라고 적혀 있어서, 100GB라고 쓰면 실제로 100GiB예요.
scrape_interval 판단표, 15초·30초·60초
전역 기본값은 설정 문서 기준 scrape_interval: 1m, scrape_timeout: 10s, evaluation_interval: 1m이에요. 공식 저장소의 예제 prometheus.yml이 15초로 적혀 있어서 15초를 기본으로 아는 경우가 많은데, 지정하지 않으면 1분이에요.
| 상황 | 권하는 간격 | 이유 |
|---|---|---|
| 웹·API 서비스 지표, 5분 안에 알림이 필요 | 15초 | rate(x[1m])에 샘플 4개가 들어가 1분 단위 반응이 가능해요 |
| 노드·DB 같은 인프라, 알림 기준이 5분 이상 | 30초 | 디스크가 15초의 절반이고 rate(x[2m])이면 샘플 4개예요 |
| 용량 추세·비용 지표, 주 단위로 보는 것 | 60초 | 기본값. rate(x[4m]) 이상으로 봐야 해요 |
| 응답에 수 초 걸리는 무거운 익스포터 | 60초 이상 + scrape_timeout 상향 | 타임아웃은 간격보다 길 수 없어요 |
| 수 초 단위로 살았다 죽는 배치 | 스크레이프 대신 Pushgateway 검토 | 간격보다 짧게 사는 작업은 아예 안 잡혀요 |
간격은 잡(job)마다 다르게 줄 수 있어요. 알림이 걸린 서비스 잡만 15초, 나머지는 30초·60초로 두면 디스크와 CPU가 직접 줄어요. 간격이 다른 잡을 한 패널에 그릴 때는 rate() 범위를 가장 긴 간격 기준으로 잡아요.
rate() 범위 고르기, 간격의 몇 배인가
rate()는 창 안에 샘플이 최소 2개 있어야 값을 내요. 공식 함수 문서에는 이 조건이 명시돼 있지 않지만, PromQL 소스 functions.go의 외삽 계산 함수가 샘플이 2개 미만이면 그 시계열을 결과에서 빼요. 창 안에 들어오는 샘플 수는 대략 "범위 ÷ 간격"이고, 스크레이프가 한 번 늦거나 빠지면 하나 줄어요.
그래서 범위 ≥ 간격 × 4를 기본으로 잡으면 스크레이프 한두 번이 빠져도 샘플이 2개 이상 남아요. 60초 간격에 rate(x[1m])을 쓰면 창에 샘플이 1개뿐인 순간이 많아서 그래프가 점선처럼 끊겨요.
범위를 너무 길게 잡는 것도 비용이 있어요. 범위 벡터는 시계열마다 그 범위의 샘플을 모두 메모리에 올려요. 시계열 10만 개에 15초 간격으로 x[1h]를 고르면 시계열당 240개, 합쳐 2,400만 샘플이에요. 명령줄 플래그 문서의 --query.max-samples 기본값이 5,000만이라 절반을 한 번에 써요. 대시보드에서 긴 범위가 필요하면 레코딩 규칙으로 rate(x[5m])을 미리 계산해 두고 그 결과를 집계하는 쪽이 싸요.
| 용도 | 함수와 범위 | 메모 |
|---|---|---|
| 알림 규칙 | rate(x[5m]) 이상 | irate()는 짧은 출렁임이 for 대기를 초기화해요 |
| 실시간 대시보드 | 간격 × 4 이상 (그라파나라면 $__rate_interval) | 변동이 큰 카운터만 irate() |
| 하루·한 주 합계 표시 | increase(x[1d]) | 외삽 때문에 소수점이 붙는 추정치예요 |
| 여러 파드 합계 | sum by (code) (rate(x[5m])) | 집계는 항상 rate 다음에 |
알림·집계 순서 권고는 공식 함수 문서에 그대로 있어요. 먼저 rate를 해야 타깃 재시작에 따른 카운터 리셋을 감지해요.
보존 기간 설정, retention.time과 retention.size
보존 기간 기본값은 15일이에요. 최신 문서에서 바뀐 점이 하나 있어요. 지금까지 쓰던 --storage.tsdb.retention.time·--storage.tsdb.retention.size 플래그는 사용 중단(deprecated) 으로 표시됐고, 설정 파일의 storage.tsdb.retention 항목이 플래그보다 우선해요. 설정 파일 쪽은 런타임 재로드가 돼서 재시작 없이 보존 기간을 바꿀 수 있어요.
# prometheus.yml — 시간·용량 둘 다 걸면 먼저 닿는 쪽이 적용돼요
storage:
tsdb:
retention:
time: 30d
size: 80GB # 2의 거듭제곱 기준이라 80GiB. 디스크 100GiB의 80%
판단 기준은 이렇게 잡아요.
- time만 걸기: 디스크가 넉넉하고 "며칠 치를 본다"는 요구가 분명할 때. 대신 시계열이 폭증하면 디스크가 먼저 찰 수 있어요.
- size만 걸기: 디스크가 고정된 볼륨이고 넘치면 안 될 때. 보존 일수는 그날그날 달라져요.
- 둘 다 걸기: 실무 기본값으로 무난해요. 평소엔 time이 지우고, 폭증 때는 size가 안전판이 돼요.
알아 둘 동작이 세 가지 있어요. 첫째, 컴팩션 블록은 보존 기간의 10%와 31일 중 작은 쪽까지 커지고, 블록 안 샘플이 모두 만료돼야 지워져요. 그래서 디스크에는 보존 기간보다 조금 더 남아요. 둘째, 만료 블록 정리는 최대 2시간 늦을 수 있어요. 셋째, size에는 WAL과 chunks_head도 포함되지만 지울 수 있는 건 블록뿐이라, 디스크는 이 둘의 최고치보다는 커야 해요.
| retention.time | 컴팩션 블록 최대 길이 | 이렇게 쓰는 경우가 많아요 |
|---|---|---|
| 15d (기본) | 1.5일 | 장애 대응용 단기 보관 |
| 30d | 3일 | 월간 리포트를 로컬에서 직접 볼 때 |
| 90d | 9일 | 분기 추세. 디스크가 15일의 6배예요 |
| 365d | 31일(상한) | 단일 노드로는 드물어요. 장기 보관은 원격 저장소로 |
운영에서 자주 나는 문제와 확인 순서
"그래프가 비었다", "값이 이상하다"는 신고가 오면 수집 → 한도 → 카디널리티 → 쿼리 → 저장소 순으로 봐요. 앞 단계에서 걸리면 뒤 단계는 볼 필요가 없어요.
각 단계에서 돌릴 쿼리예요. 서버가 스크레이프마다 직접 남기는 up·scrape_* 시계열을 쓰는데, 목록은 공식 Jobs and instances 문서에 있어요.
# 1. 죽은 타깃
up == 0
# 2. 타임아웃의 80%를 넘긴 스크레이프 (곧 실패로 넘어갈 후보)
scrape_duration_seconds / scrape_timeout_seconds > 0.8
# 3. sample_limit 의 90%를 넘긴 타깃 (한도가 없으면 0 이라 > 0 으로 걸러요)
scrape_samples_post_metric_relabeling / (scrape_sample_limit > 0) > 0.9
# 4. 지난 1시간 동안 새로 생긴 시계열이 많은 타깃 상위 10개
topk(10, sum_over_time(scrape_series_added[1h]))
scrape_timeout_seconds와 scrape_sample_limit은 기본으로 생기지 않고 --enable-feature=extra-scrape-metrics를 켜야 생겨요. scrape_sample_limit은 한도를 안 건 타깃에서 0이라, 그냥 나누면 무한대가 나와 모든 타깃이 걸려요. 그래서 위 쿼리는 0보다 큰 것만 남긴 뒤 나눠요.
3번이 특히 헷갈려요. sample_limit은 metric_relabel 뒤 샘플 수로 세는데, 넘으면 그 스크레이프 전체가 실패로 처리돼요. 일부만 버리는 게 아니라 up이 0이 되고 그 타깃의 모든 지표가 끊겨요. 배포 직후 특정 서비스 그래프가 통째로 사라졌다면 새 라벨이 한도를 넘겼는지부터 봐요. label_limit·label_value_length_limit도 넘으면 똑같이 스크레이프 전체가 실패해요.
5번에서 자주 나오는 원인은 앞 절의 두 가지예요. 범위가 간격의 4배보다 짧거나, rate(sum(x)[5m:])처럼 합친 뒤 rate를 씌운 경우예요. 큰 범위를 고른 쿼리는 query.max-samples를 넘으면 실패하고, 기본 --query.timeout 2분을 넘겨도 끊겨요.
6번에서는 디스크 사용률과 재시작 시간을 봐요. 재시작 때 WAL을 다시 읽어 head를 복원하는 동안은 수집도 쿼리도 안 되고, 시계열이 많을수록 길어져요. NFS(AWS EFS 포함)는 스토리지 문서가 지원하지 않는다고 못 박았으니 로컬 디스크나 블록 볼륨을 써요.
metric_relabel_configs:
# 원문 경로가 들어간 라벨은 저장 전에 뗀다
- regex: "raw_path|user_id"
action: labeldrop
# 쓰지 않는 히스토그램 버킷은 지표째 버린다
- source_labels: [__name__]
regex: "grpc_server_handling_seconds_bucket"
action: drop
relabel 정규식은 앞뒤가 고정(anchored)돼서 user만 쓰면 user_id에 안 맞아요. 정규식 테스트 도구에서 ^...$로 감싸 미리 확인해 보세요.
도입 전에 따질 것
프로메테우스 단일 서버가 맞는지는 아래 네 가지로 갈려요.
- 정확한 개수가 필요한가: 공식 개요 문서는 요청당 과금처럼 100% 정확도가 필요하면 프로메테우스가 좋은 선택이 아니라고 밝혀요. 과금·정산 숫자는 따로 세요.
- 보존 기간이 몇 달 이상인가: 로컬 저장소는 단일 노드의 확장성과 내구성에 묶여 있어요. 90일 이상 보거나 디스크 장애에도 데이터가 남아야 하면 원격 쓰기(remote write)로 장기 저장소를 붙이는 걸 처음부터 설계에 넣어요.
- 고가용성이 필요한가: 흔한 방법은 같은 설정의 서버 두 대가 같은 타깃을 따로 긁는 것인데, 이러면 디스크·CPU·타깃 부하가 모두 2배예요. 앞의 용량 계산에 2를 곱해 두세요.
- 활성 시계열이 몇 개인가: 공식 문서에 서버 한 대의 고정 상한은 없어요.
numSeries를 날마다 기록해 증가 추세를 보는 게 가장 확실한 근거예요.
자주 묻는 질문
scrape_interval을 지정하지 않으면 몇 초인가요?
전역 기본값은 1분이에요. 공식 예제 설정 파일이 15초로 적혀 있어서 헷갈리는데, 설정 문서의 기본값은 scrape_interval: 1m, scrape_timeout: 10s예요. 1분 간격에서는 rate() 범위를 4분 이상으로 잡아야 그래프가 끊기지 않아요.
디스크를 줄이려면 간격을 늘리는 게 나은가요, 라벨을 줄이는 게 나은가요?
라벨(시계열 수)을 줄이는 쪽이에요. 간격을 늘리면 해상도와 알림 반응 속도까지 잃지만, 원문 경로·사용자 ID 라벨을 떼면 해상도는 그대로 두고 시계열이 몇 배씩 줄어요.
retention.size를 디스크 크기와 똑같이 잡으면 안 되나요?
안 돼요. 컴팩션 중에는 원본 블록과 합친 새 블록이 동시에 디스크에 있어서 잠깐 size를 넘어요. 공식 문서는 디스크의 최대 80~85%를 권하고, 나머지 15~20%를 이 임시 공간으로 남기라고 해요.
increase()로 하루 주문 수를 세도 되나요?
추세용으로는 괜찮지만 정산 숫자로는 안 돼요. 창 끝까지 외삽한 추정치라 정수 카운터에도 소수점이 붙고, 스크레이프가 빠진 구간에서 실제 값과 어긋나요. 정확한 개수는 주문 DB에서 세요.
보존 기간을 줄였는데 디스크가 바로 안 비워져요.
정상이에요. 만료된 블록 정리는 백그라운드에서 돌아 최대 2시간 늦을 수 있고, 블록 안의 샘플이 모두 만료돼야 지워져요. 설정 파일에서 바꾼 보존 기간은 다음 블록 재로드(1분 안 또는 컴팩션 완료 시)부터 적용돼요.
정리
- 디스크는 활성 시계열 수 ÷ 간격 × 보존 초 × 1~2바이트로 어림해요. 50만 시계열·15초·15일이면 40~81GiB이고, 디스크는 그 값을 0.8로 나눠 잡아요.
- 줄일 때는 간격보다 라벨부터. 라벨 값 개수의 곱이 시계열 수이고, 히스토그램은 조합마다 버킷 수 + 2개예요.
rate()범위는 간격 × 4 이상, 집계는 rate 다음에, 알림에는irate()대신rate()를 써요.- 보존 기간은 설정 파일의
storage.tsdb.retention에 time과 size를 둘 다 걸어요. 플래그는 사용 중단이에요. - 그래프가 비면
up→ 타임아웃 비율 → sample_limit → 시계열 폭증 → 쿼리 → 저장소 순으로 봐요.
용량 단위를 바꿔 볼 때는 데이터 용량 변환기, API 응답의 밀리초 시각을 읽을 때는 타임스탬프 변환기를 쓰면 편해요. 타임존을 섞어 집계할 때의 함정은 KST·UTC 저장과 날짜 집계 글에 따로 정리해 뒀어요.
더 깊이 읽기
이 글은 실무에서 판단할 때 필요한 것 위주로 정리했어요. 같은 주제의 내부 동작 원리는 devboi 기술 블로그의 프로메테우스 동작 원리 — 스크레이프·TSDB·rate() 계산에서 도식과 코드로 단계별로 풀었어요.