第 1 部分导论 · Prometheus 架构总览
打开 Prometheus 的整体骨架——它由哪几块组成、各块怎么接线、以及贯穿其设计的两个根本选择:pull 模型和「单机自足」。这一章给出后续所有章节的坐标系。
第 1 部分导论 · Prometheus 架构总览
第 0 部分画了数据的一生。从这一部分起,我们打开 Prometheus 的源码,先看它的整体骨架——后面每一章都会落在这张图的某个位置上。
五大块
Prometheus 是一个二进制(cmd/prometheus/main.go),内部由五块组成:
| 块 | 目录 | 职责 | 本专栏 |
|---|---|---|---|
| 服务发现 | discovery/ | 动态找出要抓的目标 | 本部分 |
| 抓取 | scrape/ | 按周期拉 /metrics、解析 | 本部分 |
| 存储 | tsdb/ | 时序数据落盘与检索 | 第 3 部分 |
| 查询 | promql/ | PromQL 解析与执行 | 第 4 部分 |
| 规则 | rules/ | 预聚合与告警规则 | 第 5 部分 |
注意这五块之间靠接口解耦,而不是直接调用——这是读懂 Prometheus 架构的钥匙。 最关键的两个接口在 storage/:
storage.Appender—— 抓取只管“把采样点交出去”,不关心背后是 TSDB、远程写、还是测试假实现。storage.Querier—— 查询引擎只管“按标签匹配器要 series”,不关心数据从内存还是磁盘来。
于是“采集”和“存储”、“存储”和“查询”之间是可替换的边界。这也是 Thanos、Mimir 这类项目能“把 Prometheus 的存储换掉、复用它的抓取和 PromQL”的原因——边界处就是生态的接口。
根本选择一:pull 而非 push
Prometheus 最著名的设计取舍:它主动去抓你,而不是你推给它。
pull 让 Prometheus 掌握主动权,这带来一串连锁的好处——理解这一点,才能理解为什么它长成现在这样。
- 目标列表是“真相” —— Prometheus 通过服务发现知道应该有哪些目标。抓不到 = 目标出问题了。如果是 push,你永远不知道“没收到数据”是因为服务挂了还是因为它根本没部署。
- 抓取即健康检查 —— 每次抓取的成败,被自动记成一个
up指标(源码scrape/scrape.go:2044)。你不用额外配存活探测,up == 0就是它挂了。这是个极优雅的副产品。 - 不会被打爆 —— 抓取节奏由 Prometheus 定(如 15s 一次)。客户端就算疯狂产生指标,也只是让那一次抓取变大,不会像 push 那样瞬间打垮服务端。
- 易于调试 ——
/metrics是个普通 HTTP 端点,你curl一下就能看到这个服务当前所有指标。这个“随时可自查”的性质极其实用。
代价:Prometheus 必须网络可达每个目标。这在短命的批处理任务(跑完就没了,来不及被抓)、NAT/防火墙后的客户端上行不通——于是有了 Pushgateway 这类补丁。但补丁是例外,pull 是主线。
根本选择二:单机自足,不搞分布式
另一个同样重要、却常被忽略的选择:
一个 Prometheus 实例是完全自足的单机进程:自己发现、自己抓、自己存(本地磁盘)、自己查。它刻意不做分布式存储、不做集群共识。
这在“什么都要云原生分布式”的年代显得反直觉,但理由很硬:监控系统必须比被监控系统更可靠。如果 Prometheus 自己是个需要共识、需要协调、可能脑裂的分布式系统,那么当基础设施出问题时,它自己也会挂——而那恰恰是你最需要它的时刻。
单机自足意味着:没有外部依赖 = 没有额外的故障模式。你的 etcd 挂了、网络分区了,Prometheus 依然能抓到它够得着的目标、依然能查本地数据、依然能告警。这是一个用“能力上限”换“可靠性下限”的清醒取舍。
代价当然有:单机容量有限、数据不高可用。业界的解法不是“把 Prometheus 改成分布式”,而是在它之上叠一层——跑多个独立实例(冗余),用 Thanos / Mimir 做长期存储和全局查询。保持核心简单,把复杂性推到外层,这个思路值得记住。
一次抓取的完整链路
把五块串起来,一次抓取的实际链路是:
discovery/吐出目标列表(如 K8s 里所有带某注解的 pod)。- relabeling 对目标做改写 / 过滤(决定最终抓谁、带什么标签)。
scrape/的scrapePool(源码scrape/scrape.go:84)为每个目标起一个scrapeLoop(scrape.go:828),它的run()(scrape.go:1263)就是那个按周期循环抓取的心脏。- 抓回文本 → 解析 → 再做一轮 metric relabeling → 通过
storage.Appender交给 TSDB。 - 顺带自动写入几个元指标:
up、scrape_duration_seconds、scrape_samples_scraped等(scrape.go:2044起)——后面会讲这些是排查采集问题的第一现场。
这条链路就是第 1 部分剩下几章的顺序:数据模型 → 指标类型 → 暴露格式 → 服务发现 → 抓取循环 → relabeling。
小结与下一章
Prometheus 是一个自足的二进制,五块组成:服务发现(discovery/)、抓取(scrape/)、存储(tsdb/)、查询(promql/)、规则(rules/),彼此靠 storage.Appender/storage.Querier 两个接口解耦(边界处即生态接口,Thanos/Mimir 由此而来)。两个根本选择:pull 模型(目标列表即真相、抓取即健康检查(up)、不会被打爆、可 curl 自查;代价是要网络可达)、单机自足(监控必须比被监控更可靠 → 无外部依赖 = 无额外故障模式;容量与高可用推到 Thanos/Mimir 那层解决)。
骨架有了,先看数据本身长什么样——Prometheus 的数据模型,以及它为省内存做到了什么地步。👉 数据模型。
参考资料
- [1] Prometheus 源码
cmd/prometheus/main.go(五块的接线)、storage/interface.go(Appender/Querier)。 - [2] Prometheus 源码
scrape/scrape.go(scrapePool:84、scrapeLoop:828、run:1263、up:2044)。 - [3] Prometheus 官方文档
docs/index.md、docs/getting_started.md。