
Prometheus的监控体系里绝大多数场景都是基于Pull模式工作的Prometheus Server定期去各Exporter抓取指标。但在实际生产环境中总有那么一些任务没法直接暴露HTTP端口让Prometheus来拉取比如短生命周期的批量任务、位于防火墙内部的定时脚本、或者通过NAT访问的内网机器。这时候Pushgateway就成了那个关键的“中转站”它专门用来接收这些无法直接拉取的指标再把数据提供给Prometheus。我最早接触Pushgateway是因为一批跑在客户现场内网的离线计算任务。那批机器不对外暴露任何端口只允许主动往外连Prometheus根本够不着它们。我当时的第一反应是加个反向代理或者搞个SSH隧道后来一想这不就是Pushgateway的典型使用场景么。部署一台Pushgateway作为统一接入点脚本跑完把指标推上去Prometheus只管从Pushgateway拉数据一台Pushgateway就能把上百台内网机器的指标全部盘活。这篇文章就围绕Pushgateway来写从它解决的痛点、适用场景讲起再给出一套可以直接抄作业的Docker部署方案最后把配置Prometheus抓取、脚本推送指标这些实操细节配着踩坑经验一起说清楚。适合正在搭Prometheus监控体系、但被“推模式”指标难住的运维和开发朋友参考。1. Pushgateway在监控体系中的定位与实际应用场景1.1 为什么需要PushgatewayPull模型的盲区Prometheus的设计哲学是“只拉不推”这是它的优势也让它在面对某些场景时特别无力。周期性的批量任务是最典型的例子。比如一个每天凌晨跑一次的ETL脚本数据清洗要跑40分钟等它运行完才发现指标异常再处理就晚了。这类任务的生命周期太短进程跑完就退出了端口也关闭了Prometheus根本来不及抓数据。另一个典型场景是位于隔离网络里的机器生产网段和监控网段做了物理隔离只允许主动外联Prometheus想拉也拉不到。Pushgateway就是专门用来补这个盲区的中间层。它本身是一个常驻服务对外提供HTTP接口各种任务在执行到关键节点时用HTTP POST把指标数据主动推送到Pushgateway暂存。Prometheus Server依然按照自己的节奏去Pushgateway拉取数据整体架构变成了“任务主动推 → Pushgateway暂存 → Prometheus周期拉”。这个中转设计有个额外好处指标数据有了一份持久化的副本。任务就算已经跑完退出Prometheus还是能拉到上一次推送的历史数据这对于事后排查或者看趋势特别有用。1.2 适用与不适用场景的边界判断Pushgateway不是万能的选型时一定要分清场景。适合用Pushgateway的场景有这么几类无法通过Prometheus直接拉取的网络环境比如跨机房隔离、只有出方向权限的内网机器生命周期极短的批处理任务比如定时报表生成、数据同步Job还有需要在指标上附带自定义标识的任务比如按用户维度去统计API调用量任务每处理一个用户就推一条带user_id标签的指标。不适合用Pushgateway的场景也很明确。高并发网关注入型监控就不适合每来一个请求就直接推一条指标Pushgateway扛不住这种量级。服务端应用本身的性能指标也不适合一个正常运行的Web服务完全可以直接暴露/metrics接口没必要绕一圈推给Pushgateway。再有就是原本就是多实例分布式部署、需要按instance维度区分数据的场景虽然也能用Pushgateway实现但会丢失Prometheus原生按实例自动发现的能力反而增加了配置维护成本。1.3 Pushgateway与其他Push方案的区别聊Pushgateway很容易跟exporter、node_exporter搞混毕竟都是Prometheus生态里的组件。简单区分一下node_exporter这类是“采集器”它们主动暴露一个HTTP端口的/metrics路径Prometheus来拉取Pushgateway是“接收器”它被动接收外面推来的数据Prometheus再去它那里拉。从数据流向上看普通的Exporter是“被拉模式”Pushgateway节点接收完外部推送后就变成了“被拉模式的代理层”对外表现成一个Exporter但实际上数据源是临时推上来的。另外还有一点容易忽略Pushgateway本身不是数据持久化存储它只是短期缓存。Prometheus每次从Pushgateway拉完数据这些数据就进入了Prometheus自己的TSDBPushgateway本地的那份只是留着供下一次拉取用。如果Prometheus因为故障停机超过一段时间Pushgateway本地暂存的数据也会因为超过保留窗口被清理所以不要指望Pushgateway能承担历史数据的保存职责历史数据归Prometheus管。2. Pushgateway部署安装Docker Compose一步到位2.1 版本选择与镜像说明Pushgateway的版本迭代不算激进最新稳定版和两年多前的老版本在核心功能上没有太大差别主要改动集中在对HTTP API的细节优化和启动参数的扩展上。选择时有个原则跟Prometheus主服务保持一个大版本周期内即可不必刻意追求追新。我日常使用的是Prometheus 2.53.0搭配Pushgateway 1.6.2的组合稳定跑了大半年没有出过问题。Docker镜像方面用官方仓库的prom/pushgateway即可不需要自己构建。有一点要注意官方镜像默认以nobody用户运行工作目录是/etc/pushgateway如果后续想配置持久化存储需要挂载数据卷时要注意目录权限问题否则容器启动后没有写权限。2.2 Docker Compose完整部署文件直接给出一份我实际在用的Docker Compose配置把关键参数都配好了version: 3.8 services: pushgateway: image: prom/pushgateway:v1.6.2 container_name: pushgateway restart: always ports: - 9091:9091 volumes: - ./data:/etc/pushgateway command: - --persistence.file/etc/pushgateway/data/pushgateway.dat - --persistence.interval5m - --web.listen-address:9091 - --web.telemetry-path/metrics healthcheck: test: [CMD, wget, --spider, -q, http://localhost:9091/-/healthy] interval: 30s timeout: 5s retries: 3 start_period: 10s这条命令里几个参数值得展开说一下。--persistence.file用来指定持久化文件路径。默认情况下Pushgateway把数据存在内存里进程重启数据就全没了。如果配上这个参数Pushgateway会定期把数据落盘重启后自动从磁盘恢复。对于生产环境这是必配项否则一次不小心重启容器所有指标全清零了。--persistence.interval5m是落盘间隔。理论上间隔越短数据越安全但频繁写盘会磨损磁盘对SSD寿命也有轻微影响。我实际用下来5分钟是个比较平衡的值重启最多丢5分钟的数据可接受。--web.telemetry-path/metrics是把Pushgateway的自身指标暴露在/metrics路径下。这样Prometheus的targets页面里能看到Pushgateway这台机器的运行状态比如它接收了多少批数据、处理请求耗时等。如果不显式配置默认值就是这个路径写出来是为了让配置更明确。healthcheck那块是给容器加了个健康检查探针/-/healthy是Pushgateway官方提供的健康检查端点curl通了才认为容器健康。加了这个之后编排平台能更精确地感知容器状态有问题自动重启。2.3 部署后的验证与端口说明配置文件准备好后在当前目录执行docker compose up -d启动后先检查容器状态docker ps | grep pushgateway看到状态是Up且healthy就说明启动成功了。然后访问http://服务器IP:9091正常能看到Pushgateway的Web界面这个页面上会列出一个指标表格包括默认暴露的pushgateway_build_info等自身指标同时空白的指标列表也暗示着“还没有任何外部数据推送”。端口方面Pushgateway默认监听9091Web UI和推送API共用这一个端口不用再额外开别的口。需要特别注意的是如果服务器上有防火墙记得放行9091端口的入站规则否则外部推送方和Prometheus都连不进来。还有一个细节Pushgateway的Web界面默认不带任何认证。它的设计初衷是给内部可信网络使用的如果直接暴露到公网任何人往9091端口POST数据都能写入指标这不光是数据污染的问题更严重的是可能被塞入大量垃圾指标拖垮Prometheus的存储。生产环境一定要把9091端口限制在可信网段或者用反向代理加一层BasicAuth认证。3. 配置Prometheus抓取Pushgateway指标3.1 prometheus.yml中的scrape_configs配置Pushgateway部署好之后要让Prometheus知道去哪里拉数据需要修改Prometheus的配置文件prometheus.yml。在scrape_configs段里增加一个jobscrape_configs: - job_name: pushgateway static_configs: - targets: - pushgateway:9091 honor_labels: true scrape_interval: 30s # 推荐加上指标修剪把没用的默认指标过滤掉 metric_relabel_configs: - source_labels: [__name__] regex: pushgateway_(.*) action: keep有几处细节必须处理好不然会埋雷。honor_labels是Pushgateway配置里最关键的一个选项没有之一。默认情况下Prometheus抓取时序数据时会自动附加instance和job标签把target地址写成instancepushgateway:9091、jobpushgateway。如果推送的指标里本来就带instance标签默认行为会以Prometheus侧为准把推送来的标签覆盖掉。honor_labels: true的作用就是告诉Prometheus“谁的标签谁做主”数据里自带的instance和job标签保留原值不强制覆盖。为什么要保留因为在实际使用中往往是多个任务共用一台Pushgateway每个任务推送的数据都带了自己特有的instance标签比如instancebatch-server-01这样Prometheus就能区分不同来源的指标。一旦被默认行为顶掉所有指标都会被强行打上Pushgateway的地址数据全部混在一起完全没法按任务维度区分配置告警。scrape_interval这里我设置成了30秒。Pushgateway的数据是任务“推”上来的不是实时的所以没必要像抓普通Exporter那样设成15秒甚至5秒。30秒足够平滑也能减小Pushgateway的瞬时压力。metric_relabel_configs这一段是我后来加上的优化。Prometheus抓取Pushgateway时除了业务指标还会把Pushgateway自身的运行指标一并拉走比如pushgateway_build_info、pushgateway_http_requests_total这些。如果Prometheus还配置了抓取Pushgateway的独立job来监控它自己那么这些指标就会重复采集两遍浪费存储。用relabel规则只保留业务指标避免重复数据。3.2 让配置生效与检查抓取状态改完配置之后需要让Prometheus重新加载配置。Prometheus支持两种方式向它的/-/reload端点发送POST请求或者直接给它发SIGHUP信号。我习惯直接执行docker exec prometheus kill -HUP 1然后在Prometheus Web界面的Status → Targets页面找到pushgateway这个job看它的状态是否变成UP如果还是DOWN点进去看一眼错误信息大多都是网络不通或者端口没放行。还有一个更直观的验证方法在Status → Graph页面里搜索pushgateway_build_info如果配置正确且已抓到数据这里应该能查到一条记录。3.3 关于数据保留时长的思考配置Prometheus抓取Pushgateway时容易忽略数据保留问题。Pushgateway里暂存的数据在Prometheus拉取之后并不会自动清掉它一直存在直到被新数据覆盖或者手动删除。这意味着如果某个任务停止推送了Prometheus每次还是能从Pushgateway拉到旧值导致告警一直处于触发状态而无法自动恢复。解决这个问题的思路不是在Pushgateway侧清理而是在Prometheus的告警规则里处理。针对从Pushgateway获取的指标做告警时要结合时间维度判断数据的新鲜度。比如这样一个告警规则groups: - name: pushgateway_alerts rules: - alert: BatchJobFailed expr: | my_batch_success 0 and (time() - my_batch_timestamp) 300 for: 5m labels: severity: warning annotations: summary: Batch job failed to run successfully这里time() - my_batch_timestamp是拿当前时间减去指标里记录的时间戳如果大于300秒说明这已经是旧数据了不再触发告警。这个“带新鲜度条件的告警”写法在Pushgateway场景下几乎是必备技能希望你能避开这个坑。4. 脚本推送指标从Shell到Python的完整实操4.1 Pushgateway的HTTP API协议解析Pushgateway的推送API本身很简单一共就三件事推送、删除、查看。核心是PUT和POST两个方法。向Pushgateway推送指标本质上就是提交一段Prometheus文本格式的指标数据。光有指标内容还不够URL路径里还携带了job标签用于区分不同的任务来源。例如curl -X PUT http://pushgateway:9091/metrics/job/some_job这条命令会把请求body里的指标赋给jobsome_job这个分组。如果某条指标本身没有job标签Pushgateway会自动补上URL里的这个如果指标里自己带了job标签那情况会复杂一些后面细说。PUT和POST的区别在于对同组数据的态度PUT会先清空该job下的所有旧数据再写入新数据POST则直接在旧数据后面追加如果指标名和标签完全一致则会覆盖否则多个值会同时存在。实际使用中我默认推荐PUT因为批量任务每次执行都是全新的快照旧值不具备延续意义只有想在同一个job下保留不同标签的多组数据时才用POST。除了jobURL路径上还能携带instance标签形如curl -X PUT http://pushgateway:9091/metrics/job/some_job/instance/10.0.0.1这样一来同一套任务代码跑在不同机器上时可以用instance天然区分开来。如果数据里自带的instance标签和URL里的冲突Pushgateway的处理规则是URL路径上的会覆盖数据里的。所以设计推送地址时URL路径上的instance就相当于一个默认值不要跟数据里强调的标签重复设置。4.2 Shell脚本精确推送示例写一个稍微复杂一点、贴近实际场景的Shell示例。假设我们有一个数据备份脚本需要把备份是否成功、耗时、备份文件大小这些指标推送上去#!/bin/bash # 配置区 PUSHGATEWAYhttp://pushgateway:9091 JOB_NAMEbackup_job INSTANCE$(hostname) # 模拟备份任务记录开始时间 start_time$(date %s) backup_success1 backup_size0 # 执行真正的备份逻辑这里用sleep模拟 if cp /data/important /backup/important_$(date %Y%m%d) 2/dev/null; then backup_success1 backup_size$(du -sb /backup/important_$(date %Y%m%d) | awk {print $1}) else backup_success0 fi end_time$(date %s) duration$((end_time - start_time)) # 组装Prometheus文本格式的指标数据 cat EOF | curl -s -X PUT --data-binary - ${PUSHGATEWAY}/metrics/job/${JOB_NAME}/instance/${INSTANCE} # HELP backup_success Whether the backup job succeeded (1) or failed (0) # TYPE backup_success gauge backup_success ${backup_success} # HELP backup_duration_seconds Duration of the backup job in seconds # TYPE backup_duration_seconds gauge backup_duration_seconds ${duration} # HELP backup_file_size_bytes Size of the backup file in bytes # TYPE backup_file_size_bytes gauge backup_file_size_bytes ${backup_size} EOF推送成功后会返回HTTP 200。细节上注意三点第一使用--data-binary -从标准输入读取数据可以在管道里完成数据组装避免写临时文件。curl -d会把内容里的换行符吃掉导致格式错误这个坑我踩过一次。第二每条指标前面建议写全# HELP和# TYPE两行注释。虽然Pushgateway不做强校验缺注释也能正常接收但Prometheus端如果同时看到两条同名但注释不同的指标会告警写全注释能避免后续维护的坑。第三最好在脚本退出时加一个trap不管脚本是正常结束还是中途崩溃都能确保推送一次状态。比如trap echo backup_success 0 | curl -s -X PUT --data-binary - ${PUSHGATEWAY}/metrics/job/${JOB_NAME}/instance/${INSTANCE} EXIT这样就算主流程挂了Pushgateway上也会留下一份“失败”的记录告警系统能及时感知。4.3 Python客户端库与多指标推送Python环境下操作Pushgateway更方便因为有了现成的第三方库。最常用的是prometheus_client包它直接集成了Pushgateway操作类不用手动拼Prometheus文本格式。安装依赖pip install prometheus-client定义指标并推送的例子from prometheus_client import CollectorRegistry, Gauge, push_to_gateway # 创建一个独立的registry避免和其他模块的指标混在一起 registry CollectorRegistry() # 定义指标注意job和instance不在指标定义里而是在推送时指定 backup_success Gauge(backup_success, Whether backup succeeded, registryregistry) backup_duration Gauge(backup_duration_seconds, Duration of backup in seconds, registryregistry) # 模拟业务逻辑 backup_success.set(1 if success else 0) backup_duration.set(42.5) # 推送到Pushgateway指定job名和instance push_to_gateway(pushgateway:9091, jobbackup_job, registryregistry, grouping_key{instance: server-01})这段代码里grouping_key参数很关键。它除了指定instance以外还能传其他自定义标签组合比如{instance: server-01, env: prod}。这些额外标签会拼接到推送URL的路径里而不是数据内部Pushgateway在存储时就会维护一组独立的时序方便Prometheus按维度做切割查询。用prometheus_client推送还有个好处它自动帮你处理好Prometheus文本格式的序列化不会再出现Shell方案里容易犯的格式错误。另外同一个Python脚本里可以定义多种类型的指标Counter、Gauge、Histogram都支持不用像Shell方案那样手动拼字符串。用Python配合Pushgateway处理批处理任务的监控上报是我最推荐的方式。4.4 删除API与数据清理实操指标推上去了数据在Pushgateway里会一直存着时间久了堆积的数据会占用内存也会影响Prometheus的抓取效率。所以定期清理是一个必须考虑的运维动作。Pushgateway支持按级别删除数据API路径跟推送一致用DELETE方法即可# 删除某个job的全部数据 curl -X DELETE http://pushgateway:9091/metrics/job/backup_job # 删除某个job下指定instance的数据 curl -X DELETE http://pushgateway:9091/metrics/job/backup_job/instance/server-01推荐的清理策略是“任务结束时主动清理”。推送完最新数据后如果这个任务的使命已经完成不再需要历史数据就在脚本最后加一条删除指令把Pushgateway里的旧数据清掉。这样Prometheus下一次拉取时抓不到这些指标也就不会再在存储里写入陈旧数据。需要注意的边界情况是如果Prometheus的告警规则依赖这些数据来判断“最近任务是否执行过”那清理动作反而会破坏告警逻辑这时候就不适合自动清理应该保留指标并在告警规则里加新鲜度判断。5. 生产环境的避坑经验与常见问题排查5.1 指标数据重复与冲突的处理Pushgateway用久了最头疼的问题之一就是指标冲突。表现形式为Prometheus的tsdb中出现了大量重复的时序数据一个指标名下面挂着多个值打图表时线条乱七八糟。冲突的根源在于标签集合不一致。同一个指标名backup_duration_seconds一批数据带jobbackup_job, instanceserver-01另一批带jobbackup_job没写instancePrometheus会认为这是两条完全不同的时序分别存储。看起来就是同一指标重复了。解决办法是约束推送规范推送地址的URL路径中必须保持一致的标签组合策略。要么所有任务都用jobinstance组合要么都不带instance不要混乱使用。在团队的推送脚本模板里把URL格式和标签规范写死从源头杜绝不标准的数据产生。5.2 高基数问题每个用户一个标签的陷阱Pushgateway最容易踩的另一个大坑是高基数。有人在推送指标时喜欢把动态变化的标识放在标签里比如api_request_count{user_id12345} 1 api_request_count{user_id67890} 3看似合理实则危险。每条带不同user_id的数据都会在TSDB里创建一条独立的时序用户量上千后时序数量膨胀到百万级会直接把Prometheus的存储压垮。正确的做法是把这类标签从指标里移除改成用单条不带高基数标签的指标配合外部查询条件来切分。或者退一步讲Pushgateway只用于聚合后的汇总指标比如“总请求数”“成功率”不要为了细粒度分析而在标签里塞唯一标识。监控系统要的是趋势和告警不是数据库。5.3 时间戳与数据时效性的坑Pushgateway数据的时间戳由谁决定这是很多人搞不清楚的地方。默认情况下任务推送数据时不带时间戳Pushgateway接收数据时会自动把当前时间作为采样时间。也就是说数据推上来的那一刻就成为这条时序的“当前值”。这里有个隐含的坑如果脚本运行了30分钟才结束运行期间的瞬时状态没有推送最后只推了一个最终结果那这个结果的时间戳就是推送时间而不是任务开始时间。排查问题时看到指标时间跟任务实际执行时间对不上容易产生误导。解法是推送时显式带上业务时间戳。Prometheus文本格式支持在指标值后面加时间戳字段单位为毫秒backup_duration_seconds 42.5 1699999999000配合前面提到的告警新鲜度判断可以准确表达“这是哪个时刻的数据”。但要注意显式指定了时间戳之后Prometheus会以这个时间戳为准进行数据存储如果跟系统当前时间差太远查询时会因为不在时间范围内而看不到这点容易被忽略。5.4 Pushgateway页面无法访问的排查流程部署完成后经常遇到的情况是容器起来了但Web页面访问不了。按我的排查经验优先级从高到低排列检查容器是否真正启动成功docker logs pushgateway看启动日志有没有报错。检查端口映射是否生效docker port pushgateway确认9091端口映射到宿主机没有冲突。检查防火墙规则云服务器安全组、iptables、ufw是否放行9091端口。这是最常见的原因尤其是云上机器。检查服务绑定地址如果Pushgateway启动时指定了--web.listen-address127.0.0.1:9091外部就连不上只能本机访问需要改成:9091或0.0.0.0:9091。一般照这个顺序排查99%的问题都能定位到。5.5 Pushgateway自身监控Pushgateway毕竟承担着所有“推模式”指标的中转任务它挂了所有依赖它的监控数据都会断流。所以监控Pushgateway本身也是必须做的。Prometheus里单独加一个job来采集Pushgateway自身的指标关注几个关键指标up{jobpushgateway}判断它是否存活pushgateway_http_requests_total按HTTP状态码统计请求量如果4xx/5xx暴增说明有推送方在报错process_resident_memory_bytes内存占用情况长期上涨可能是数据堆积过多pushgateway_build_info版本信息升级排查时有用给Pushgateway的存储告警设个阈值如果持久化文件大小超过1GB或者内存持续超过500MB且没有回落趋势就该检查是不是数据堆积得太严重、有哪些job的数据该清理了。6. 从单体到集群Pushgateway的进阶玩法6.1 多Pushgateway分片与高可用设计当推送方数量上到一定规模单台Pushgateway可能会出现性能瓶颈。这时候可以拆成多台按业务线或者机房来划分。比如华东机房的机器推到pushgateway-east华北机房的推到pushgateway-north然后在Prometheus里配置两个job分别抓取。这样做的好处不只是分担压力还能做到故障隔离一个Pushgateway挂了不会影响其他机房的监控数据。高可用方面Pushgateway本身不像Prometheus那样支持原生的集群模式多台实例之间不共享数据。但这在绝大多数场景下不影响使用因为Prometheus可以从多台Pushgateway分别拉取数据单台故障只会暂时丢一部分数据拉取恢复正常后数据就自动补回来了。如果要求更高可以从任务推送侧做双写每份指标同时推给两台PushgatewayPrometheus侧用两台的数据做冗余一台挂了另一台顶上。代价是存储翻倍一般建议只在核心业务链路上用这个方案。6.2 结合Alertmanager实现可靠的批处理告警Pushgateway跟Alertmanager配合是批处理任务监控告警最实用的组合。原理是这样的批处理任务跑完把成功与否的状态推到PushgatewayPrometheus定期拉取这些指标一旦发现失败状态且数据是新鲜的就触发告警规则把告警发送给Alertmanager由它负责路由到钉钉、邮件或者企业微信。这里有个人经验值得分享告警恢复的判断不能依赖Pushgateway的数据自动清除因为Pushgateway不会主动清数据。配置失败告警规则时一定要把“当前数据新鲜度”作为触发条件的一部分否则任务失败后即使后续不再推送数据告警也会一直挂着不恢复。结合Prometheus的for参数设置一个持续时间比如连续5分钟都处于失败状态才告警能有效避免偶发抖动造成误报。关于Pushgateway实际操作下来我最大的感受用了Pushgateway这两年最大的体会是它解决了一个实际问题同时引入了一堆“习惯性约束”。它的价值在于把Prometheus的采集边界扩展到了非HTTP服务、短生命周期任务和隔离网络中架构上确实补上了Pull模型的短板。但它的坑也足够隐蔽标签冲突、高基数、数据陈旧、认证缺失任何一个踩进去都要花不少时间去排查。我个人建议把这几点固化成团队规范部署时必须开启持久化推送时必须统一标签规范Prometheus抓取时必须开honor_labels告警时必须带新鲜度判断管理页面前置必须做流量限制或认证。这五条做到位Pushgateway在大部分生产场景下都能稳定运行。至于那些更进阶的用法比如动态发现推送目标、跨集群汇总指标都可以基于这套基础架构慢慢延伸。如果这篇文章能帮你少踩几个坑那它就是有价值的。欢迎大家在实际使用中多总结、多交流毕竟监控系统这种基础设施最怕的不是技术难而是踩了坑没人说。