Prometheus도 디스크가 꽉 차면 장애 원인이 된다: 쿼리 추적 파일로 보는 관측 시스템 운영
장애를 보기 위해 둔 Prometheus가 디스크 부족 때문에 먼저 죽지 않게 하려면 무엇을 감시하고 제한해야 할까?
Prometheus도 디스크가 꽉 차면 장애 원인이 된다: 쿼리 추적 파일로 보는 관측 시스템 운영
- 카테고리: infra_dev
- 예상 읽기 시간: 10분
- 오늘의 질문: 장애를 보기 위해 둔 Prometheus가 디스크 부족 때문에 먼저 죽지 않게 하려면 무엇을 감시하고 제한해야 할까?
- 핵심 출처:
- Prometheus 3.13.2 Release - 2026-07-30, 확인일 2026-07-31
- Prometheus PR #19289: avoid SIGBUS by explicitly allocating active query log file - merged 2026-07-30, 확인일 2026-07-31
- Prometheus Storage documentation - 공식 문서, 확인일 2026-07-31
- Prometheus HTTP API: expression query limit/timeout - 공식 문서, 확인일 2026-07-31
- Prometheus configuration: query logging and tracing correlation - 공식 문서, 확인일 2026-07-31
1. 왜 지금 봐야 하나
Prometheus 3.13.2 릴리스에는 작지만 운영적으로 중요한 버그 수정이 들어갔다. 릴리스 노트는 PromQL: Preallocate the active query tracker file to avoid SIGBUS crashes when the data disk is full라고 적고 있다. 보안 의존성 업데이트도 있었지만, 인프라 개발자에게 더 흥미로운 부분은 디스크가 가득 찼을 때 Prometheus가 PromQL active query tracker 때문에 SIGBUS로 크래시할 수 있었고, 이를 파일 사전 할당 방식으로 고쳤다는 점이다.
PR #19289 설명에 따르면 active query tracker는 queries.active라는 memory-mapped file을 사용한다. 이전에는 파일 크기를 Truncate로 맞췄는데, 일부 파일시스템에서는 이것이 실제 디스크 블록을 확보하지 않는 sparse file이 될 수 있다. 그러면 운영체제는 “파일 크기 변경 성공”이라고 답하지만, 나중에 memory map 영역에 쓰는 순간 디스크가 꽉 차 있으면 SIGBUS가 날 수 있다. 이번 수정은 zero를 쓰고 sync해서 공간을 명시적으로 확보한다. 공간이 없으면 나중에 갑자기 크래시하는 대신 시작 시점에 일반 오류로 실패한다.
이 이야기는 Prometheus 내부 구현 디테일처럼 보인다. 하지만 더 큰 교훈은 이것이다.
관측 시스템도 데이터베이스다. 그래서 디스크, 쿼리, 보존 기간, cardinality, 장애 모드를 제품 DB처럼 운영해야 한다.
작은 팀은 Prometheus를 “모니터링 도구”로만 생각하기 쉽다. 하지만 Prometheus의 로컬 TSDB는 WAL, block, chunk, compaction을 가진 on-disk database다. 여기에 대시보드, alert rule, 임시 PromQL, API 쿼리가 계속 붙는다. 디스크가 꽉 차면 단순히 “옛 metric이 안 쌓이는 문제”가 아니라 장애 분석, 알림, SLO 확인, 배포 판단이 동시에 흔들릴 수 있다.
최근 infra_dev 글은 Kubernetes native sidecar, CloudNativePG Operator, PostgreSQL logical replication, Docker Content Trust, Cloud Run multi-region health, OpenTelemetry Collector 운영을 다뤘다. 오늘은 Kubernetes나 컨테이너 보안이 아니라 Prometheus 자체를 운영 대상 데이터베이스로 보고, 디스크 부족과 쿼리 폭주가 관측 시스템을 어떻게 망가뜨리는지에 집중한다.
2. 핵심 개념
active query tracker는 왜 필요한가
Prometheus는 PromQL 쿼리를 실행한다. 쿼리는 UI, Grafana, Alertmanager rule evaluation, 자동화 스크립트, 사람이 날리는 ad-hoc 요청에서 들어온다. active query tracker는 현재 실행 중인 쿼리 정보를 추적해, Prometheus가 비정상 종료됐을 때 어떤 쿼리가 실행 중이었는지 파악하는 데 쓰인다.
중요한 점은 “쿼리 추적”도 파일 I/O를 한다는 것이다. metric 저장만 디스크를 쓰는 게 아니다. 쿼리 추적 파일, WAL, block compaction, rule evaluation 결과, 로그, container runtime 로그가 같은 볼륨 또는 같은 노드 디스크를 압박할 수 있다. 그래서 Prometheus의 디스크 문제는 저장소 설정 하나로 끝나지 않는다.
SIGBUS는 왜 무서운가
일반적인 파일 쓰기 실패라면 애플리케이션은 no space left on device 같은 오류를 받고 처리할 수 있다. 하지만 memory-mapped file은 프로세스 주소 공간에 파일을 매핑한다. 프로그램 입장에서는 메모리에 쓰는 것처럼 보이지만, 실제로는 파일-backed page에 쓰는 동작이다. 이때 디스크 블록을 확보하지 못하면 OS가 SIGBUS를 보낼 수 있고, 애플리케이션은 예외 처리를 하기 전에 죽을 수 있다.
Prometheus 3.13.2의 수정은 이 실패를 “운영자가 이해 가능한 실패”로 바꾼다. 시작 시점에 파일 공간을 실제로 확보해보고, 안 되면 명시적인 오류를 남기는 방향이다. 이것은 좋은 운영 설계의 전형이다.
- 늦게 터지는 크래시보다 일찍 드러나는 오류가 낫다.
- 불규칙한 런타임 실패보다 재현 가능한 startup failure가 낫다.
- “왜 죽었는지 모르겠다”보다 “디스크 공간이 부족해 query tracker 파일을 만들 수 없다”가 낫다.
Prometheus 로컬 스토리지는 단일 노드 DB다
Prometheus storage 문서는 로컬 TSDB가 custom on-disk format을 사용하며, ingested sample을 2시간 단위 block으로 묶고, WAL과 checkpoint를 가진다고 설명한다. 동시에 로컬 스토리지는 clustered/replicated가 아니며, drive 또는 node outage에 대해 임의로 확장 가능하거나 durable하지 않다고 명시한다.
즉 Prometheus 하나는 “가볍게 띄운 exporter 수집기”가 아니라 단일 노드 시계열 데이터베이스다. 따라서 아래 질문을 DB처럼 답해야 한다.
- retention time 또는 retention size는 얼마인가?
- WAL과 block compaction이 쓸 여유 공간은 있는가?
- node disk pressure가 오면 Prometheus와 애플리케이션 중 무엇이 먼저 영향을 받는가?
- 원격 저장소(remote write/read)를 쓴다면 로컬 보존 기간과 장애 시 재전송 정책은 무엇인가?
- Prometheus 자체가 죽었을 때 어떤 외부 알림 경로가 남아 있는가?
3. 최신 이슈와 연결
이번 릴리스의 직접적인 변화는 active query tracker 파일 preallocation이다. 하지만 이것을 “Prometheus 버그 하나 고침”으로만 보면 학습 가치가 작다. 우리가 가져가야 할 관점은 세 가지다.
첫째, 관측 시스템의 보조 파일도 장애 경로가 된다. 보통 Prometheus 디스크 계획을 할 때는 sample ingestion rate, retention, block 크기만 계산한다. 하지만 query tracker, query log, trace correlation, WAL, compaction 임시 작업, container 로그까지 포함하면 실제 위험 면적이 넓어진다.
둘째, 디스크 부족은 데이터 유실 문제가 아니라 제어면 장애다. Prometheus가 불안정하면 alert rule 평가가 지연되고, 배포 중 에러율을 확인하지 못하고, incident commander가 대시보드를 믿지 못한다. 관측 시스템은 장애를 “보는” 도구인 동시에 장애 대응의 제어면이다.
셋째, 쿼리 제한은 비용 최적화가 아니라 안정성 장치다. Prometheus HTTP API 문서는 쿼리에 timeout과 limit 파라미터가 있고, 서버 쪽 -query.timeout 같은 상한이 있음을 보여준다. limit은 vector/matrix series 수를 자를 수 있고, timeout은 장시간 쿼리를 제한한다. 대시보드가 예뻐 보여도 너무 많은 series를 매번 긁는다면, Prometheus가 장애 순간에 제 역할을 못 할 수 있다.
4. 개발자 관점 해석
“모니터링은 SRE가 알아서”가 아니다
개발자가 만든 label 하나가 Prometheus 비용을 바꾼다. 예를 들어 HTTP metric에 user_id, request_id, email, full_path처럼 cardinality가 높은 label을 붙이면 series 수가 폭발한다. series 수가 늘면 메모리, WAL, block, query cost가 같이 오른다. 그 결과 디스크 압박이 오고, 디스크 압박은 오늘 본 것처럼 관측 시스템 자체의 크래시 경로가 될 수 있다.
따라서 애플리케이션 개발자는 최소한 다음 감각을 가져야 한다.
- metric label은 로그 필드가 아니다.
- PromQL 쿼리는 공짜 SELECT가 아니다.
- 대시보드 panel 하나도 주기적으로 실행되는 운영 부하다.
- alert rule은 “장애 때만 보는 쿼리”가 아니라 계속 실행되는 background job이다.
쿼리 로그는 처벌 도구가 아니라 병목 지도다
Prometheus configuration 문서는 query logging과 tracing이 같이 켜져 있으면 query log file에 traceID/spanID를 주입해 log/trace correlation에 쓸 수 있다고 설명한다. 이것은 “누가 나쁜 쿼리를 날렸나”를 찾는 용도보다, 어떤 대시보드와 alert rule이 Prometheus를 압박하는지 찾는 병목 지도로 보는 편이 좋다.
실무에서는 아래처럼 쓸 수 있다.
- 가장 오래 걸리는 PromQL top 10을 뽑는다.
- incident 중 자동 새로고침되는 Grafana dashboard panel을 확인한다.
- alert rule 중 range vector가 과도하거나 label matcher가 넓은 쿼리를 찾는다.
- 기록 규칙(recording rule)으로 미리 계산할 쿼리와 ad-hoc으로 남겨도 되는 쿼리를 나눈다.
“디스크 80% 알림”만으로 부족하다
디스크 사용률 80% 알림은 필요하지만 충분하지 않다. Prometheus는 시간에 따라 데이터를 쌓고 compact한다. ingestion spike, cardinality spike, remote write 지연, compaction 실패, WAL 증가가 각각 다른 속도로 디스크를 압박한다. 그래서 아래와 같이 “남은 시간” 중심으로 봐야 한다.
- 현재 증가율이면 몇 시간 뒤 가득 차는가?
- WAL이 평소보다 빠르게 늘고 있는가?
- block compaction이 밀리고 있는가?
- retention size가 설정되어 있다면 실제로 여유 공간을 확보하는가?
- Prometheus 재시작 시 필요한 임시 공간이 남아 있는가?
5. 내 프로젝트에 적용할 체크포인트
디스크와 retention
- Prometheus 데이터 디렉터리가 애플리케이션 로그와 같은 볼륨을 쓰는가?
-
--storage.tsdb.retention.time또는--storage.tsdb.retention.size가 명시되어 있는가? - retention size를 쓴다면 “전체 디스크”가 아니라 WAL, compaction, query tracker, OS 여유 공간을 뺀 값으로 잡았는가?
- 디스크 사용률뿐 아니라 “남은 시간 예측” alert가 있는가?
- Prometheus pod/node가 죽어도 외부에서 알림을 받을 수 있는 경로가 있는가?
쿼리와 대시보드
- Grafana panel 자동 새로고침 주기가 너무 짧지 않은가?
- dashboard variable이 모든 label value를 매번 조회하지 않는가?
- API 쿼리에
timeout과 필요시limit을 지정하는가? - 느린 PromQL은 recording rule로 옮길 수 있는가?
- incident용 dashboard와 탐색용 dashboard가 분리되어 있는가?
metric cardinality
- label에 user id, request id, raw URL, email, tenant별 고유값이 들어가지 않는가?
- 배포 후 series 수 증가율을 확인하는 절차가 있는가?
- 새 metric 추가 PR에서 label cardinality 리뷰를 하는가?
- exporter를 추가할 때 기본 metric 전체를 켜는 대신 필요한 metric만 수집하는가?
업그레이드와 장애 대응
- Prometheus 3.13 LTS 계열을 쓴다면 3.13.2 이상으로 올릴 계획이 있는가?
- Prometheus 시작 실패 시
queries.active, data dir, disk full 오류를 확인하는 runbook이 있는가? - full disk 상황에서 Prometheus를 무작정 재시작하기 전에 어떤 파일을 지울 수 있고 없는지 정리되어 있는가?
- 로컬 TSDB를 백업 대상으로 볼지, 재생성 가능한 캐시로 볼지 팀 합의가 있는가?
6. 오늘 10분 액션
- Prometheus가 떠 있는 노드 또는 볼륨에서 데이터 디렉터리와 로그 디렉터리가 같은 디스크인지 확인한다.
- Prometheus 실행 인자에서
storage.tsdb.retention.time,storage.tsdb.retention.size,query.timeout값을 찾아 적는다. - Grafana에서 가장 자주 열리는 dashboard 하나를 골라 panel 수, refresh interval, 가장 무거워 보이는 PromQL 3개를 적는다.
- “Prometheus disk 90%” 알림이 아니라 “현재 증가율 기준 6시간 이내 full 예상” 알림을 만들 수 있는지 backlog에 넣는다.
- metric 추가 PR 템플릿에 “label cardinality 예상” 체크박스를 하나 추가한다.
7. 더 볼 자료
- Prometheus 3.13.2 Release
- Prometheus PR #19289
- Prometheus Storage
- Prometheus HTTP API
- Prometheus Configuration
중복 회피 메모
로컬 content/generated와 Supabase 최근 infra_dev 글을 확인했다. 최근 글은 Kubernetes native sidecar 생명주기, CloudNativePG Operator, Kubernetes custom metrics 기반 HPA/backpressure, PostgreSQL logical replication WAL 보존, Docker Content Trust 종료, Cloud Run multi-region health, OpenTelemetry Collector 운영을 다뤘다. 이번 글은 Prometheus와 관측성을 다루지만 Collector 파이프라인이나 OpenTelemetry semantic convention이 아니라, Prometheus 3.13.2의 active query tracker 디스크 full SIGBUS 수정에서 출발해 관측 시스템 자체의 디스크 예산, 쿼리 제한, cardinality, runbook을 운영하는 관점에 집중해 중복을 피했다.
핵심 출처
로그인하면 이 글을 북마크하고, 나만 보는 한 줄 메모를 남길 수 있어요.
댓글 0
최신순 ▾혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)