2、Prometheus 基础:Prometheus 架构与核心概念、数据模型、Metric 类型、Pull vs Push 模式
好,咱们正式开始聊 Prometheus。说实话,我第一次接触它的时候,第一反应是——这玩意儿跟 Zabbix 长得完全不一样啊。没有数据库,没有复杂的 agent 部署,就一个二进制文件跑起来,居然能撑起大规模监控?后来我深入用了几年,才真正理解它的设计哲学。
这一节,我会带你从架构入手,把核心概念、数据模型、指标类型,还有那个经典的 Pull vs Push 之争,一次性讲透。嗯,咱们不搞教科书式的罗列,我尽量用我踩过的坑来帮你理解。
2.1 Prometheus 架构:一张图讲清楚
Prometheus 的架构,说白了就是「中心化采集 + 主动拉取」。它不像传统监控那样,等着 agent 把数据推过来。而是自己主动去「问」目标服务:嘿,你现在的状态怎么样?
我习惯把它的架构拆成三块来看:
- 采集端:Prometheus Server 自己就是采集器。它通过配置文件里的 targets 列表,定期去拉取指标数据。
- 存储端:拉回来的数据,存在本地的 TSDB(时序数据库)里。这个 TSDB 是自研的,不是挂个 MySQL 或者 PostgreSQL 完事。
- 告警与展示端:Alertmanager 负责告警,Grafana 负责展示。Prometheus 自己也有个简单的 Web UI,但说实话,生产环境我基本不用它看数据,太简陋了。
你可能会问:那 Exporter 算什么?Exporter 其实就是一个「翻译官」。你的应用可能不懂 Prometheus 的指标格式,没关系,Exporter 帮你把应用的状态数据转成 Prometheus 能理解的格式。比如 node_exporter 把 Linux 系统指标转成 Prometheus 格式,mysqld_exporter 把 MySQL 状态转成 Prometheus 格式。
核心流程:Prometheus Server 定期向 Exporter 或应用自身暴露的 /metrics 端点发起 HTTP GET 请求,拉取指标数据,解析后存入本地 TSDB。同时,它会根据预设的告警规则,将告警信息推送给 Alertmanager。
2.2 核心概念:这些术语你得门儿清
刚开始学 Prometheus,有几个概念容易搞混。我一个个说清楚。
2.2.1 时间序列(Time Series)
这是 Prometheus 最核心的概念。一条时间序列,就是一组带时间戳的数值,按时间顺序排列。每条序列由 metric name(指标名) 和 labels(标签) 唯一标识。
举个例子:
http_requests_total{method="GET", endpoint="/api/users", status="200"}
这里的 http_requests_total 是指标名,{method="GET", endpoint="/api/users", status="200"} 是标签组合。这两者合在一起,就唯一确定了一条时间序列。
我在项目中遇到过一个问题:有同事给每个请求的 URL 都加了一个标签,结果标签基数爆炸,Prometheus 直接 OOM 了。嗯,这里要记住——标签的基数不能太高,否则存储和查询都会出问题。
2.2.2 样本(Sample)
样本就是时间序列里的一个数据点。它包含两部分:
- 一个 float64 类型的值
- 一个精确到毫秒的时间戳
说白了,一条时间序列就是无数个样本按时间排成的队。
2.2.3 作业与实例(Job & Instance)
这两个概念是 Prometheus 配置里的核心。一个 Job 代表一组相同功能的目标,比如所有 Web 服务器。一个 Instance 代表一个具体的采集目标,比如 192.168.1.1:9090。
Prometheus 会自动给每个抓取到的样本加上两个标签:job 和 instance。这样你在查询时就能很方便地按作业或实例来过滤数据。
2.3 数据模型:PromQL 的基石
Prometheus 的数据模型,我总结为四个字:指标 + 标签。所有数据都围绕这个模型展开。
指标名通常描述「测量什么」,比如 cpu_usage_percent、memory_bytes。标签则描述「哪个维度的测量」,比如 host="web-01"、region="us-east"。
这种设计的好处是什么?你想想看,如果你想知道所有 Web 服务器的平均 CPU 使用率,只需要一条 PromQL:
avg(cpu_usage_percent{job="web-server"})
不需要写复杂的 SQL 关联查询,一个标签过滤就搞定了。这就是标签驱动设计的威力。
我的习惯:在设计指标时,我会先想清楚「这个指标会被怎么查询」,然后再决定加哪些标签。标签不是越多越好,够用就行。我曾经见过一个团队给每个指标加了十几个标签,结果查询性能惨不忍睹。
2.4 Metric 类型:四种基础类型
Prometheus 的客户端库支持四种核心指标类型。我一个个讲,顺便说说它们各自适合什么场景。
2.4.1 Counter(计数器)
Counter 只增不减,重启后归零。典型的场景是:请求总数、错误总数、任务完成数。
为什么设计成只增不减?因为 Prometheus 的查询语言 PromQL 里有 rate() 和 increase() 函数,它们专门用来计算 Counter 类型指标的变化率。如果 Counter 可以随意增减,这些函数就没法用了。
我在项目中遇到过一个问题:有人用 Counter 来记录当前在线人数,结果发现数据完全不对。因为在线人数会增会减,Counter 根本不适合。嗯,这个坑我踩过。
2.4.2 Gauge(仪表盘)
Gauge 可增可减,反映的是某个时刻的瞬时值。比如 CPU 使用率、内存使用量、当前连接数。
Gauge 没有 rate() 函数,但可以用 avg_over_time() 等聚合函数来分析趋势。
2.4.3 Histogram(直方图)
Histogram 用来统计数据的分布情况。比如:请求延迟在 0-100ms 的有多少,100-200ms 的有多少,200-500ms 的有多少。
它自动生成三个指标:
<basename>_bucket{le="..."}:累积计数<basename>_sum:所有值的总和<basename>_count:总次数
用 Histogram 可以很方便地计算 P99、P95 等分位数。我习惯用 Histogram 来监控 API 的响应延迟,效果非常好。
2.4.4 Summary(摘要)
Summary 和 Histogram 类似,也是用来计算分位数的。区别在于:Summary 的分位数是在客户端计算的,而 Histogram 是在服务端计算的。
我个人更推荐用 Histogram,因为它的分位数计算更灵活,而且可以跨实例聚合。Summary 的分位数不能聚合,这在多实例场景下是个硬伤。
| 类型 | 特点 | 典型场景 | 常用函数 |
|---|---|---|---|
| Counter | 只增不减 | 请求总数、错误数 | rate(), increase() |
| Gauge | 可增可减 | CPU、内存、连接数 | avg_over_time() |
| Histogram | 分布统计,服务端计算分位数 | 请求延迟分布 | histogram_quantile() |
| Summary | 分布统计,客户端计算分位数 | 延迟分位数(不跨实例聚合) | 直接查询 |
2.5 Pull vs Push 模式:为什么 Prometheus 选择 Pull?
这是 Prometheus 最受争议的设计之一。为什么它坚持用 Pull(拉取)模式,而不是像传统监控那样用 Push(推送)模式?
我刚开始也觉得 Push 更方便——应用主动上报数据,多省事啊。但用了一段时间 Pull 模式后,我发现了它的几个好处:
- 更容易控制采集频率:Prometheus Server 自己决定什么时候拉取,不会因为目标端突发大量数据而被打爆。
- 更容易发现故障:如果某个目标挂了,Prometheus 拉取不到数据,立刻就能发现。Push 模式下,你很难区分「目标没数据」和「目标挂了」。
- 更容易做安全控制:Prometheus 只需要知道目标的地址和端口,不需要目标端主动连接 Server。这在网络隔离的环境下特别有用。
当然,Pull 模式也有缺点。比如有些短期任务(批处理作业、定时任务),它们运行时间很短,Prometheus 还没来得及拉取,任务就结束了。这时候就需要 Pushgateway 来帮忙——任务结束时把指标推给 Pushgateway,Prometheus 再从 Pushgateway 拉取。
避坑指南:我曾经在一个项目里过度依赖 Pushgateway,把所有指标都往里面推。结果 Pushgateway 成了单点故障,而且指标过期了也不会自动清理,导致数据混乱。后来我调整了策略:只有短期任务才用 Pushgateway,长期运行的服务一律用 Pull 模式。
总结一下我的观点:能用 Pull 就用 Pull,Push 只是补充手段。这个原则我到现在还在用。
好了,Prometheus 的基础部分就聊到这里。下一节我们会动手搭建一个 Prometheus 实例,到时候这些概念你就能真正用起来了。