Prometheus 源码解读(一)
Prometheus 是云原生监控领域的事实标准,越来越多的开源项目开始支持 Prometheus 监控数据格式。从本篇开始,我将和大家一起阅读分析 Prometheus 源码。学习 Prometheus 的设计理念,了解 Prometheus 的局限性与不足。本系列分八个板块逐一拆解 Prometheus 源码。本文基于 Prometheus v2.13.0。
- 工作原理与架构
- 时序数据库模块(TSDB)
- 配置文件加载模块(Configuration Reloader)
- 服务发现模块(Service Discovery Manager)
- 数据抓取模块(Scrape Manager)
- RES API 模块(Web Handler)
- 查询引擎模块(Query Engine & PromQL)
- 性能与优劣势总结
1. Prometheus 介绍
Prometheus 是基于时序数据库(Time Series Database, TSDB)的监控告警系统。Prometheus 在 2016 年加入 CNCF 基金会,成为继 Kubernetes 后第二个毕业项目,其火热程度可见一斑。相比于传统的监控方案,Prometheus 有以下几个优势:
- 高效的存储引擎
Prometheus 将监控指标以时序数列的格式存储在时序数据库 TSDB 中。相比于传统的关系型数据库,时序数据库便于对已有数据进行聚合;在高并发的情况下,读写性能也远高于关系型数据库。Prometheus 2.0 版本重构了底层时序存储引擎。目前,单个 Prometheus 服务器可以做到每秒存储百万条指标数据,同时占用磁盘空间也很小
- 强大的查询能力:PromQL
Prometheus 有独立的 PromQL 查询语言,另外还提供了很多内置的基于时间的处理函数,降低数据聚合的难度
- 面向服务的架构
Prometheus 采用拉模型收集时序数据,数据拉取行为是由服务端来决定的。服务端可以通过某种服务发现机制来自动发现监控对象。而对于推模型的监控系统,客户端需要负责在服务端上进行注册及监控数据推送,这在微服务架构里实现起来比较难的。当大量客户端向服务的主动推送数据时,服务端的压力较大
- 与 Kubernetes 天然集成
Kubernetes 本身的指标也是以 Prometheus 格式暴露出来的
- 逐步完善的生态
OpenMetrics:Prometheus 的数据格式逐渐成为一种标准。OpenMetrics 正在从 Prometheus 的数据格式中分离出来,逐渐成为监控数据格式的国际标准
Thanos:支持数据存储的可伸缩,弥补 Prometheus 数据持久化方面的不足Prometheus
Prometheus Operator:简化 Prometheus 配置管理
2. 架构分析
图的左边开始是监控数据源。任何应用服务想要接入 Prometheus,都需要提供 HTTP 接口(通常是 x.x.x.x/metrics 地址),并暴露 Prometheus 格式的监控数据。Prometheus Server 通过 HTTP 协议周期性抓取监控目标的监控数据、打时间戳、存储到本地。Prometheus 提供了 Client 库帮助开发人员在自己的应用中集成符合 Prometheus 格式标准的监控指标。
而对于不适合直接在代码中集成 Client 库的场景,比如应用来自第三方、不是由自己维护,应用不支持 HTTP 协议,那就需要为这些场景单独编写 Exporter 程序。Exporter 作为代理,把监控数据暴露出来。比如 Mysql Exporter,Node Exporter。
Prometheus 将采集到的数据存储在本地时序数据库中,但缺少数据副本。这也是 Prometheus 自身在数据持久化方面做的不足的地方。但这些存储问题都有其他的解决方案,Prometheus 支持 remote write 方式将数据存储到远端。
Prometheus 支持通过 Kubernetes、静态文本、Consul、DNS 等多种服务发现方式来获取抓取目标(targets)。最后,用户编写 PromQL 语句查询数据并进行可视化。
3. 核心组件
Prometheus 的功能由多个互相协作的组件共同完成。这些组件也即本文开头所列出的模块,比如 Service Discovery Manager。我们后续会逐一介绍。Prometheus 源码入口 main() 函数 完成参数初始化工作,并依次启动各依赖组件。
首先,main 函数解析命令行参数(详见附录 A),并读取配置文件信息(由 --config.file 参数提供)。Prometheus 特别区分了命令行参数配置(flag-based configuration)和文件配置(file-based configuration)。前者用于简单的设置,并且不支持热更新,修改需要启停 Prometheus Server 一次;后者支持热更新。
main 函数完成初始化、启动所有的组件。这些组件包括:Termination Handler、Service Discovery Manager、Web Handler 等。各组件是独立的 Go Routine 在运行,之间又通过各种方式相互协调,包括使用 Synchronization Channel、引用对象 Reference、传递 Context(Context 包的使用可以参考作者的 《Golang Context 包详解》一文)。
这些 Go Routine 的协作使用了 oklog/run 框架。oklog/run 是一套基于 Actor 设计模式的 Go Routine 编排框架,实现了多个 Go Routine 作为统一整体运行并有序依次退出。这在很多开源项目中都有使用,进一步了解可参考作者的另一篇文章《Go routine 编排框架:oklog/run 包》
4. 参考文档
「K8S 技术落地实践」Prometheus 在 K8S 上的监控实践 来自本文作者在杭州容器 Meetup 的分享
Prometheus Internal architecture
附录 A:Prometheus 启动参数
- web:Prometheus 服务器 HTTP 连接参数,REST API 启用相关参数,Prometheus Console 网页配置
- storage:TSDB 相关配置
- query:查询执行相关配置
参数名 | 解释 |
---|---|
config.file | 配置文件的位置,包含抓取监控对象列表(用于服务发现)和 recording rules 文件位置 |
web.listen-address | Prometheus server 端监听请求(API 请求、Dashboard 界面)的地址和端口,默认本地端口 9090 |
web.read-timeout | 设置 Prometheus server 读取客户端请求的超时时间。以此来避免客户端因超慢的写操作,长时间占用链接资源。ReadTimeout 覆盖了从连接请求被接受到请求报文被完全读取的时间。默认 5m |
web.max-connections | 最大连接数,默认 512 |
web.external-url | 设置 Prometheus 对外的 URL。需要设置外部访问的 Prometheus 通常因为启用了反向代理。如果 external-url 包含路径,则 Prometheus 所有对外的接口,都会带上该路径作为前缀。eg. http://<host>:<port>/<path>/api/v1/query?query=up |
web.route-prefix | 用于替换 external-url 的路径前缀,eg. http://<host>:<port>/<route-prefix>/api/v1/query?query=up |
web.user-assets | 网页静态 asset 文件夹路径 |
web.enable-lifecycle | 开启后,可以实现通过请求 /-/reload 热加载更新配置,默认 false |
web.enable-admin-api | 开启后,允许使用一些管理员级别的 api,包括删除时间序列等,/api/v1/admin/tsdb/delete_series 。默认 false |
web.console.templates | Prometheus Console 网站模板文件夹路径 |
web.console.libraries | Prometheus Console 使用的库路径 |
web.page-title | Console 页面标题 |
web.cors.origin | 设置 Prometheus 服务端允许的域,用正则表达式表示。eg. https?://(domain*).com |
storage.tsdb.path | 监控数据存储路径,默认 data/ |
storage.tsdb.min-block-duration | 设置数据块最小时间跨度,默认 2h 的数据量。监控数据是按块(block)存储,每一个块中包含该时间窗口内的所有样本数据(data chunks) |
storage.tsdb.max-block-duration | 设置数据块最大时间跨度,默认为最大保留时间的 10% |
storage.tsdb.wal-segment-size | 设置 WAL 分段存储每个分段的大小。默认 128MB |
storage.tsdb.retention.time | 监控数据最大保留时间,默认 15d |
storage.tsdb.no-lockfile | 不在数据存储目录中创建文件锁 |
storage.tsdb.wal-compression | 开启后,会对 WAL 文件进行压缩(成本是带来 CPU 开销)。默认 false |
storage.remote.flush-deadline | |
storage.remote.read-sample-limit | 一次最多从远端存储中读取采样数据量,默认 5e7 条,0 表示无限制 |
storage.remote.read-concurrent-limit | 最大并发读,默认 10 个请求,0 表示无限制 |
rules.alert.for-outage-tolerance | |
rules.alert.for-grace-period | |
rules.alert.resend-delay | |
alertmanager.notification-queue-capacity | |
alertmanager.timeout | |
query.lookback-delta | 在计算 PromQL 表达式结果时,最大回看时间 |
query.timeout | 查询超时时间 |
query.max-concurrency | 最大并发处理查询请求数,默认 20 个请求 |
query.max-samples | 能载入到内存中最大采样数据量,默认 5e7 条 |