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 源码解读(一)-LMLPHP

  • 面向服务的架构

Prometheus 采用拉模型收集时序数据,数据拉取行为是由服务端来决定的。服务端可以通过某种服务发现机制来自动发现监控对象。而对于推模型的监控系统,客户端需要负责在服务端上进行注册及监控数据推送,这在微服务架构里实现起来比较难的。当大量客户端向服务的主动推送数据时,服务端的压力较大

Prometheus 源码解读(一)-LMLPHP

  • 与 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 语句查询数据并进行可视化。

Prometheus 源码解读(一)-LMLPHP

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 在运行,之间又通过各种方式相互协调,包括使用 Channel、引用对象 Reference、传递 Context(Context 包的使用可以参考作者的 《Golang Context 包详解》一文)。

这些 Go Routine 的协作使用了 oklog/run 框架。oklog/run 是一套基于 Actor 设计模式的 Go Routine 编排框架,实现了多个 Go Routine 作为统一整体运行并有序依次退出。这在很多开源项目中都有使用,进一步了解可参考作者的另一篇文章《Go routine 编排框架:oklog/run 包》

Prometheus 源码解读(一)-LMLPHP

4. 参考文档

「K8S 技术落地实践」Prometheus 在 K8S 上的监控实践 来自本文作者在杭州容器 Meetup 的分享

Prometheus Internal architecture

附录 A:Prometheus 启动参数

  • web:Prometheus 服务器 HTTP 连接参数,REST API 启用相关参数,Prometheus Console 网页配置
  • storage:TSDB 相关配置
  • query:查询执行相关配置
10-24 22:31