☰
Prometheus接入Pushgateway实战:二进制与Docker部署、指标推送与远程写入
2026/9/30 7:28:39 网站建设 项目流程

为什么会有 Pushgateway 这个组件

Prometheus 的核心采集模型是 pull:它按scrape_interval周期性地去各个 target 的/metrics拉数据。这个模型对长期运行的服务很合适,但对另一类任务就不太友好——那些跑几秒、几十秒就结束的进程。

比如:

  • 每天凌晨跑一次的数据库备份脚本,成功与否、耗时多少,你希望有监控;
  • 一个 CI 流水线里执行的数据同步任务,处理了多少条记录;
  • 临时拉起的离线推理任务,跑完就退出。

这些进程在 Prometheus 下一次抓取之前就已经结束了,pull 模型根本抓不到。Pushgateway 就是为这种场景设计的:任务主动把指标 push 给 Pushgateway,Pushgateway 把这些指标缓存起来并长期暴露在/metrics上,Prometheus 只需要照常抓取 Pushgateway 一个 target,就能拿到所有短生命周期任务上报的数据。

这里有个关键点要先说清楚,也是很多人第一次用会误解的地方:

【关键结论】Pushgateway 不是"指标中转的时序数据库",它是一个指标的暂存与聚合点。它不会自动过期删除你推送的数据,同一条指标会一直保留到你主动删除或覆盖。这一点和 Prometheus 主库的 retention 机制完全不同,后面会专门讲怎么处理。

部署方式一:二进制直接跑

Pushgateway 是 Go 写的单文件程序,官方在 GitHub Releases 提供各平台二进制。截至我写这篇文章时,较新的稳定版本是 1.x 系列(例如 v1.9.x / v1.11.x 这类版本号,具体以你下载时 Releases 页面显示的为准,这一点我没有逐一验证每个小版本)。下载对应平台的压缩包后:

# 以 linux-amd64 为例,版本号请替换成 Releases 页面上实际的最新版wgethttps://github.com/prometheus/pushgateway/releases/download/v1.11.1/pushgateway-1.11.1.linux-amd64.tar.gztarxvf pushgateway-1.11.1.linux-amd64.tar.gzcdpushgateway-1.11.1.linux-amd64 ./pushgateway--version

默认它监听:9091,直接启动:

./pushgateway

几个在真实环境里会用到的参数:

  • --web.listen-address=:9091:监听地址,默认就是 9091;
  • --persistence.file=/var/lib/pushgateway/data:把指标持久化到磁盘。这个参数很重要,默认情况下 Pushgateway 的数据只在内存里,进程重启后所有 pushed 指标全丢。加上它之后,Pushgateway 会定期把状态写入文件,重启后恢复;
  • --persistence.interval=5m:持久化写入间隔,默认 5 分钟。

生产环境建议至少加上--persistence.file。否则一次重启,你所有定时任务的历史指标就没了,而 Pushgateway 本身又不像 Prometheus 那样有 WAL 和远程存储兜底。

想开机自启的话,用 systemd 写个 unit 就行,这部分是标准操作,不展开。

部署方式二:Docker

如果是容器化环境,直接用官方镜像prom/pushgateway:

dockerrun-d\--namepushgateway\-p9091:9091\-v/data/pushgateway:/pushgateway\prom/pushgateway:v1.11.1\--persistence.file=/pushgateway/data

注意两点:

  1. 镜像 tag 不要用latest。我见过因为latest被更新、行为有变化导致排查困难的案例,生产上固定版本 tag 更稳妥。
  2. 挂载卷的目录权限要对。官方镜像默认以非 root 用户运行(nobody或类似),如果宿主机目录属主不对,持久化文件会写失败。可以先chown一下,或者先用不挂载的方式跑通再处理权限。

两种方式怎么选,我列个对比:

方案优点缺点适用场景
二进制无依赖,启动快,资源占用低;systemd 集成简单需要自己管升级、管进程单机部署、已有 systemd 体系、边缘节点
Docker环境隔离,升级换 tag 即可;和容器编排天然契合多一层运行时;卷权限、网络要额外配置K8s / Docker Compose 环境,团队统一用容器

我自己的习惯是:如果整个监控栈(Prometheus、Grafana、Alertmanager)都在容器里跑,那 Pushgateway 也放容器里,用同一个 compose 文件管理最省心;如果是裸机部署的 Prometheus,那 Pushgateway 用二进制 + systemd 反而更少出问题。

验证服务是否起来

不管哪种方式,起来之后先确认:

curl-shttp://localhost:9091/-/healthy# 正常会返回 Pushgateway is Healthy.

访问http://localhost:9091/能看到一个简单的 Web UI,初始状态下没有任务,页面是空的。push 之后这里会列出所有 job 和对应的指标。

用 Python 推送指标

Pushgateway 的 push 接口是 HTTP 的,理论上你用requests手拼也行,但官方维护了prometheus_client这个库,封装了 push 逻辑,用起来更规范。安装:

