第2章:Prometheus基础架构——Pull vs Push模型、核心组件、数据模型、作业与实例
好,咱们正式开始聊Prometheus的骨架。说实话,很多新手一上来就被各种组件搞晕了。我当年第一次搭Prometheus的时候,也是对着架构图发了好一会儿呆。别急,咱们一个一个拆开看,其实没那么复杂。
2.1 Pull vs Push:两种截然不同的哲学
先聊最核心的设计理念——Prometheus为什么坚持用Pull(拉)模式,而不是像Zabbix、OpenTSDB那样用Push(推)模式?
说白了,Pull模式就是Prometheus Server主动去各个目标机器上“要”数据。你想想看,这有什么好处?
- 可控性更强:Server决定什么时候拉、拉哪些数据。不会因为被监控端突然爆发大量数据而把Server冲垮。
- 健康检测天然自带:如果某个目标拉不到数据,那基本可以断定它挂了。Push模式下,数据没来,你还要猜是目标挂了还是网络延迟了。
- 配置即发现:改一下配置文件,重启一下,新目标就自动被纳管了。
但Pull模式也不是万能的。我在项目中遇到过一种情况:有些批处理任务,跑几分钟就结束了,Prometheus还没来得及拉,任务已经退出了。这时候怎么办?
嗯,Prometheus也考虑到了这一点。它提供了一个叫Pushgateway的组件,专门用来处理这种短生命周期任务的指标推送。说白了,Pushgateway就是一个“中转站”,任务把指标推给它,Prometheus再去Pull它。
核心原则:能Pull就别Push。Push只作为补充手段,不要滥用。
我的经验:我曾经见过一个团队把所有指标都往Pushgateway里塞,结果Pushgateway成了单点瓶颈,而且指标过期了也不清理,内存越涨越高。记住,Pushgateway只适合批处理任务,不适合长期运行的守护进程。
2.2 核心组件:三驾马车
Prometheus的架构可以拆成三个核心组件,我习惯叫它们“三驾马车”。
2.2.1 Prometheus Server
这是整个系统的大脑。它负责三件事:
- 数据采集:按照配置的间隔(默认15秒),去各个目标拉取指标。
- 数据存储:拉回来的数据按时间序列存到本地磁盘。默认保留15天。
- 查询与告警:提供PromQL查询接口,同时根据告警规则计算是否触发告警。
我个人习惯把Server的存储目录单独挂一块SSD,因为Prometheus对磁盘IO还是比较敏感的。尤其是你管理的目标超过几千个的时候,磁盘性能直接决定了查询速度。
2.2.2 Alertmanager
Server只负责“产生”告警事件,但怎么通知人、怎么分组、怎么抑制重复告警,这些脏活累活都交给Alertmanager。
举个例子:你有一组Web服务器同时挂了,Server会发出N条告警。如果没有Alertmanager,你的手机就会被N条短信轰炸。但Alertmanager可以配置分组规则,把同一类告警合并成一条通知——“你的5台Web服务器都挂了”。
注意:Alertmanager和Server是独立的进程。很多人刚开始以为Alertmanager是Server的一个模块,其实不是。它们通过HTTP API通信。
2.2.3 Exporters
Exporter就是“翻译官”。你的应用、系统、中间件不会直接吐出Prometheus格式的指标,Exporter负责把它们翻译成Prometheus能理解的格式。
常见的Exporter有:
| Exporter名称 | 监控目标 | 端口(默认) |
|---|---|---|
| node_exporter | Linux/Windows系统指标(CPU、内存、磁盘、网络) | 9100 |
| blackbox_exporter | HTTP/HTTPS/TCP/ICMP探测 | 9115 |
| mysqld_exporter | MySQL数据库指标 | 9104 |
| nginx_exporter | Nginx状态指标 | 9113 |
你可能会问:“那我自己的Java应用怎么办?” 嗯,Prometheus官方提供了Client Libraries,支持Go、Java、Python、Ruby等主流语言。你只需要在代码里引入库,暴露一个/metrics端点就行了。
2.3 数据模型:Metric、Label、Sample
这是Prometheus最核心的概念,也是很多人容易搞混的地方。我尽量用大白话讲清楚。
2.3.1 Metric(指标名)
指标名就是“你测量的是什么”。比如:
http_requests_total—— HTTP请求总数node_cpu_seconds_total—— CPU使用时间go_memstats_alloc_bytes—— Go程序分配的内存字节数
命名规范上,我建议遵循Prometheus的惯例:用下划线分隔,单位放在最后(如_total、_seconds、_bytes)。
2.3.2 Label(标签)
标签是Prometheus的灵魂。同一个指标名,加上不同的标签,就变成了不同的时间序列。
举个例子:
http_requests_total{method="GET", endpoint="/api/users", status="200"} 1024
http_requests_total{method="POST", endpoint="/api/users", status="500"} 3
你看,指标名都是http_requests_total,但通过method、endpoint、status这三个标签,我们可以精确地知道:GET请求成功了多少次,POST请求失败了多少次。
关键点:标签的基数(Cardinality)决定了你的Prometheus能撑多久。如果一个标签有10万种取值(比如user_id),那你的Prometheus很快就会内存爆炸。我曾经接手过一个项目,就是因为把用户ID当成了标签,导致Server每隔半小时就OOM一次。
2.3.3 Sample(样本)
样本就是“某个时间点的具体数值”。它由三部分组成:
- 时间戳(毫秒精度)
- 值(float64浮点数)
- 所属的时间序列(指标名+标签集合)
说白了,一条完整的数据长这样:
http_requests_total{method="GET", endpoint="/api/users", status="200"} @ 1695000000 → 1024
这就是一个样本。Prometheus存储的就是海量这样的样本。
2.4 作业与实例:Prometheus的“目标管理”
最后聊聊Prometheus怎么管理它要监控的目标。
2.4.1 实例(Instance)
实例就是“一个具体的监控端点”。通常是一个IP:Port的组合。比如:
192.168.1.10:9100 # 这是一台服务器的node_exporter实例
192.168.1.20:9100 # 这是另一台服务器的node_exporter实例
2.4.2 作业(Job)
作业就是“一组相同用途的实例的集合”。比如:
job="node" → 包含所有node_exporter实例
job="mysql" → 包含所有mysqld_exporter实例
在Prometheus的配置文件中,你通常这样定义:
scrape_configs:
- job_name: 'node'
static_configs:
- targets: ['192.168.1.10:9100', '192.168.1.20:9100', '192.168.1.30:9100']
- job_name: 'mysql'
static_configs:
- targets: ['192.168.1.10:9104', '192.168.1.20:9104']
这样配置之后,Prometheus会自动给每个抓取到的指标加上两个标签:
job="node"或job="mysql"instance="192.168.1.10:9100"等
你想想看,这有什么好处?当你查询的时候,可以按job分组看整体情况,也可以按instance看单台机器的细节。非常灵活。
避坑指南:我曾经犯过一个错误——把所有目标都放在同一个job里。结果想单独看MySQL的指标时,发现根本没法区分。后来我养成了一个习惯:一个job只放一种类型的Exporter。这样管理起来清晰得多。
小结
好了,这一章的内容就这些。咱们回顾一下:
- Pull模式是Prometheus的默认哲学,Pushgateway只做补充。
- 三驾马车:Server(大脑)、Alertmanager(通知管家)、Exporter(翻译官)。
- 数据模型:Metric(测什么)、Label(怎么区分)、Sample(具体值)。
- 作业与实例:Job是组,Instance是具体端点。
下一章咱们会动手搭建一个真正的Prometheus环境。到时候你会看到,这些概念在实际操作中是怎么落地的。嗯,敬请期待。