1. 为什么你的监控数据总是“失联”?聊聊PushGateway这个“数据中转站”

搞云原生监控的朋友,对Prometheus肯定不陌生。它那个“拉”(Pull)的模式,主动去各个目标上抓取(scrape)指标,设计得很优雅,也成了事实上的标准。但实际干过活的兄弟都知道,理想很丰满,现实很骨感。你有没有遇到过下面这些让人头大的情况?

你写了个定时跑的批处理脚本,凌晨3点启动,跑5分钟就结束。等Prometheus按固定间隔(比如15秒)去抓取的时候,脚本早就“人走茶凉”了,啥数据也捞不着。或者,有些服务躲在公司内网的防火墙后面,出于安全考虑,Prometheus服务器根本没法直接访问它们。再或者,一些临时性的计算任务、一次性的CI/CD流水线作业,它们的生命周期比Prometheus的抓取周期还短,这些“短命鬼”的监控数据要怎么上报?

这时候,你就需要一个“中间人”,一个“数据中转站”。让这些不方便被“拉”,或者来不及被“拉”的服务,自己主动把数据“推”给这个中间人。然后,再由Prometheus从这个中间人那里稳定地“拉”走数据。这个完美的“中间人”,就是PushGateway

你可以把它想象成一个临时的“快递柜”。那些行踪不定的“外卖员”(短生命周期的Job),没法等你下楼(Prometheus来拉取),就把外卖(监控指标)放进快递柜(PushGateway)。而你(Prometheus)可以随时、稳定地去快递柜取件。这样一来,数据链路就通了。

所以,PushGateway绝不是Prometheus的替代品,而是对它“拉”模式的一个关键补充。它专门用来处理那些不适合或无法被Prometheus直接抓取的监控场景,是构建完整、无死角监控体系不可或缺的一块拼图。接下来,我就带你亲手部署、配置它,并搞定几种常见的数据推送姿势。

2. 手把手部署PushGateway:从零到一的保姆级教程

理论说再多,不如动手装一遍。咱们这就找台机器,把PushGateway跑起来。我建议你单独用一台虚拟机或容器来部署,资源消耗很小,也方便管理。

2.1 环境准备与安装

首先,咱们得把PushGateway的二进制包弄下来。它是个用Go写的单文件程序,部署起来特别省心。

# 假设我们已经在规划为PushGateway的服务器上(主机名 push-gw)
# 下载最新稳定版的PushGateway,可以去官网查看版本号,这里以1.6.1为例
wget https://github.com/prometheus/pushgateway/releases/download/v1.6.1/pushgateway-1.6.1.linux-amd64.tar.gz

# 解压到指定目录,比如 /usr/local/bin/
sudo tar -zxvf pushgateway-1.6.1.linux-amd64.tar.gz -C /usr/local/bin/ --strip-components=1

解压完,你会发现在 /usr/local/bin/ 目录下多了一个叫 pushgateway 的可执行文件。直接运行 ./pushgateway 就能启动,但这样不够“正规”,咱们还是把它配置成系统服务,让它能开机自启、用systemctl管理。

2.2 配置Systemd服务,实现稳定运行

用systemd来管理,是生产环境的标准操作。创建一个服务配置文件。

sudo vim /etc/systemd/system/pushgateway.service

把下面的配置内容贴进去。这里我加了一些常用的启动参数,你可以根据需求调整:

[Unit]
Description=Prometheus PushGateway
After=network.target
Wants=network.target

[Service]
Type=simple
User=nobody # 为了安全,使用非root用户运行
Group=nobody
ExecStart=/usr/local/bin/pushgateway \
    --web.listen-address=:9091 \          # 监听端口,默认9091,避免和Prometheus的9090冲突
    --persistence.file=/var/lib/pushgateway/persistence.file \ # 数据持久化文件路径
    --persistence.interval=5m \           # 持久化间隔,5分钟写一次盘
    --log.level=info                      # 日志级别
Restart=always                            # 总是自动重启
RestartSec=10

[Install]
WantedBy=multi-user.target

这里有几个参数我多说两句。--web.listen-address 指定服务监听的端口,我习惯用9091。--persistence.file 很重要,它指定一个文件路径,PushGateway会把内存中的指标数据定期写入这个文件,防止服务重启后数据丢失。记得要给存放这个文件的目录(比如 /var/lib/pushgateway/)设置好权限,让 nobody 用户能写。

# 创建数据目录并授权
sudo mkdir -p /var/lib/pushgateway
sudo chown -R nobody:nobody /var/lib/pushgateway

配置好之后,就可以启动服务了:

# 重新加载systemd配置
sudo systemctl daemon-reload

# 设置开机自启并立即启动服务
sudo systemctl enable --now pushgateway.service

# 查看服务状态,确认是 active (running)
sudo systemctl status pushgateway.service

# 检查9091端口是否在监听
sudo netstat -tlnp | grep 9091

如果一切顺利,现在你访问 http://你的服务器IP:9091,就能看到PushGateway的Web界面了。刚部署完是空白的,因为还没有任何数据推过来。

3. 让Prometheus认识这个新伙伴:配置抓取任务

