Prometheus - Monitoring system & time series database
오픈소스 시계열 모니터링 시스템. 서버, 애플리케이션, OS 등의 숫자 지표를 주기적으로 수집하고, 저장하고, 쿼리하는 도구다.
대부분의 모니터링 도구는 애플리케이션이 데이터를 보내는(Push) 방식이지만, Prometheus는 반대다.
Prometheus가 주기적으로 타겟의 /metrics URL을 HTTP GET으로 가져간다 (Pull).
[애플리케이션] [Prometheus]
GET /metrics <── 5초마다 ── 시계열 DB에 저장
텍스트 응답 ──> PromQL로 조회
따라서 애플리케이션은 "요청이 오면 현재 값을 텍스트로 응답"만 하면 된다. 수집 주기, 저장, 알림은 전부 Prometheus가 담당한다.
애플리케이션은 Prometheus 클라이언트 라이브러리가 제공하는 API로 값을 조작하며, 이 값들은 **앱의 프로세스 메모리(힙)**에 저장된다. 별도 파일이나 DB를 사용하지 않는다.
// 1. Registry 생성 (메트릭들을 모아두는 컨테이너)
auto registry = std::make_shared<prometheus::Registry>();
// 2. Counter 객체 생성 — 이 시점에 메모리에 할당됨
auto& counter_family = prometheus::BuildCounter()
.Name("http_requests_total")
.Help("Total HTTP requests")
.Register(*registry);
auto& request_counter = counter_family.Add({{"method", "GET"}});
// 3. 값 조작 — 내부적으로 atomic 변수를 증가시키는 것
request_counter.Increment(); // 15234 → 15235 (메모리에서 +1)
개발자가 하는 일은 Increment(), Set(), Observe() 같은 API를 적절한 시점에 호출하는 것뿐이고, 메모리 관리와 /metrics 응답 생성은 라이브러리가 알아서 처리한다.
라이브러리 API 호출 메모리 내부
───────────────── ──────────
counter.Increment() → atomic<double> value: 15235
gauge.Set(23.5) → atomic<double> value: 23.5
histogram.Observe(0.042) → bucket[0] += 1, sum += 0.042
/metrics 요청이 오면 registry가 모든 객체를 순회하며 텍스트로 직렬화해서 응답한다. 앱이 재시작되면 메모리가 날아가므로 Counter는 0부터 다시 시작하며, 이것이 Prometheus 쪽에서 rate()로 변화량을 보는 이유이기도 하다. 영속적 저장은 전부 Prometheus 서버 쪽이 담당하고, 앱은 현재 스냅샷만 메모리에 들고 있는 구조다.
대부분의 모니터링 도구가 Push 방식을 쓰는데, Prometheus가 Pull을 선택한 데는 명확한 이유가 있다.
| 관점 | Push 방식 | Pull 방식 (Prometheus) |
|---|---|---|
| 애플리케이션 복잡도 | 모니터링 서버 주소를 알아야 하고, 전송 로직 필요 | HTTP 엔드포인트 하나 열어두면 끝. 어디로 보낼지 몰라도 됨 |
| 대상 추가/제거 | 각 서버 코드를 수정해야 함 | prometheus.yml만 수정하면 됨, 서버 코드 안 건드림 |
| 장애 감지 | "안 보내는 건지 죽은 건지" 구분 어려움 | 긁으러 갔는데 응답 없으면 즉시 DOWN 판정 |
| 과부하 제어 | 서버 100대가 동시에 데이터를 쏟아부을 수 있음 | 수집 주기를 Prometheus가 관리하므로 트래픽 예측 가능 |
MonitorServer.h는 /metrics에 텍스트를 뿌려주는 게 전부이고, 누가 언제 가져가는지 전혀 신경 쓰지 않는다. 수집 주기나 대상 관리는 전부 Prometheus 쪽 설정(prometheus.yml)에서 해결된다.