pipinstallprometheus_client

我这边用的是prometheus_client0.20.x 系列,下面代码基于这个版本的 API。

最基础的推送长这样:

fromprometheus_clientimportCollectorRegistry,Gauge,push_to_gateway# 1. 用独立 registry,避免和进程内默认 registry 混在一起registry=CollectorRegistry()# 2. 定义指标g=Gauge('backup_duration_seconds','Duration of the nightly backup job',registry=registry)g.set(42.5)# 3. 推送push_to_gateway('localhost:9091',job='nightly_backup',registry=registry)

几点解释:

为什么要单独建CollectorRegistry?默认的 registry 是全局的,如果这个进程里还跑着 HTTP server 之类的、暴露了别的指标,用默认 registry 会把不相关的一堆指标一起推上去。用独立 registry 只推你想推的,干净。

job参数是什么?它是 Pushgateway 里分组用的标签。同一个job下的指标会归到一组。后面 Prometheus 抓取时,这个job会体现在job标签上(注意:如果 Prometheus 的 scrape_config 里也定义了job,会按规则处理,这里容易混淆,见下一节)。

推送的位置:push_to_gateway默认走的是POST /metrics/job/<job>这个路径。这个路径语义是"替换该 job 下的所有指标"——每次 push 会覆盖掉这个 job 之前的内容。如果你希望按实例区分,用grouping_key:

push_to_gateway('localhost:9091',job='nightly_backup',registry=registry,grouping_key={'instance':'db-primary-01'})

这样路径会变成/metrics/job/nightly_backup/instance/db-primary-01,不同实例的数据互不覆盖。

push 完,去 Pushgateway 的 Web UI 或curl http://localhost:9091/metrics就能看到你推的指标了,格式和 Prometheus 的 exposition format 一致。

一个容易踩的语义坑

push_to_gateway(也就是默认的 POST 到/metrics/job/...)是全量替换。这意味着:

如果你分两次推送,第一次推了指标 A,第二次只推了指标 B,那 A 会被删掉。因为 POST 的语义是"这个 job 下的指标现在就是这些"。

如果你想要"追加/只更新某一条"的语义,应该用pushadd_to_gateway,它对应 PUT 方法,只更新你推送的指标,不动其他。这个区别很多人第一次用会踩。

fromprometheus_clientimportpushadd_to_gateway# 只更新这一条,不影响该 job 下其他已有指标pushadd_to_gateway('localhost:9091',job='nightly_backup',registry=registry)

配置 Prometheus 抓取 Pushgateway

Pushgateway 本身就是一个普通的 Prometheus target,在prometheus.yml里加一段:

scrape_configs:-job_name:'pushgateway'honor_labels:truestatic_configs:-targets:['localhost:9091']

这里重点说honor_labels。

默认情况下,Prometheus 抓取时会给它抓到的所有指标加上job和instance标签,值来自 scrape_config。但 Pushgateway 上暴露的指标里,本身就带了 push 时指定的job标签(还有instance等 grouping key)。两边的job会冲突。

  • 如果honor_labels: false(默认),Prometheus 会把 Pushgateway 暴露的job标签重命名成exported_job,然后用自己的job="pushgateway"覆盖。结果就是你在 Grafana 里查job="nightly_backup"查不到,得查exported_job。
  • 如果honor_labels: true,Prometheus 保留指标自带的job,不覆盖。这样你在 Pushgateway 里推的job="nightly_backup"在 Prometheus 里就是原样的。

大多数用 Pushgateway 的场景,你都是希望保留 push 时指定的 job 的,所以honor_labels: true基本是必配项。

【踩坑提醒】如果你的 Pushgateway 上不同 job 推了同名指标、但标签不同,加上honor_labels: true后要确认这些标签组合不会互相冲突,否则会出现 duplicate metric 的抓取错误。

指标清理:Pushgateway 不会自动删

前面提过,Pushgateway 不会自动清理你 push 的数据。一个每天跑一次的备份任务,如果某天不再运行了,它的backup_duration_seconds会永远留在那里,Prometheus 也会一直抓到它,告警规则可能因此误判。

所以推送方要负起清理责任。常见做法有两个:

做法一:任务结束前主动删除自己的指标。prometheus_client提供了delete_from_gateway:

fromprometheus_clientimportdelete_from_gateway delete_from_gateway('localhost:9091',job='nightly_backup',grouping_key={'instance':'db-primary-01'})

适合"任务跑完就不需要这个指标了"的场景。但注意,如果任务异常退出(比如被 kill),这段清理代码不会执行,指标还是会残留。

做法二:用 Pushgateway 的 API 手动/脚本清理。比如:

# 删除某个 job 下的全部指标curl-XDELETE http://localhost:9091/metrics/job/nightly_backup# 删除某个 job 某个 instance 的指标curl-XDELETE http://localhost:9091/metrics/job/nightly_backup/instance/db-primary-01