PushGateway自己跑起来没用,得让Prometheus知道它,并定期从它这里拉取数据。这个配置是在Prometheus服务器的 prometheus.yml 配置文件中完成的。

找到你的Prometheus配置文件,在 scrape_configs 部分添加一个新的抓取任务(job)。

# 在 prometheus.yml 中
scrape_configs:
  # 你原有的抓取任务,比如抓取Node Exporter的
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['192.168.1.10:9100']

  # 新增一个专门抓取PushGateway的任务
  - job_name: 'pushgateway'  # 任务名称,自定义,有意义就行
    honor_labels: true       # 这个参数非常关键!建议设为true
    static_configs:
      - targets: ['192.168.93.104:9091']  # 你的PushGateway服务器地址和端口

这里最重要的就是 honor_labels: true 这个配置。我解释一下为啥它关键。当你的业务服务把指标推送到PushGateway时,可以附带一些标签(label),比如 job="batch_job", instance="server-a"。同时,Prometheus在抓取时,自己也会给指标加上一些标签,比如 job="pushgateway"(取自抓取任务名),instance="192.168.93.104:9091"(取自目标地址)。

如果 honor_labels 设为 false(默认值),Prometheus会优先使用自己生成的标签,而把推送端带来的同名标签重命名(加上 exported_ 前缀),比如变成 exported_job="batch_job"。这经常会导致数据查询时对不上,图表显示异常。

设为 true 后,Prometheus会“尊重”并优先使用推送端带来的标签,覆盖掉自己生成的那些。这样,在Prometheus里看到的指标,其 jobinstance 标签就是推送服务自己定义的那个,非常直观,也符合我们的预期。配置完成后,需要让Prometheus重新加载配置:

# 向Prometheus发送POST请求,触发重载
curl -X POST http://你的Prometheus-IP:9090/-/reload

或者直接重启Prometheus服务。之后,你可以在Prometheus的Web UI(Status -> Targets)里看到这个名为 pushgateway 的抓取任务状态是 UP。不过,因为还没数据推过来,所以指标是空的。

4. 实战数据推送:三种方法把指标送进“中转站”

核心环节来了:怎么把数据推上去?我分享三种最常用的方法,从简单到复杂,覆盖不同场景。

4.1 基础款:Shell脚本 + cURL,一行命令就搞定

这是最快捷、最轻量的方式,特别适合在Shell脚本、Cron定时任务里上报一次性指标。它的核心就是使用HTTP POST请求,将指标数据发送到PushGateway的 /metrics/job/<JOB_NAME> 这个端点。

假设我们有一个备份脚本,想在每次成功完成后,上报一个名为 backup_last_success_timestamp 的指标(值是当前时间戳),并打上 type="full" 的标签。

#!/bin/bash
# backup_script.sh

# ... 这里是你的备份逻辑 ...
echo "开始执行全量备份..."
# 模拟备份操作
sleep 2
echo "备份成功!"

# 备份成功后,推送指标到PushGateway
BACKUP_TIMESTAMP=$(date +%s) # 获取当前Unix时间戳
JOB_NAME="nightly_backup"
INSTANCE_NAME=$(hostname -f) # 使用主机名作为实例标识

# 关键推送命令
echo "backup_last_success_timestamp $BACKUP_TIMESTAMP" | \
curl --data-binary @- \
"http://192.168.93.104:9091/metrics/job/$JOB_NAME/instance/$INSTANCE_NAME/type/full"

把上面这行命令拆解一下:

  • echo "backup_last_success_timestamp $BACKUP_TIMESTAMP":生成指标数据,格式是 <指标名> <指标值>
  • | curl --data-binary @-:将上一条命令的输出作为二进制数据,通过POST发送。
  • http://.../metrics/job/$JOB_NAME/...:这是PushGateway的接收URL。路径中的 job/$JOB_NAMEinstance/$INSTANCE_NAME 会自动成为该指标的两个固定标签。你还可以继续添加更多标签,比如 /type/full

