工作流程
整个工作流程可以分为以下四个主要阶段:
- 指标采集(Metrics Collection) :Prometheus Server从被监控的服务中拉取metrics数据。这通常是通过HTTP endpoint实现的,被监控的服务暴露出一个如
/metrics的接口,Prometheus Server定时从这个接口拉取数据。对于不方便直接被Prometheus采集的场景(比如短命令任务),可以使用PushGateway,将metrics推送到这个中间层,然后由Prometheus来拉取。 - 数据存储(Data Storage) :拉取到的数据会被Prometheus Server存储在本地磁盘上,也可以选择远端存储。Prometheus使用自己的时间序列数据库TSDB存储数据,并且将每个时间点的所有值存储在连续的块中。
- 数据展示(Data Visualization) :Prometheus提供了基本的数据可视化界面,但是大多数情况下我们会使用Grafana等更强大的工具进行数据可视化。Prometheus Server通过API提供数据给这些工具。
- 告警处理(Alerting) :Prometheus Server可以配置规则文件,一旦满足某种条件就触发告警。告警信息会发送到Alertmanager,Alertmanager负责对告警信息进行去重、分组并路由给接收者。Alertmanager还可以静默和抑制告警,并通过邮件、webhook等方式发送告警通知。
数据展示
Grafana 是一个流行的开源可视化工具,它与 Prometheus 集成紧密,可以通过 Prometheus 的查询语言( PromQL )直接查询和可视化监控数据。通过 Grafana ,用户能够创建丰富、交互式的仪表板,实时监控系统的状态。此外, Prometheus 还提供了 API ,使得其他应用或工具也能够使用其数据进行定制化的集成和可视化。
官方架构
Prometheus Server
用于收集和存储时间序列数据。Prometheus Server 是 Prometheus 组件中的核心部分,负责实现对监控数据的获取,存储以及查询。 Prometheus Server 可以通过静态配置管理监控目标,也可以配合使用 Service Discovery 的方式动态管理监控目标,并从这些监控目标中获取数据。其次 Prometheus Server 需要对采集到的监控数据进行存储,Prometheus Server 本身就是一个时序数据库,将采集到的监控数据按照时间序列的方式存储在本地磁盘当中。最后Prometheus Server 对外提供了自定义的 PromQL 语言,实现对数据的查询以及分析。
Exporter
用于暴露已有的第三方服务的 metrics 给 Prometheus。Exporter 将监控数据采集的端点通过 HTTP 服务的形式暴露给 Prometheus Server,Prometheus Server 通过访问该 Exporter 提供的 Endpoint 端点,即可获取到需要采集的监控数据。
Push Gateway
主要用于短期的 jobs。由于这类 jobs 存在时间较短,可能在 Prometheus 来 pull 之前就消失了。为此,这些 jobs 可以直接向 Prometheus server 端推送它们的 metrics。
Grafana
第三方展示工具,可以编写 PromQL 查询语句,通过 http 协议与 prometheus 集成。
AlertManager
从 Prometheus Server 端接收到 alerts 后,会进行去除重复数据,分组,并路由到对方的接受方式,发出报警。常见的接收方式有:电子邮件,钉钉、企业微信,pagerduty等。
Client Library
客户端库,为需要监控的服务生成相应的 metrics 并暴露给 Prometheus Server。当 Prometheus Server 来 pull 时,直接返回实时状态的 metrics。
指标类型
- Counter(计数器) :是一个只能增加(或在某些情况下重置为零)的度量。常用于记数,例如记录请求数量、任务完成数量等。
- Gauge(仪表盘) :是一个可以任意上下波动的度量。常用于测量当前的值,例如内存使用量、CPU占用率、正在运行的线程数等。
- Summary(摘要) :是一种复杂的度量,它提供了总数、平均值和百分位数信息。摘要类型的度量在客户端进行计算和聚合,并且由于涉及到百分位数计算,所以无法在多个实例或服务间进行聚合。
- Histogram(直方图) :也是一种复杂的度量,提供了观察的数量、总和以及在配置桶中观察的数量。不同于Summary,直方图将数据分布到一系列预定义的桶中,因此允许对多个实例或服务进行聚合。然而,由于桶的定义需要预先设定,过细或过粗的桶划分都会影响其效果。
数据模型
Prometheus的监控数据储存形式是指标(Metric),这些都被保存在内建的TSDB数据库中。这些数据均为时间序列属性,即包含时间戳的样本值,每一份数据都有其独特的标识符。除了持久化储存的时间序列数据,基于用户查询的需求,Prometheus也能生成临时、派生的时间序列作为返回结果。以下我们详细解析一下:
-
指标(Metric) :在Prometheus系统中,所有监控数据都以指标的方式来保存。一个指标由其名称(常用作标识符)和一系列样本值组成。这些样本按照时间顺序排列,构成时间序列数据。指标通常反映了某个系统、应用或其他元素的性能,例如CPU占用率、内存使用量、请求处理速度等。
-
时间序列(Time Series) :时间序列是一系列依据时间排序的数据点。在Prometheus语境下,每个指标都有对应的时间序列。一个时间序列主要由以下部分构成:
- 标签(Labels) :可以用来区分不同的维度(比如
method="GET"与method="POST"就可以用来区分这两种不同的HTTP请求指标数据) - 样本值(Sample Values) :这是与时间戳关联的度量值。
- 标签(Labels) :可以用来区分不同的维度(比如
以监控多个微服务的HTTP请求延迟为例,你可能会使用名为http_request_duration_seconds的指标Metric来衡量这些请求的时长。但由于涉及到多个服务的监控,我们需要一种方式来明确区分不同服务的请求。
此时,就可以利用如下的标签进行区分:
service="user-service"service="payment-service"
在Prometheus中执行查询时,这些标签就可以被用来过滤结果。例如,若要查看"user-service"的HTTP请求延迟情况,可以执行以下查询:
http_request_duration_seconds{service="user-service"}
这里,"service"标签成为了"http_request_duration_seconds"指标的独特标识符,使得我们可以区分并查询各个服务的请求延迟。
其中http_request_duration_seconds是一个指标(Metric)。在Prometheus中,指标用于描述你希望衡量的某项属性,比如在此例中,http_request_duration_seconds这个指标就用来衡量HTTP请求的响应时间。
此类指标通常还会包含一些标签(Labels),如 method, path, status_code 等,为请求提供更多详细信息。例如,下面这个完整的时间序列样本:
http_request_duration_seconds{method="GET", path="/api/user", status_code="200"} 0.2
表示一个向"/api/user"路径发起的GET请求,状态码为200,响应时间是0.2秒。
通过上述指标及其附带的标签,Prometheus能够追踪、监控和报告各种HTTP请求的响应时间。
关于指标与标签命名的最佳实践: prometheus.io/docs/practi…
指标
Prometheus 的指标暴露方式就是通过 HTTP 端点来提供基于文本格式的指标数据。其中包括三个重要部分:样本的一般注释信息(HELP),样本的类型注释信息(TYPE)以及样本本身。
以 # HELP 开始的行,后续跟随指标名称和说明信息。 例如:
# HELP http_requests_total The total number of processed HTTP requests.
这里 http_requests_total 是指标名称,而 "The total number of processed HTTP requests." 是对该指标的描述。
再比如以 # TYPE 开始的行,后面跟随指标名称和指标类型。 例如:
# TYPE http_requests_total counter
这里 http_requests_total 是指标名称,counter 是该指标的类型。
样本本身则包含指标名、可能的标签与其值。 例如:
http_requests_total{status="200"} 9968
这里 http_requests_total 是指标名称,status="200" 是一个标签,9968 是该标签下此指标的值。最后需要注意,“TYPE”注解必须在指标的第一个样本之前,如果没有明确的指标类型,需要返回为 untyped。
所有以 # 开始的行外,其他行都被认为是监控样本数据,包含指标名、标签和样本值。每一行样本应符合以下的格式:
<指标名称>{<标签名>=<标签值>, ...}
比如说,如果我们有一个指标叫做 http_request_total,配有标签 method="POST" 和 endpoint="/messages",我们可以用以下的方式来描述这个指标:
http_request_total{method="POST", endpoint="/messages"}
在 Prometheus 的底层实现中,指标名称其实是以 name=<指标名称> 的形式存储在数据库里的。因此,上述指标也可以表述为:
{__name__="http_request_total", method="POST", endpoint="/messages"}
所以,一个指标可以被看作是一个标签集,其中必然包含一个 name 标签来定义该指标的名称。
时间序列
Prometheus 采集并把所有的样本数据以时间序列方式保留在内存数据库里,定期同步到硬盘存储。每个时间序列都是根据时间戳和值来组织的,我们可以将其视为向量(vector)。唯一性标识符由一个度量名称和一对标签(键值对)构成,这用于精确确定每条时间序列。下面我们详细探讨:
-
内存数据库与时间序列:Prometheus 在内存数据库中以时间序列形式保存收集到的样本数据,从而实现数据查询和实时监控的高效性。每个时间序列包含了时间戳以及对应的测量值,反映了特定指标在各个时间点的读数。
-
向量(vector):在 Prometheus 中,我们常常称时间序列为向量(vector),因为每个时间序列都可以被视为由某一时刻上的数据值构成的向量。Prometheus 利用向量来表达和处理时间序列数据,这便于进行时间序列的操作和查询。
-
唯一标识符:在 Prometheus 中,每条时间序列都有一个独特的标识符,由度量名称和一系列标签(键值对)构成。度量名称通常表示被测量的属性,而标签则更精确地指出了时间序列的来源、特性或环境。这种结构在查询和筛选数据时提供了极大的灵活性。
-
定期同步至硬盘:为了确保数据的持久性,Prometheus 定期将内存中的时间序列数据同步至硬盘上的本地存储引擎(TSDB)。这样,即使 Prometheus 需要重新启动,也能从硬盘上恢复已经采集的时间序列数据。
-
本地存储引擎:本地存储引擎( TSDB )是 Prometheus 的关键组件之一,负责实际的时间序列数据存储和管理。这种本地化的存储设计使得 Prometheus 在进行数据查询和分析上更加高效。
向量
在Prometheus中,"向量"是一种特殊的数据类型,用于表示和处理时间序列数据。这里的"向量"并不是传统数学上的向量概念,而更多地被用来表示一组数据。具体到Prometheus,有两种类型的向量:
- 即时向量(Instant Vector) :这种向量代表一个特定时间点上的所有时间序列数据。它们包含了在某一特定时间点,各个时间序列的最新样本值。
- 范围向量(Range Vector) :这种向量表示的是在一段时间范围内的所有时间序列数据。它们包含了在特定时间范围内,各个时间序列的所有样本值。
在这里,“一组数据”是指作为向量(vector)的一部分的一系列相关联的值。对于Prometheus,这些值通常都关联了一个特定的时间戳。
例如,假设你在监控两个微服务的HTTP请求延迟,你可能有一个名为http_request_duration_seconds的度量,用于衡量这些请求的持续时间。每个微服务生成的时间序列都是该度量的不同实例,并带有相应的标签来区分彼此。
如果你在某一特定时间点查询http_request_duration_seconds度量,你将得到两个值:一个对应第一个服务,另一个对应第二个服务。这两个值就构成了"一组数据",在Prometheus中,也被称作一个"即时向量"。
它可能看起来像这样:
http_request_duration_seconds{service="user-service"} 0.3http_request_duration_seconds{service="payment-service"} 0.4
虽然每个值都与特定的时间戳紧密相关,但它们同时也表示了各自的微服务在同一时间点的HTTP请求延迟。所以,我们说这是"一组数据",因为这些数据项有着共同的度量和时间戳,但有着不同的标签和值。
存储格式
Prometheus 采用了两小时为一单位的时间窗口,将这个窗口内产生的数据集合在一个称为块(Block)的地方进行存储。每个块对应一个独立的目录,该目录含有所有属于那个时间窗口的样本数据(chunks),元数据文件(meta.json)和索引文件(index)。
以下是关于时间窗口、块和块目录结构的详细解读:
- 时间窗口和块: Prometheus 把时间序列数据分为多个时间窗口,常规情况下,每个窗口的时长是两小时。这个窗口期间生成的所有样本数据都会被保存在一个块里。块是 Prometheus 存储引擎的基础组成部分,代表了一定时期内的数据集合。
- 块目录结构:每个块形成一个单独的目录,其中包含了对应时间窗口内的全部样本数据。通常, 块目录会包括以下几个部分:
- Chunks:位于块目录中的 chunks 文件包含了该时间窗口内产生的所有样本数据。Chunks 是优化和压缩后的样本数据,设计用于高效的存储和查询。
- 元数据文件(meta.json):meta.json 文件提供了块的元数据信息,如时间范围、标签信息等。这些信息对于数据的查询和检索至关重要。
- 索引文件(index):索引文件包含了快速查找和定位时间序列数据所需的索引信息。
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
以下是各个部分的描述:
-
01FB9HHY61KAN6BRDYPTXDX9YF和01FB9Q76Z0J10WJZX3PYQYJ96R:这些是 Block ID,Prometheus 为每个块生成一个唯一的 ID。chunks: 它包含了采集到的样本数据,存储在不同的文件中,如000001。index:它是索引文件,帮助快速查找和定位时间序列数据。meta.json:它是元数据文件,包含有关该块的信息,例如时间范围、标签等。tombstones: 它记录了已删除的样本数据。
-
chunks_head: 这个目录保留当前活动的时间窗口的样本数据。 -
lock:这个文件用于防止 Prometheus 同时启动多次。 -
queries.active:此文件跟踪正在进行的查询。 -
wal(Write Ahead Log):这个目录保存了尚未压缩进块但需要持久化的近期样本数据和事务日志,以防止在故障中丢失这些数据。子目录checkpoint.00000010是 WAL 的检查点,用于在重启或者故障恢复时加速处理。
集成报警
Prometheus 是一个集时间序列数据的收集、处理和主动警报系统为一体的平台。其核心理念是在一个统一的数据模型中最大限度地收集关于系统的数据,以便用户可以进行全面的查询。同时,用于实时查询和创建仪表盘的查询语言也同样适用于制定警报规则。
以下是一个 Prometheus 报警规则的例子,这个规则将会在本地机器的磁盘可用空间小于 15% 时触发。
groups:
- name: example
rules:
- alert: LowDiskSpace
expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 < 15
for: 1m
labels:
severity: critical
annotations:
summary: "Low disk space on {{ $labels.instance }}"
description: "{{ $labels.instance }} has low disk space."
解释:
alert为报警名。expr是触发此报警的 PromQL 表达式。该表达式检查所有节点的磁盘使用率是否高于85%(即可用空间小于15%)。for规定了该表达式持续满足多久才发送报警,此处设为 1 分钟。labels和annotations都可以包含任意用户定义的键值对,它们在报警通知中被展示出来。其中,severity属于标签,而summary和description属于注解。
Prometheus不足
Prometheus作为一个基于度量的系统,不适合存储日志 (Log) 和链路 (Tracing) 等监控。
数据存储默认15天,可以根据需要进行更改,核心还是关注短期内的数据。