起源
Prometheus 起源于 SoundCloud ,因为微服务迅速发展,导致实例数量以几何倍数递增,不得不考虑设计一个符合以下几个功能的监控系统:
多维数据模型,可以按照实例,服务,端点和方法之类的维度随意对数据进行切片和切块。
操作简单,可以随时随地部署监控服务,甚至在本地工作站上,而无需设置分布式存储后端或重新配置环境。
可扩展的数据收集和分散的架构,以便于可以可靠的监控服务的许多实例,独立团队可以部署独立的监控服务。
转化为一种查询语言,可以利用数据模型进行有效的警报和图形展示。
但是,当时的情况是,以上的功能都分散在各个系统之中,直到2012年 SoundCloud 某位大神启动了一个孵化项目, SoundCloud 才把所有功能集合到一起, 这时也就有了 Prometheus。 Prometheus 是用Go语言编写,从一开始就是开源的。 尽管一直很低调,一开始还是获得了很多粉丝与贡献者; 2016 年 Prometheus 成为继 Kubernetes 之后,成为 CNCF(Cloud Native Computing Foundation) 第二个成员。
云原生
对于云原生一词,想必大家已经不陌生了,因为谷歌、百度、必应搜索以后,必然会出现一堆关于云原生的相关介绍,为了解惑,在这里就简单的讲一下云原生的定义起源与发展,如果大家想 了解更多云原生的定义与详细解释,可以去搜索关键词 CloudNative
了解。
Pivotal最初的定义
云原生最早的定义是Pivotal公司的Matt Stine写了一本叫做迁移到云原生应用架构的小册子,里面提到了:
符合12因素应用
面向微服务架构
自服务敏捷架构
基于API的协作
抗脆弱性
CNCF最初的定义
到了2015年Google主导成立了云原生计算基金会CNCF,起初CNCF对云原生(Cloud Native)的定义包含以下三个方面:
应用容器化
面向微服务架构
应用支持容器的编排调度
重定义
到了2018年,随着近几年来云原生生态的不断壮大,所有主流云计算供应商都加入了该基金会,且从Cloud Native Landscape中可以看出云原生有意蚕食原先非云原生应用的部分。CNCF基金会中的会员以及容纳的项目越来越多,该定义已经限制了云原生生态的发展,CNCF为云原生进行了重新定位。
云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。
这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。
云原生计算基金会(CNCF)致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用。
CNCF Cloud Native Definition v1.0 - github.com
发展
2015
GitHub 完全开源后,STAR数直逼9k,同时也被很多公司作为监控方案采用;
2016
Prometheus 加入 CNCF(Cloud Native Computing Foundation),继 Kubernetes 加入后的第二个项目成员。
2017
Prometheus 2.0 发布,这是Prometheus的一个重要的里程碑,在集成TSDB后的Prometheus 2.0,与Prometheus 1.8相比,CPU使用率降低到20%-40%,磁盘I/O、磁盘空间使用率降低到33%-50%,查询负载通常平均<1%;
2020
时至今日, Prometheus 版本已经迭代到2.17.1 ,STAR数已超30k,已经是云原生不可分割的重要一部分。
什么是 Prometheus
Prometheus的数据以时序数据库的方式进行存储,让单台的Prometheus处理大量的数据,并且发明了promsql配套语法,同时采用pull模型,主动发送请求去pull这些数据,然后将数据存储到本地磁盘上面,存储格式是按照时序数据库的格式。
如果有些没有提供metric接口,没有兼容Prometheus,那么就需要写exporter接口,这通常由第三方提供。
Prometheus 是一个基于指标监控和报警的工具栈。 Prometheus 起源于 SoundCloud ,因为微服务迅速发展,导致实例数量以几何倍数递增,不得不考虑设计一个符合以下几个功能的监控系统:
- 多维数据模型,可以按照实例,服务,端点和方法之类的维度随意对数据进行切片和切块。
- 操作简单,可以随时随地部署监控服务,甚至在本地工作站上,而无需设置分布式存储后端或重新配置环境。
- 可扩展的数据收集和分散的架构,以便于可以可靠的监控服务的许多实例,独立团队可以部署独立的监控服务。
- 一种查询语言,可以利用数据模型进行有效的报警和图形展示。
但是,当时的情况是,以上的功能都分散在各个系统之中,直到 2012 年 SoundCloud 启动了一个孵化项目把这些所有功能集合到一起,也就是 Prometheus。Prometheus 是用 Go 语言编写,从一开始就是开源的,到 2016 年 Prometheus 成为继 Kubernetes 之后,成为 CNCF 的第二个成员。
到现在为止 Prometheus 提供的工具或与其他生态系统组件集成可以提供完整的监控管道:
- 检测(跟踪和暴露指标)
- 指标收集
- 指标存储
- 查询指标,用于报警、仪表板等
Prometheus 具有足够的通用性,可以监控各个级别的实例:你自己的应用程序、第三方服务、主机或网络设备等等。此外 Prometheus 特别适用于监控动态云环境和 Kubernetes 云原生环境。
但是也需要注意的是 Prometheus 并不是万能的,目前并没有解决下面的一些问题:
- 日志和追踪(Prometheus 只处理指标,也称为时间序列)
- 基于机器学习或 AI 的异常检测
- 水平扩展、集群化的存储
这些功能显然也是非常有价值的,但是 Prometheus 本身并没有尝试去解决,而是留给了第三方解决方案。
但是整体来说与其他监控解决方案相比,Prometheus 提供了许多重要功能:
- 多维数据模型,允许对指标进行跟踪
- 强大的查询语言(PromQL)
- 时间序列处理和报警的整合
- 与服务发现集成来确定应监控的内容
- 操作简单
- 执行高效
尽管这些功能中的许多功能如今在监控系统中变得越来越普遍,但是 Prometheus 是第一个将所有功能组合在一起的开源解决方案。
操作简单
Prometheus 的整个概念很简单并且操作也非常简单。 Prometheus 用 Go 编写,直接使用独立的二进制文件即可部署,而无需依赖外部运行时(例如 JVM)、解释器(例如 Python 或 Ruby)或共享系统库。
每个 Prometheus 服务器都独立于任何其他 Prometheus 服务器收集数据并评估报警规则,并且仅在本地存储数据,而没有严格的集群或副本。
要创建用于报警的高可用性配置,你仍然可以运行两个配置相同的 Prometheus 服务器,以计算相同的报警(Alertmanager 将对通知进行去重操作):
当然,Prometheus 的大规模部署还是非常复杂的,在后面的章节中接触到,此外 Prometheus 还暴露了一些接口,允许第三方开发者来解决一些问题,比如远程存储这些功能。
性能高效
Prometheus 需要能够同时从许多系统和服务中收集详细的指标数据,为此,特别对以下组件进行了高度优化:
- 抓取和解析传入的指标
- 写入和读取时序数据库
- 评估基于 TSDB 数据的 PromQL 表达式
根据社区的使用经验显示,一台大型 Prometheus 服务器每秒可以摄取多达 100万
个时间序列样本,并使用 1-2
字节来将每个样本存储在磁盘上。
系统架构
Prometheus 是一个监控系统和时序数据库,特别适合监控云原生环境,它具有多维数据模型和强大的查询语言,并在一个生态系统中集成了检测、指标收集、服务发现和报警等功能。
下图演示了 Prometheus 系统的整体架构
Prometheus是可以去抓取后面的指标数据,指标数据可以是多种多样的,可以是第三方的服务,或者一些设备,或者一些metrics的接口(微服务,设备,中间件,数据库),同时这些抓取的任务配置到Prometheus,这样Prometheus启动的时候就可以主动的去抓取这些数据,数据抓取之后会存储到本地的TSDB数据库里面。
还有一种方式是通过服务发现来抓取目标。
一个团队通常会运行一个或多个 Prometheus 服务器,这些服务器构成了 Prometheus 监控的核心。Prometheus 服务器可以配置为使用服务发现机制(如 DNS、Consul、Kubernetes 等)来自动发现一组指标源(所谓的目标
),然后 Prometheus 通过 HTTP 定期从这些目标中以文本格式抓取
指标,并将收集到的数据存储在本地时序数据库中。
抓取的目标可以是一个直接暴露 Prometheus 指标的应用程序,也是可以是一个将现有系统(比如 MySQL)的 metrics 指标转换为 Prometheus 指标标准格式的中间应用(也就是我们说的 exporter,因为对于像MySQL,redis这种没有内置指标的服务,所以可以利用exporter来做一个中间的转换
),然后 Prometheus 服务器通过其内置的 Web UI、其他仪表盘工具(比如 Grafana)或者直接使用其 HTTP API 来提供收集到的数据进行查询。
注意:每次抓取只会将目标的每个时间序列的当前值传输给 Prometheus,所以抓取间隔决定了存储数据的最终采样频率。目标进程本身不保留任何历史指标数据。(metrics接口只负责将最新的数据暴露,不会存储这些数据)
另外我们还可以配置 Prometheus 根据收集的指标数据生成报警,但是 Prometheus 不会直接把报警通知发送给我们,而是将原始报警转发到 Alertmanager
服务,Alertmanager 是作为单独的服务运行的,可以从多个 Prometheus 服务器上接收报警,并可以对这些报警进行分组、汇总和路由,最后可以通过 Email、Slack、PagerDuty、企业微信、Webhook 或其他通知服务来发送通知。
Prometheus通过pushgateway推送已经获取到的Metric数据,用于短期存储。
把所有获取到的指标存储到本地,并对这些数据进行处理,记录为时间序列的数据用以生成警报。
PromDash (提供WEB页面,PromQL查询)。
提供API,为使用者用于可视化所需要收集的数据。
普罗米修斯的主要特点是:
- 多维数据模型
- 一种灵活的查询语言,可利用此维度查询
- 不依赖分布式存储,单个服务器节点
- 时间序列收集采用HTTP上的Pull模型
- 通过PushGateway推送时间序列指标与联邦查询
- 通过服务发现或静态配置发现目标
- 图形和仪表板支持的多种模式
- Docker、Kubernetes原生支持
Prometheus
特性
- 具有Metrics名称和key/value对标识的时间序列数据的多维数据模型
- 可以利用数据模型进行有效的警报和图形展示的查询语言
- 本地存储,不依赖分布式存储文件系统
- 通过 HTTP WEB 服务获取时间序列数据
- 可配置推送的方式来添加时间序列数据
- 支持通过服务发现或静态配置发现目标
- 多种图形、仪表板支持
Prometheus
核心组件
Prometheus Server
:用于Scrap 目标 Job Metric、存储本地TSDB数据exporter
:暴露本地Metrics
给Prometheus
,让Prometheus
通过Pull
模式Scrap
目标Job指标数据pushgateway
:推送网关,以Push
模式将本地Metrics
数据推送到PushGatewayalertmanager
:警报组件,配置分组、警报发送源ad-hoc
:过滤器,允许动态创建新的键/值过滤器,这些过滤器会自动应用于使用该数据源的所有查询
Prometheus 组件大多都是使用 Go语言 编写,所以很容易构建为静态的二进制文件用于部署。以下是 Prometheus 官方提供的架构图,描述一些相关的生态系统组件:
数据模型
Prometheus 采集的监控数据都是以指标(metric)的形式存储在内置的 TSDB 数据库中,这些数据都是时间序列:一个带时间戳的数据,这些数据具有一个标识符和一组样本值。除了存储的时间序列,Prometheus 还可以根据查询请求产生临时的、衍生的时间序列作为返回结果。
时间序列
Prometheus 会将所有采集到的样本数据以时间序列的形式保存在内存数据库中,并定时刷新到硬盘上,时间序列是按照时间戳和值的序列方式存放的,我们可以称之为向量(vector),每一条时间序列都由一个指标名称和一组标签(键值对)来唯一标识。
- 指标名称反映了被监控样本的含义(如
http_request_total
表示的是对应服务器处理的 HTTP 请求总数)。 - 标签可以用来区分不同的维度(比如
method="GET"
与method="POST"
就可以用来区分这两种不同的 HTTP 请求指标数据)。
如下所示,可以将时间序列理解为一个以时间为 Y 轴的数字矩阵:
^
│ . . . . . . . . . . . . . . . . . . . http_request_total{method="GET",status="200"}
│ . . . . . . . . . . . . . . . . . . . http_request_total{method="POST",status="500"}
│ . . . . . . . . . . . . . . . . . .
│ . . . . . . . . . . . . . . . . . .
v
<------------------ 时间 ---------------->
需要注意的是指标名称只能由 ASCII 字符、数字、下划线以及冒号组成,同时必须匹配正则表达式 [a-zA-Z_:][a-zA-Z0-9_:]*
(冒号不能用来定义指标名称,是用来表示用户自定义的记录规则)。标签的名称只能由 ASCII 字符、数字以及下划线组成并满足正则表达式 [a-zA-Z_][a-zA-Z0-9_]*
,其中以 __
作为前缀的标签,是系统保留的关键字,只能在系统内部使用,标签的值则可以包含任何 Unicode 编码的字符。
样本
时间序列中的每一个点就称为一个样本(sample),样本由以下 3 个部分组成:
- 指标:指标名称和描述当前样本特征的标签集
- 时间戳:精确到毫秒的时间戳数
- 样本值:一个 64 位浮点数
指标
想要暴露 Prometheus 指标服务只需要暴露一个 HTTP 端点,并提供 Prometheus 基于文本格式的指标数据即可。这种指标格式是非常友好的,基本上的格式看起来类似于下面的这段代码:
# HELP http_requests_total The total number of processed HTTP requests.
# TYPE http_requests_total counter
http_requests_total{status="200"} 8556
http_requests_total{status="404"} 20
http_requests_total{status="500"} 68
其中 #
开头的行是注释信息,用来描述下面提供的指标含义,其他未注释行代表一个样本(带有指标名、标签和样本值),使其非常容易从系统和服务中暴露指标出来。
事实上所有的指标也都是通过如下所示的格式来标识的:
<metric name>{<label name>=<label value>, ...}
例如,指标名称是 http_request_total
,标签集为 method="POST", endpoint="/messages"
,那么我们可以用下面的方式来标识这个指标:
http_request_total{method="POST", endpoint="/messages"}
而事实上 Prometheus 的底层实现中指标名称实际上是以 __name__=<metric name>
的形式保存在数据库中的,所以上面的指标也等同与下面的指标:
{__name__="http_request_total", method="POST", endpoint="/messages"}
所以也可以认为一个指标就是一个标签集,只是这个标签集里面一定包含一个 __name__
的标签来定义这个指标的名称。
存储格式
Prometheus 按照两个小时为一个时间窗口,将两小时内产生的数据存储在一个块(Block)中,每个块都是一个单独的目录,里面包含该时间窗口内的所有样本数据(chunks),元数据文件(meta.json)以及索引文件(index)。
其中索引文件会将指标名称和标签索引到样本数据的时间序列中,如果该期间通过 API 删除时间序列,删除记录会保存在单独的逻辑文件 tombstone 当中。
当前样本数据所在的块会被直接保存在内存数据库中,不会持久化到磁盘中,为了确保 Prometheus 发生崩溃或重启时能够恢复数据,Prometheus 启动时会通过预写日志(write-ahead-log(WAL))来重新播放记录,从而恢复数据,预写日志文件保存在 wal 目录中,wal 文件包括还没有被压缩的原始数据,所以比常规的块文件大得多。
Prometheus 保存块数据的目录结构如下所示:
.
├── 01FB9HHY61KAN6BRDYPTXDX9YF
│ ├── chunks
│ │ └── 000001
│ ├── index
│ ├── meta.json
│ └── tombstones
├── 01FB9Q76Z0J10WJZX3PYQYJ96R
│ ├── chunks
│ │ └── 000001
│ ├── index
│ ├── meta.json
│ └── tombstones
├── chunks_head
│ ├── 000014
│ └── 000015
├── lock
├── queries.active
└── wal
├── 00000011
├── 00000012
├── 00000013
├── 00000014
└── checkpoint.00000010
└── 00000000
7 directories, 17 files