Blog
prometheuspushgatewaymetricsbatchobservabilitypromql

Prometheus Pushgateway로 메트릭 송신하기 — 클라이언트와 순수 HTTP, 그리고 METHOD별 동작

짧게 살다 죽는 배치 잡의 메트릭을 Prometheus로 보내는 방법을 정리합니다. Pushgateway의 grouping key 개념, Python·Java 클라이언트 라이브러리 송신, 순수 HTTP(curl) 송신, 그리고 PUT/POST/DELETE에 따라 Pushgateway가 내부적으로 메트릭을 어떻게 처리하는지, honor_labels 설정과 staleness 운영 주의점까지 다룹니다.

Data Dynamics2026年6月12日13 min read
This post is not yet translated. The original Korean version is shown below.

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-client
from 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/backup

METHOD 를 명시하면 동작이 달라집니다.

# 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/backup

5. HTTP METHOD에 따른 내부 처리

Pushgateway 가 같은 URL(=같은 grouping key)이라도 METHOD 에 따라 저장된 그룹을 다르게 갱신합니다. 이게 가장 자주 헷갈리는 부분입니다.

상황을 가정해 봅시다. job="backup" 그룹에 현재 메트릭 AB 가 저장돼 있고, 이번에 A(새 값)만 담아 보냅니다.

METHOD동작위 예시의 결과
POST본문에 있는 이름의 메트릭만 교체. 그룹 내 다른 이름은 보존A(새 값) + B 유지
PUT그룹 전체를 본문으로 교체. 본문에 없는 메트릭은 삭제A(새 값)만, B 삭제
DELETEgrouping 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"} > 3600

7. 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 패널로 묶어 "어젯밤 배치가 제대로 돌았는가"를 한눈에 보는 대시보드를 만들어 보길 권합니다.