可以配合一个定时清理脚本,定期删掉长时间没更新的 job。判断"长时间没更新",可以看 Pushgateway 自动加的一个指标push_time_seconds,它记录了每条 push 的时间戳。

# 该指标能反映每个 job 最后一次 push 的时间curl-shttp://localhost:9091/metrics|greppush_time_seconds

【关键结论】Pushgateway 只适合"状态型"的短任务指标。不要把它当成通用指标通道,更不要用它来做高频率、多实例的常规服务监控——那样会把 push 语义的坑放大,而且所有数据都挤在一个 target 上,抓取压力也集中。

关于远程上报的一点取舍

标题里提到"远程上报",这里想澄清一个容易混淆的概念。

Pushgateway 和 Prometheus 的remote write(远程写入)是两回事,经常被混为一谈:

  • Pushgateway:面向被监控的短任务,任务是主动方,push 给 Pushgateway,Prometheus 再 pull Pushgateway。它解决的是"pull 抓不到短任务"的问题。
  • remote write:是 Prometheus 自身把采集到的数据转发到远端存储(比如 Thanos、VictoriaMetrics、Mimir 或云厂商的托管服务)的机制,配置在 Prometheus 的remote_write段。它解决的是"本地存储容量有限、需要长期/跨集群存储"的问题。

如果你是"任务在 A 网络,Prometheus 在 B 网络,任务无法直接被 B 的 Prometheus 抓到"这种场景,做法是:任务 push 到本网络内的 Pushgateway,然后让 B 网络的 Prometheus 跨网抓这个 Pushgateway,或者用 Prometheus 的 remote write 把 B 采集的数据转发出去。选择取决于你的网络拓扑和存储需求,不是简单二选一。

# remote_write 的配置形态(示意,具体 endpoint 和认证按你的远端存储文档来)remote_write:-url:"https://your-remote-storage/api/v1/write"basic_auth:username:"xxx"password:"yyy"

这里我要明确一点:remote write 的具体认证方式、endpoint 路径、是否支持某些高级参数,取决于你所用的远端存储后端,各家实现不完全一致。这一点我没法给一个通用答案,请以对应后端的官方文档为准,我不在这里编造。

一个完整的推送示例

把前面的点串起来,写一个"任务开始记录时间 + 结束推送耗时 + 记录处理条数"的脚本:

importtimefromprometheus_clientimportCollectorRegistry,Gauge,push_to_gateway,delete_from_gateway GATEWAY='localhost:9091'JOB='data_sync'GROUPING={'instance':'sync-worker-01'}defrun_task():# 模拟实际业务time.sleep(2)return1234# 处理条数defmain():registry=CollectorRegistry()duration=Gauge('data_sync_duration_seconds','Duration of the data sync job',registry=registry)processed=Gauge('data_sync_processed_records','Number of records processed',registry=registry)last_success=Gauge('data_sync_last_success_timestamp','Unix timestamp of last successful run',registry=registry)start=time.time()try:count=run_task()exceptException:# 失败也推一次,但 success 时间戳不更新,方便告警duration.set(time.time()-start)push_to_gateway(GATEWAY,job=JOB,registry=registry,grouping_key=GROUPING)raiseduration.set(time.time()-start)processed.set(count)last_success.set(time.time())push_to_gateway(GATEWAY,job=JOB,registry=registry,grouping_key=GROUPING)if__name__=='__main__':main()

这个脚本的要点:

  • 失败时也 push,但last_success不更新。这样你可以基于time() - data_sync_last_success_timestamp做"多久没成功"的告警,比单纯看失败次数更直观。
  • 用grouping_key区分实例,避免多 worker 互相覆盖。
  • 每次 push 都是全量替换该 job+instance 下的指标,所以三个指标必须一起推,不能只推一部分。

跑完之后,去 Prometheus 里查询data_sync_duration_seconds,应该能看到数据。如果没有,按这个顺序排查:

  1. curl http://localhost:9091/metrics确认 Pushgateway 上确实有这条指标;
  2. 确认 Prometheus 的scrape_configs里 target 是 Pushgateway 的地址,且 Prometheus 的 Targets 页面显示这个 target 是 UP;
  3. 确认honor_labels配置符合预期,查询时用对标签名。

结尾

Pushgateway 解决的问题很具体——pull 模型抓不到短生命周期任务。它的部署不复杂,二进制和 Docker 两种方式按你的环境选即可。真正需要花心思的是语义:push 是全量替换还是追加、指标谁来清理、honor_labels怎么配、别把它当成常规服务监控的通道。

如果你只是想让一个定时脚本的结果能被 Prometheus 看到,上面这套流程足够用了。再往上,如果涉及多集群、长期存储、跨网络,那就是 remote write 和远端存储的领域,Pushgateway 只负责它该负责的那一段。

文中涉及具体版本号的地方(如 Pushgateway 的 1.x 小版本、prometheus_client的 0.20.x),请以你实际下载和安装时看到的版本为准,我在文中没有逐一验证每个小版本的行为差异。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询