Prometheus Pushgateway로 메트릭 송신하기 — 클라이언트와 순수 HTTP, 그리고 METHOD별 동작
짧게 살다 죽는 배치 잡의 메트릭을 Prometheus로 보내는 방법을 정리합니다. Pushgateway의 grouping key 개념, Python·Java 클라이언트 라이브러리 송신, 순수 HTTP(curl) 송신, 그리고 PUT/POST/DELETE에 따라 Pushgateway가 내부적으로 메트릭을 어떻게 처리하는지, honor_labels 설정과 staleness 운영 주의점까지 다룹니다.
Prometheus 는 pull 모델이라, 타깃을 주기적으로 긁어 메트릭을 가져갑니다. 그런데 몇 초 만에 끝나고 사라지는 배치·크론 잡은 Prometheus 가 긁을 틈이 없습니다. 잡이 살아 있는 짧은 순간에 scrape 타이밍이 맞을 리 없으니까요. 이럴 때 다리 역할을 하는 것이 Pushgateway 입니다. 잡이 종료 직전에 메트릭을 Pushgateway 로 밀어 넣으면, Pushgateway 가 그 값을 보관하고, Prometheus 는 평소처럼 Pushgateway 를 pull 합니다.
이 글에서 다루는 내용:
- Pushgateway 를 언제 쓰고, 언제 쓰면 안 되는가
- 모든 송신의 단위가 되는 grouping key
- Prometheus 클라이언트 라이브러리(Python·Java)로 송신
- 순수 HTTP(curl) 로 송신
- PUT/POST/DELETE 에 따른 Pushgateway 내부 처리 차이
- Pushgateway 자체 메트릭,
honor_labels설정, staleness 운영 주의점
1. Pushgateway는 언제 쓰는가 (그리고 언제 쓰면 안 되는가)
Pushgateway 는 메트릭 캐시입니다. 푸시된 값을 메모리에 보관했다가 /metrics 로 그대로 노출할 뿐, 합산·집계를 하지 않습니다. 같은 자리에 새 값이 오면 덮어쓸 뿐입니다.
쓰는 경우
- 수명이 짧은 배치/크론/ETL 잡 — "마지막 성공 시각", "처리 레코드 수", "소요 시간" 등을 잡 종료 직전에 송신
쓰면 안 되는 경우 (안티패턴)
- 일반 상시 서비스의 메트릭 — 이건 그냥
/metrics를 열고 Prometheus 가 pull 하게 하세요(앞선 글 《Prometheus Exporter 개발하기》 참고) - 카운트를 "여러 인스턴스에서 보내 합치고 싶을" 때 — Pushgateway 는 합치지 않습니다. 마지막 푸시가 이전 값을 덮습니다
- 고빈도·요청 단위 메트릭 — Pushgateway 가 병목·단일 장애점이 됩니다
한 줄 요약: "긁힐 만큼 오래 살지 못하는 잡" 에만 쓰세요.
2. 핵심 개념 — grouping key
Pushgateway 의 모든 송신·삭제는 그룹 단위로 일어납니다. 그룹을 식별하는 것이 grouping key 이고, 이는 URL 경로로 표현됩니다.
/metrics/job/<JOB_NAME>{/<LABEL_NAME>/<LABEL_VALUE>}job은 필수이고, 그 뒤에 라벨/값 쌍을 원하는 만큼 붙입니다.- 경로에 담긴
job+ 라벨들이 그대로 grouping key 이자, 푸시된 모든 메트릭에 붙는 라벨이 됩니다.
예를 들어 /metrics/job/backup/instance/host1 로 보내면, 그 그룹의 모든 메트릭에 job="backup", instance="host1" 라벨이 붙습니다. 이후 PUT/POST/DELETE 는 모두 이 grouping key 가 가리키는 그룹에 대해 동작합니다 — 이게 5장의 핵심 전제입니다.
라벨 값에
/가 들어가야 하면 URL 경로에 그대로 못 넣습니다. base64 변형 인코딩(/<label>@base64/<value>)을 써야 하며, 클라이언트 라이브러리는 이를 자동 처리합니다.
3. 클라이언트 라이브러리로 송신
Python — prometheus-client
배치 잡이 끝나는 시점에 별도 CollectorRegistry 를 만들어 그 잡의 메트릭만 담아 보내는 것이 정석입니다.
pip install prometheus-clientfrom prometheus_client import CollectorRegistry, Gauge, Counter
from prometheus_client import push_to_gateway, pushadd_to_gateway, delete_from_gateway
GATEWAY = "localhost:9091"
def run_backup():
registry = CollectorRegistry() # 이 잡 전용 레지스트리
last_success = Gauge(
"backup_last_success_unixtime",
"마지막 성공 시각", registry=registry,
)
processed = Counter(
"backup_records_processed_total",
"처리한 레코드 수", registry=registry,
)
# ... 실제 백업 작업 ...
processed.inc(12345)
last_success.set_to_current_time()
# 그룹(job=backup, instance=host1) 전체를 이 값으로 교체 → PUT
push_to_gateway(
GATEWAY, job="backup",
grouping_key={"instance": "host1"},
registry=registry,
)세 가지 함수가 각각 다른 HTTP METHOD 로 매핑됩니다(5장에서 상술).
push_to_gateway(GATEWAY, job="backup", registry=registry) # PUT — 그룹 전체 교체
pushadd_to_gateway(GATEWAY, job="backup", registry=registry) # POST — 같은 이름만 교체
delete_from_gateway(GATEWAY, job="backup") # DELETE — 그룹 삭제인증이 필요한 Pushgateway 라면 handler 로 처리합니다.
from prometheus_client.exposition import basic_auth_handler
def auth_handler(url, method, timeout, headers, data):
return basic_auth_handler(url, method, timeout, headers, data, "user", "secret")
push_to_gateway(GATEWAY, job="backup", registry=registry, handler=auth_handler)Java — Micrometer / client_java (보너스)
Spring Boot 에서는 PrometheusPushGatewayManager 가 주기적/종료 시 자동 송신을 처리합니다. 설정만으로 켜집니다.
management:
prometheus:
metrics:
export:
pushgateway:
enabled: true
base-url: http://localhost:9091
job: my-batch-job
push-rate: 1m
shutdown-operation: push # 종료 시 push (배치에 적합)저수준으로 직접 보내려면 client_java 의 PushGateway 를 씁니다. 메서드 이름이 METHOD 매핑을 그대로 드러냅니다.
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Gauge;
import io.prometheus.client.exporter.PushGateway;
CollectorRegistry registry = new CollectorRegistry();
Gauge duration = Gauge.build("backup_duration_seconds", "백업 소요 시간")
.register(registry);
Gauge.Timer timer = duration.startTimer();
try {
// ... 배치 작업 ...
} finally {
timer.setDuration();
PushGateway pg = new PushGateway("localhost:9091");
pg.pushAdd(registry, "backup"); // POST — 같은 이름만 교체
// pg.push(registry, "backup"); // PUT — 그룹 전체 교체
// pg.delete("backup"); // DELETE — 그룹 삭제
}4. 순수 HTTP로 송신 (curl)
클라이언트 라이브러리 없이도 됩니다. 본문은 exposition format 텍스트면 되고, URL 에 grouping key 를 인코딩합니다. curl 의 --data-binary 는 기본적으로 POST 로 전송됩니다.
# job=backup 그룹에 메트릭 하나 송신 (POST)
echo "backup_records_processed_total 12345" \
| curl --data-binary @- http://localhost:9091/metrics/job/backup
# grouping key에 라벨 추가 (job=backup, instance=host1)
echo "backup_duration_seconds 42.3" \
| curl --data-binary @- http://localhost:9091/metrics/job/backup/instance/host1여러 메트릭은 줄바꿈으로 구분하고, 본문은 반드시 개행으로 끝나야 합니다. # TYPE 선언을 함께 보내면 타입이 보존됩니다.
printf '# TYPE backup_records_processed_total counter\nbackup_records_processed_total 12345\nbackup_duration_seconds 42.3\n' \
| curl --data-binary @- http://localhost:9091/metrics/job/backupMETHOD 를 명시하면 동작이 달라집니다.
# PUT — 그룹 전체를 이 본문으로 교체
echo "backup_duration_seconds 42.3" \
| curl -X PUT --data-binary @- http://localhost:9091/metrics/job/backup
# DELETE — 그룹 전체 삭제 (본문 불필요)
curl -X DELETE http://localhost:9091/metrics/job/backup5. HTTP METHOD에 따른 내부 처리
Pushgateway 가 같은 URL(=같은 grouping key)이라도 METHOD 에 따라 저장된 그룹을 다르게 갱신합니다. 이게 가장 자주 헷갈리는 부분입니다.
상황을 가정해 봅시다. job="backup" 그룹에 현재 메트릭 A 와 B 가 저장돼 있고, 이번에 A(새 값)만 담아 보냅니다.
| METHOD | 동작 | 위 예시의 결과 |
|---|---|---|
| POST | 본문에 있는 이름의 메트릭만 교체. 그룹 내 다른 이름은 보존 | A(새 값) + B 유지 |
| PUT | 그룹 전체를 본문으로 교체. 본문에 없는 메트릭은 삭제 | A(새 값)만, B 삭제 |
| DELETE | grouping key 의 그룹 전체 삭제 | 그룹 비워짐(A·B 모두 삭제) |
즉,
- POST(=
pushadd/pushAdd) — "이 메트릭들만 갱신해 줘." 같은 그룹에 다른 잡 단계가 따로 푸시하는 메트릭이 있을 때 안전합니다. - PUT(=
push) — "이 그룹의 상태는 이게 전부야." 매 실행마다 그룹을 깨끗이 덮고 싶을 때. - DELETE(=
delete) — 잡이 끝나 더는 노출하고 싶지 않을 때 정리용.
주의: PUT/POST 의 교체 단위는 메트릭 이름(metric family) 입니다. 같은 이름이면 라벨이 달라도 그 이름 전체가 한 번에 교체됩니다. 또한 어느 METHOD든 grouping key 가 다르면 서로 다른 그룹이라 영향을 주지 않습니다.
6. Pushgateway가 스스로 만드는 메트릭
Pushgateway 는 그룹마다 푸시 상태 메트릭을 자동으로 덧붙여 노출합니다.
push_time_seconds{job="backup",...}— 그 그룹에 마지막으로 성공한 푸시 시각push_failure_time_seconds{...}— 마지막으로 실패한 푸시 시각
배치가 제때 푸시했는지(=신선도)를 이 값으로 감시할 수 있습니다.
# 1시간 넘게 새 푸시가 없으면 배치가 멈춘 것
time() - push_time_seconds{job="backup"} > 36007. Prometheus 설정 — honor_labels
Prometheus 가 Pushgateway 를 긁도록 타깃을 등록합니다. honor_labels: true 가 사실상 필수입니다.
scrape_configs:
- job_name: "pushgateway"
honor_labels: true # 푸시된 job/instance 라벨을 보존
static_configs:
- targets: ["localhost:9091"]왜 필요할까요? Prometheus 는 보통 scrape 시 job/instance 라벨을 자기 설정값으로 덮어씌웁니다(앞 글에서 본 타깃 라벨). 그대로 두면 Pushgateway 로 들어온 job="backup" 이 전부 job="pushgateway" 로 바뀌어, 어느 잡에서 온 메트릭인지 사라집니다. honor_labels: true 는 노출된 메트릭의 라벨을 우선하게 해서, 푸시할 때 지정한 job/instance 를 그대로 유지시킵니다.
8. 운영 주의점 — staleness와 정리
Pushgateway 의 가장 큰 함정은 "절대 잊지 않는다" 는 점입니다.
- 한 번 푸시된 값은 DELETE 하거나 Pushgateway 가 재시작될 때까지 영원히
/metrics에 남습니다. 끝난 일회성 잡의 마지막 값이 계속 노출되어, 대시보드가 "오래된 값"을 현재처럼 보여줄 수 있습니다 → 잡 종료 시 DELETE 로 정리하거나,push_time_seconds로 신선도를 함께 보세요. - Pushgateway 는 메트릭에 타임스탬프를 저장하지 않습니다. 노출 메트릭은 Prometheus 의 scrape 시각을 갖게 되므로, "언제 푸시됐는지"는 값이 아니라
push_time_seconds로 판단합니다. - 모든 배치 메트릭이 한 곳을 거치므로 단일 장애점입니다. 다중화·재시작 시 데이터 유실(영속화 옵션
--persistence.file미설정 시)을 고려하세요. - grouping key 가 무한정 늘어나지 않도록(예: 매 실행마다 고유
run_id를 라벨로) 카디널리티를 통제하세요.
마무리
Pushgateway 는 "pull 로는 닿지 않는 짧은 잡"을 위한 임시 보관소입니다. 송신 자체는 단순합니다 — 클라이언트 라이브러리(push/pushadd/delete)든 순수 HTTP(PUT/POST/DELETE)든, 결국 grouping key 가 가리키는 그룹을 METHOD 규칙대로 갱신하는 일입니다. 진짜 중요한 건 사용 판단입니다: 상시 서비스엔 exporter 를, 합산이 필요하면 다른 설계를 쓰고, Pushgateway 는 배치 잡에만 쓰며 honor_labels 와 staleness 정리를 잊지 마세요.
다음 단계로는 push_time_seconds 기반 신선도 알림과, 배치 성공/소요 시간을 Grafana 패널로 묶어 "어젯밤 배치가 제대로 돌았는가"를 한눈에 보는 대시보드를 만들어 보길 권합니다.