如果你用过 Zabbix,再接触 Prometheus 会有种"画风突变"的感觉:它不装 Agent 到被监控机上主动汇报,而是反其道而行,由监控端主动去各个目标拉数据。这种设计一开始让人不习惯,但它恰恰是云原生时代流行的原因——被监控对象随时生灭,注册发现自动跟上,采集端不用维护一份长长的清单。
这篇文章讲清楚 Prometheus 的核心:为什么是拉取模型、时序数据长什么样、四种指标类型分别用在什么场景、PromQL 怎么写、node_exporter 怎么装、scrape_configs 怎么配,以及它和被动推送模式的本质差异。目标不是让你背概念,而是理解它的数据模型后,看到任何一条指标都能自己写出查询、想清楚该用哪种类型。搞明白了这些,后面配 Grafana 面板、写告警规则都是水到渠成的事。
拉取模型:为什么是 pull 而不是 push
Prometheus 的采集方式叫 pull,也叫 scrape。它周期性地向被监控目标发起 HTTP 请求,目标在一个固定路径上返回当前的指标快照。默认路径是 /metrics,默认间隔十五秒。
Prometheus ----> GET http://target:9100/metrics ----> 目标返回纯文本指标
/metrics 返回的内容长这样:
# HELP node_cpu_seconds_total Seconds the CPUs spent in each mode.
# TYPE node_cpu_seconds_total counter
node_cpu_seconds_total{cpu="0",mode="idle"} 12345.67
node_cpu_seconds_total{cpu="0",mode="system"} 234.56
node_memory_MemAvailable_bytes 5.2e+09
这是纯文本、每行一条的格式,# HELP 是解释,# TYPE 是指标类型,下面每行是具体数值。这种"目标自己暴露、采集端来取"的约定叫 exporter 模式。
为什么用 pull?几个实际好处:采集端能通过目标是否响应,天然判断对方存活(up 指标);能通过服务发现自动跟上容器和实例的增减;能集中控制采集频率和超时;调试时直接 curl 目标即可,不依赖任何中间组件。缺点也有:不适合一次性任务和短生命周期任务,也不容易穿透防火墙——这些场景要靠 Pushgateway 或服务端代理来补。
时序数据模型:指标名加标签
Prometheus 存的是时序数据,一条时序由指标名和一个标签集合唯一确定,样点是"时间戳 + 数值"的序列。
指标名{标签1="值1", 标签2="值2"} 数值
举个例子:
http_requests_total{method="POST", handler="/api/login", status="200"} 1027
这里 http_requests_total 是指标名,method、handler、status 是三个标签。指标名加这组标签就构成一条独立的时序。换个 status="500" 就是另一条时序。这样设计的好处是,同一族数据可以用标签自由切分,查询时按标签过滤和聚合,非常灵活。
标签的两个原则要记住:标签值尽量是有限、低基数的枚举,比如状态码、方法、实例名;不要把用户 ID、请求 ID、时间戳这种高基数的值当标签,否则会炸出成千上万条时序,把内存吃光。这是新手最容易犯的性能错误。
四种指标类型
Prometheus 客户端库定义了四种指标类型,选错类型会让查询变得别扭。它们的区别值得一提。
| 类型 | 特点 | 适合场景 | 例子 |
|---|---|---|---|
| Counter | 只增不减,重启归零 | 累计次数、累计字节数 | 请求总数、错误总数 |
| Gauge | 可增可减,瞬时值 | 当前状态快照 | CPU 使用率、内存占用、温度 |
| Histogram | 分桶统计,可算分位数 | 请求耗时、响应大小 | 接口延迟分布 |
| Summary | 客户端算分位数 | 同上但更省查询开销 | 请求耗时(滑动窗口) |
Counter 是只增的计数器,直接看它的值没意义,要用 rate() 或 increase() 函数算出每秒增长率才有意义。http_requests_total 就是典型 Counter。
Gauge 是瞬时值,可以直接看、直接比较,比如 node_memory_MemAvailable_bytes。
Histogram 把观测值分到预设的桶里(比如耗时小于 0.1 秒、小于 0.5 秒、小于 1 秒各有几个),配合 histogram_quantile() 可以算 P95、P99 分位数。它还会额外产生 _sum 和 _count 两个指标。
Summary 在客户端直接算出分位数,省去服务端计算,但分位数不能在服务端跨实例聚合,且计算成本落在被监控进程上。选择上,需要跨实例聚合计分位数就用 Histogram,单实例且想减轻服务端负担可以用 Summary;绝大多数场景用 Histogram 更合适。
PromQL 入门
PromQL 是 Prometheus 的查询语言,掌握几条常用表达式就能覆盖八成日常需求。
即时查询某个 Gauge:
node_memory_MemAvailable_bytes
node_memory_MemAvailable_bytes / 1024 / 1024 / 1024 # 转成 GB
用标签过滤:
node_filesystem_avail_bytes{mountpoint="/", fstype!="tmpfs"}
http_requests_total{status=~"5.."} # 正则匹配 5xx
Counter 求速率(最常用):
rate(http_requests_total[5m]) # 每秒请求数
rate(http_requests_total{status=~"5.."}[5m]) # 每秒错误数
increase(node_network_receive_bytes_total[1h]) # 一小时增量
算 CPU 使用率(一条经典表达式,值得背下来):
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
它的逻辑是:node_cpu_seconds_total{mode="idle"} 是各核心空闲时间的 Counter,rate(...[5m]) 算出每秒空闲比例,avg by (instance) 按实例对多核心取平均,最后用 100 减去空闲比例,就是使用率。
按标签聚合:
sum by (instance) (rate(http_requests_total[5m])) # 按实例汇总 QPS
sum(rate(http_requests_total[5m])) # 全量汇总
topk(5, rate(http_requests_total[5m])) # 取前五
avg_over_time(node_load1[30m]) # 三十分钟平均负载
几个高频函数要分清:rate 用于 Counter,算每秒平均增长率,会自动处理重启归零;irate 用最后两个点算瞬时速率,更敏感但易受抖动影响;increase 算一段时间内的增量;avg_over_time、max_over_time 用于对 Gauge 做时间窗口聚合;histogram_quantile(0.95, ...) 算 P95 分位数。记住 rate 只用于 Counter,用在 Gauge 上结果没有意义。
部署 node_exporter
node_exporter 是采集 Linux 主机指标的官方 exporter,它暴露 CPU、内存、磁盘、网络、文件系统、负载等指标。
下载并安装(以最新稳定版思路为例,版本号按官方发布替换):
cd /tmp
wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
tar xf node_exporter-1.7.0.linux-amd64.tar.gz
sudo mv node_exporter-1.7.0.linux-amd64/node_exporter /usr/local/bin/
/usr/local/bin/node_exporter --version
做成 systemd 服务,开机自启:
# /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=nobody
ExecStart=/usr/local/bin/node_exporter
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
curl -s http://localhost:9100/metrics | head
能返回一堆指标就说明装好了。RHEL 系记得放行防火墙:
# RHEL、Rocky、CentOS
sudo firewall-cmd --permanent --add-port=9100/tcp
sudo firewall-cmd --reload
# Debian、Ubuntu
sudo ufw allow 9100/tcp
风险提示: 9100 端口会暴露主机的大量信息,应该只允许监控机访问。生产环境建议用 --web.listen-address=127.0.0.1:9100 绑定本机,再用 Prometheus 通过 SSH 隧道或反向代理访问,或者用防火墙限定来源 IP,不要直接对公网开放。
安装并配置 Prometheus
从官方发布页下载解压:
cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.51.0/prometheus-2.51.0.linux-amd64.tar.gz
tar xf prometheus-2.51.0.linux-amd64.tar.gz
sudo mv prometheus-2.51.0.linux-amd64 /opt/prometheus
sudo mkdir -p /var/lib/prometheus
配置文件 /opt/prometheus/prometheus.yml:
global:
scrape_interval: 15s # 默认采集间隔
evaluation_interval: 15s # 规则评估间隔
rule_files:
- /opt/prometheus/rules/*.yml
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets:
- '192.0.2.20:9100'
- '192.0.2.21:9100'
labels:
env: 'prod'
role: 'web'
- job_name: 'node-dev'
static_configs:
- targets: ['192.0.2.31:9100']
labels:
env: 'dev'
scrape_configs 里每个 job_name 是一组采集任务,targets 是要拉取的目标。给目标加 labels 是很好的习惯,后面写告警和画面板时可以按 env、role 过滤。启动:
cd /opt/prometheus
./prometheus --config.file=prometheus.yml --storage.tsdb.retention.time=30d
--storage.tsdb.retention.time 控制数据保留时长,默认十五天。建议做成 systemd 服务管理。访问 http://监控机IP:9090 进入自带的查询界面,在 Graph 页输入 PromQL 就能看图,Status 里的 Targets 页可以看到每个目标是否 UP。
除了静态配置,Prometheus 还支持多种服务发现:file_sd_configs 读本地 JSON 文件,容器环境用 kubernetes_sd_configs,云上还有 ec2_sd_configs 等。运维中很实用的做法是让 Ansible 或脚本生成 targets JSON 文件,Prometheus 监听文件变化自动重载,避免每次加机器都改主配置。
pull 与 push 的差异和适用场景
| 维度 | pull(Prometheus 原生) | push(如 Zabbix 主动、Pushgateway) |
|---|---|---|
| 发起方 | 监控端主动拉 | 被监控端主动推 |
| 目标存活判断 | 通过 up 指标天然获得 | 需额外做心跳检测 |
| 动态扩缩容 | 服务发现自动跟上 | 目标需自行注册到推送端 |
| 穿透防火墙 | 需监控端能访问目标 | 只需目标能访问监控端 |
| 短任务 | 不友好,任务结束就没目标了 | 友好,Pushgateway 暂存 |
| 时钟要求 | 由采集端统一时间 | 依赖被监控端时间准确 |
结论落到场景:长期在线的服务器、容器、中间件,用 pull 最省心;一次性的批处理任务、定时脚本、位于 NAT 后无法被访问的设备,用 Pushgateway 补;跨公网的大规模边缘节点,也可以考虑在侧线部署一个采集代理,用远程写入(remote write)回传。不要把 Pushgateway 当通用方案,它是给短任务兜底的,长期指标硬推会失去 pull 的诸多好处。
常见问题
目标显示 DOWN 但手动 curl 明明能通?
最常见是两个原因:一是 Prometheus 所在机器到目标 IP 的网络不通(curl 是在本机测的,不代表监控机能通);二是 Prometheus 配置文件里路径写错了,或改了配置没重启。另外检查 scrape_timeout 是否太短,目标响应慢时会超时。可以在 Status → Targets 页面点开目标看具体报错。
Counter 数值为什么不是从零开始?
Counter 只在进程重启时归零。如果目标运行了很久,http_requests_total 可能是几百万,这是正常的。不要直接看它的绝对值,用 rate() 看每秒增量。当你看到数值突然变小,通常是进程重启了,rate() 会自动处理这种重置。
rate 的时间窗口该选多长?
一般选采集间隔的四倍以上。十五秒采集就至少用 [1m],常用 [5m]。窗口太短,点数太少,rate 会抖动甚至算不出来(少于两个点);窗口太长,反应迟钝,看不出近期变化。告警用 [5m] 比较稳妥。
Prometheus 单机存得下多少数据?
取决于采集规模、标签基数和保留时间。经验上,几万条活跃时序、保留一个月,单机 SSD 通常能扛住;十几万条以上就要考虑分片、远程存储或换用兼容的时序数据库。真正吃内存的是标签基数,务必避免高基数标签。
总结
- Prometheus 用 pull 模型,监控端主动去目标的
/metrics拉数据,天然能判断目标存活,适合动态环境。 - 数据模型是指标名加标签,标签要低基数,别把用户 ID 之类的高基数值当标签。
- 四种指标类型各司其职:Counter 配
rate()、Gauge 直接看、Histogram 算分位数、Summary 客户端算分位数。 - 掌握
rate、increase、avg by、topk、histogram_quantile几条 PromQL,就能覆盖绝大多数监控与告警查询。 - node_exporter 负责主机指标,9100 端口要限制来源;Prometheus 通过
scrape_configs配置目标,加标签便于后续过滤。 - pull 适合长期在线目标,短任务和 NAT 环境用 Pushgateway 补,二者是搭配而非对立。
企业QQ咨询