执行完脚本后,立刻刷新PushGateway的Web页面(http://192.168.93.104:9091),你就能看到刚刚推送的指标了。等个几十秒,Prometheus抓取后,你也能在Prometheus的表达式浏览器里查询到 backup_last_success_timestamp 这个指标。

4.2 进阶款:使用官方Client SDK,集成到应用程序

对于自己开发的应用程序(比如用Python、Go、Java写的),更优雅的方式是使用Prometheus官方提供的Client Library。这些库内置了与PushGateway集成的功能,能帮你处理指标类型(Counter、Gauge、Histogram等)、标签和推送逻辑。

这里以Python为例,使用 prometheus_client 库。假设我们有一个Flask Web应用,想统计请求次数。

首先安装客户端库:pip install prometheus-client

然后在你的应用代码中,可以这样写:

from prometheus_client import Counter, push_to_gateway
from prometheus_client.exposition import basic_auth_handler
import time

# 1. 定义一个计数器指标
REQUEST_COUNT = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint'])

# 模拟应用逻辑:记录几次请求
REQUEST_COUNT.labels(method='GET', endpoint='/api/users').inc()
REQUEST_COUNT.labels(method='POST', endpoint='/api/order').inc(2)

# 2. 定义推送函数(如果需要认证,可以在这里处理)
def push_metrics():
    # PushGateway的地址
    gateway = 'http://192.168.93.104:9091'
    # 将上面定义的指标推送到指定job下
    push_to_gateway(gateway, job='my_flask_app', registry=REQUEST_COUNT._collector())

# 在应用退出时,或定时任务中调用推送
if __name__ == '__main__':
    # 模拟应用运行后退出
    print("应用运行,记录指标...")
    time.sleep(1)
    push_metrics()
    print("指标已推送到PushGateway。")

使用SDK的好处是功能强大、类型安全,能轻松处理复杂的指标和标签。你可以把 push_to_gateway 的调用放在应用正常关闭的钩子函数里,或者用一个单独的线程定时推送。对于短生命周期的批处理Job,在Job开始、结束和关键阶段推送指标,是监控其运行状态和性能的绝佳手段。

4.3 管理款:数据清理与生命周期管理

PushGateway是“中转站”,不是“永久仓库”。它默认会将所有推送上来的指标都保存在内存(和持久化文件)里,直到你手动删除或者服务重启。对于短周期任务,如果只推不删,就会堆积大量过期数据,造成混淆和资源浪费。

所以,我们需要学会清理。PushGateway提供了API来删除指标。

  • 删除某个Job下某个Instance的数据

    # 删除 job="nightly_backup", instance="server01.example.com" 的所有指标
    curl -X DELETE http://192.168.93.104:9091/metrics/job/nightly_backup/instance/server01.example.com
    
  • 删除整个Job的数据(无论instance是什么):

    # 删除 job="nightly_backup" 的所有指标
    curl -X DELETE http://192.168.93.104:9091/metrics/job/nightly_backup
    

最佳实践是,谁推送,谁负责清理。对于短生命周期的任务,应该在任务成功执行完毕后,立即推送一个最终状态指标(比如 task_last_successtask_duration_seconds),然后紧接着发送一个DELETE请求,清理掉这个任务实例在PushGateway上的所有临时指标。这样既能捕获到任务结果,又不会留下垃圾数据。

你可以把清理命令放在Shell脚本的最后,或者放在应用程序的退出逻辑中。对于定时运行的脚本,这是一个非常重要的好习惯,能保持PushGateway的清洁。

5. 避坑指南与生产环境最佳实践

用PushGateway这几年,我踩过不少坑,也总结出一些让系统更稳健的经验。

第一个大坑:指标时间戳问题。 Prometheus设计上是基于抓取时间来评估指标的。当你把数据推送到PushGateway时,如果不显式指定时间戳,Prometheus会使用它抓取PushGateway的那一刻作为该指标的时间戳。这对于实时运行的服务没问题,但对于已经结束的批处理Job,这会导致图表上显示的时间远晚于实际发生时间。解决办法是,在推送时,使用Prometheus的特定格式附带时间戳(毫秒级)。在Shell中,可以这样推:

cat <<EOF | curl --data-binary @- http://192.168.93.104:9091/metrics/job/my_job
my_metric{label="a"} 3.14 $(date +%s)000
EOF

注意 $(date +%s)000 是生成毫秒时间戳的一种方法。在客户端SDK中,通常有更规范的方法来设置时间戳。

第二个坑:监控PushGateway自身。 PushGateway本身也是一个需要被监控的服务。别忘了给它也部署一个Node Exporter,监控它的CPU、内存、磁盘。更重要的是,要监控它保存的指标数量,防止内存爆掉。你可以让Prometheus抓取PushGateway自身的运行时指标(它暴露在 /metrics 端点),重点关注 pushgateway_http_requests_total(请求量)和 pushgateway_metrics(当前存储的指标组数)等。

第三个实践:标签规划要清晰。 推送到PushGateway的指标,一定要通过标签(尤其是 jobinstance)清晰地标明来源。instance 标签最好能唯一标识推送源(比如主机名、Pod ID)。混乱的标签会让后续的数据查询和告警规则编写变得极其困难。

第四个实践:设置合理的持久化间隔。 启动参数中的 --persistence.interval 控制数据落盘频率。间隔太短会增加磁盘IO,太长则可能在意外崩溃时丢失较多数据。根据你的数据重要性和推送频率,设置一个折中的值,比如5分钟。

最后,也是最重要的:PushGateway不是万能的。 它主要适用于短生命周期、无法被拉取的任务。对于常驻服务,强烈建议还是让它们暴露标准的Prometheus指标端点,由Prometheus直接拉取。直接拉取的模式更简单、更原生,也避免了PushGateway可能带来的单点问题和数据延迟。千万不要因为有了PushGateway,就把所有服务都改成推送模式,那会引入不必要的复杂性。

把PushGateway用对了地方,它就是你监控体系里解决特定痛点的“神兵利器”。希望这篇从原理到实战,再到踩坑经验的分享,能帮你真正掌握这个组件,搭建起更健壮、更全面的云原生监控系统。

更多推荐