《Prometheus Book》阅读笔记

看了一本在线的小书,叫《Prometheus Book》,做了一点摘抄和笔记。

第 1 章:天降奇兵

第一章对 Prometheus 的架构和用法做了简单的介绍。

基本介绍

通过建立完善的监控体系,我们可以达到以下目的:长期趋势分析、对照分析、告警、故障分析与定位、数据可视化。
Prometheus是一个开源的完整监控解决方案,其对传统监控系统的测试和告警模型进行了彻底的颠覆,形成了基于中央化的规则计算、统一分析和告警的新模型。
个人理解,这里的中央化主要是指数据存储中央化:各个数据源将指标暴露出来,由 Prometheus 服务器采集后统一存储、统一分析,方便聚合查询。

Prometheus 的基本结构如图所示:

Prometheus 架构

其中 Prometheus Server 是整个组件中的核心部分,负责实现对监控数据的获取、存储及查询;PushGateway 用于在某些环境中被动收集用户指标,并提供给 Server;在 Server 中定义的报警规则如果被触发,则 AlertManager 会负责该报警的后续处理流程(如通知用户)。

Prometheus Server 通过两种方式来获取数据:

  1. 通过 HTTP API 直接访问实例以获取数据
  2. 当 Prometheus Server 无法直接访问到实例时,实例可以主动推送数据到 PushGateway,Server 访问 PushGateway 来获取数据。

Prometheus 的基本数据模型:
一个指标,由它的名字和值组成。
指标的名字由指标名称以及多对描述样本特征的 KV 标签构成;而指标的值是一个序列,记录了每个时间点的指标值。
Prometheus 将这个基本指标进行了组合和拓展,构成了四种指标数据结构:Counter、Gauge、Summary 和 Histogram,使用者可以使用这四种数据结构来实现多样化的指标定义和分析模式。

安装运行

Prometheus 采用 Golang 编译,不存在第三方依赖,可以直接运行;或者 Prometheus 官方也提供了 Docker 镜像。
官方提供了 node_exporter 用于采集主机的运行指标。运行后,可以通过配置 prometheus.ymlscrape_configs 来添加任务:

1
2
3
4
5
6
7
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets: ['localhost:9100']

重启后,查询 up 指标,可以看到 up{job="node"} 的值为 1,说明来自改端点的指标可以正常获取。

杂项

  • Prometheus 自带的 UI 界面功能比较简单,此时可以考虑使用第三方的可视化的工具如 Grafana.
  • 在 Prometheus 中,每个暴露监控样本数据的 HTTP 服务称为一个实例,一组出于相同采集目的的实例,可以通过一个任务(Job)进行分组管理。

第 2 章:探索 PromQL

第二章主要讲解了查询中所用到的数据类型;监控指标类型;以及基本的 PromQL 使用。

基本概念

  • 时间序列:一个序列,其中的每个值由一个时间戳和一个浮点数构成,表示某一时刻监控样本的具体值
  • 样本:时间序列的一个点,由三部分组成:
    • 指标(metric):指标名,标签组;
    • 时间戳(timestamp)
    • 样本值(value)
  • 指标名反映了监控样本的含义;标签组反映了样本的特征维度,可用于过滤或聚合。指标名其实也是标签,key 为 __name__

指标类型

Prometheus 为实例应用定义了四种不同的指标类型:Counter、Gauge、Summary 和 Histogram.

Counter 只增不减,Gauge 可加可减;Summary 和 Histogram 是组合指标,用于统计和分析样本的分布情况。

与Summary类型的指标相似之处在于Histogram类型的样本同样会反应当前指标的记录的总数(以_count作为后缀)以及其值的总量(以_sum作为后缀)。不同在于Histogram指标直接反应了在不同区间内样本的个数,区间通过标签len进行定义。
同时对于Histogram的指标,我们还可以通过histogram_quantile()函数计算出其值的分位数。不同在于Histogram通过histogram_quantile函数是在服务器端计算的分位数。 而Sumamry的分位数则是直接在客户端计算完成。因此对于分位数的计算而言,Summary在通过PromQL进行查询时有更好的性能表现,而Histogram则会消耗更多的资源。反之对于客户端而言Histogram消耗的资源更少。在选择这两种方式时用户应该按照自己的实际场景进行选择。

查询

查询方法

查询时,首先要提供指标名,然后根据 label 来筛选指标。PromQL 提供四种 label 筛选的方式:k=v, k!=v, k=~regex, k!~regex

查询时间范围:使用方括号将时间段括住,可以查询最近一段时间内的数据,如 http_requrest_total{}[5m]
时间偏移:使用 offset 将查询时间段向前推移如 http_requrest_total{}[5m] offset 1h

聚合筛选操作:用 by 可以对某些标签进行聚合,用 without 可以将某些标签排除,对剩下的标签进行聚合。如 sum(up) by (instance)

数据结构表示

每个实例的 metrics API 产出的数据结构只有一个由多个数据指标构成的瞬时向量;这里的数据结构为输出数据结构,用于查询时进行运算及输出。

  • 标量(Scalar):一个浮点型数据值
  • 字符串(String)
  • 时间序列(Time Series):一个序列,其中的每个点都由一个时间戳和一个数据值构成
  • 向量:由多个指标和其对应的时间序列构成
  • 瞬时向量(Instant Vector):一个向量,其中每个指标只包含一个点
  • 区间向量:使用区间向量表达式 [5m] 得到的结果,每个指标包含多个点;用于表示一段时间内的变化值

栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 瞬时向量
Query: checkin_rpc_request_time_count

checkin_rpc_request_time_count{endpoint="CreateFCL"} 1
checkin_rpc_request_time_count{endpoint="CreateUCL"} 3

# 区间向量
Query: checkin_rpc_request_time_count[2m]

checkin_rpc_request_time_count{endpoint="CreateFCL"} 1 @1541421409.712
1 @1541421469.712
checkin_rpc_request_time_count{endpoint="CreateUCL"} 3 @1541421437.904
3 @1541421497.904

运算

数学运算

PromQL 支持的数学运算符有 +, -, *, /, %, ^(幂运算)

当两个向量相加时,对应时间点的值将会相加并返回;若某点在另一向量中不存在,则该点会被丢弃;
当一个向量加一个标量时,会将向量中每个点的值加上这个标量并返回。

条件运算

PromQL 支持的条件运算符有 ==, !=, >, <, >=, <=

a > 2,会筛选出所有值大于 2 的点,不满足条件的点将会被丢弃
a > bool 2,会对所有点进行逻辑运算,得出结果 0 或 1 并返回,不会将点丢弃。

集合运算

PromQL 支持瞬时向量间的集合运算, and, or, unless 分别对应交集、并集和差集。

向量匹配模式

匹配模式用于在向量运算时,对左边的指标名进行标签匹配。
PromQL 有两种典型的匹配模式:一对一和一对多

  • 一对一用于两边表达式的标签一致的情况;
    若不一致,可以使用 onignoreing 筛选出一些标签再进行匹配
  • 一对多 / 多对一用于两边向量长度不一致的情况:
    修正标签后,可能一边的向量中只有一个值,而另一边有多个;
    此时,可以使用 group_leftgroup_right 来进行匹配,其中 group_left 表示左边的向量基数更大,也就是多对一;反之亦然。

聚合操作

PromQL 可以对瞬时向量中的多个指标进行聚合,生成另外一个瞬时向量。
支持的聚合函数太多,见文档
在聚合时,可以通过 by/without 语句选择根据/不根据那些标签进行聚合。有点像 SQL 里的 GROUP BY.

栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Query: request_time_count
request_time_count{endpoint='/', instance='1'} 1
request_time_count{endpoint='/', instance='2'} 1
request_time_count{endpoint='/', instance='3'} 1
request_time_count{endpoint='/index.html', instance='1'} 2
request_time_count{endpoint='/index.html', instance='2'} 3
request_time_count{endpoint='/index.html', instance='3'} 4

Query: sum(request_time_count)
sum(request_time_count) 12

Query: sum(request_time_count) by (instance)
request_time_count{instance='1'} 3
request_time_count{instance='2'} 4
request_time_count{instance='3'} 5

内置函数

更多了,见文档

rateirate 的区别:两个函数都能够表示区间向量中各个时间的变化率,不过 iraterate 具有更高的灵敏度。
所以,观察指标时可以用 irate,但设置报警规则时应该用 rate,以免一些瞬时变化产生误报。

监控指标的设计实践

Prometheus 鼓励大家设计多层指标,从多个维度监控到所有东西。
书中列举的一些常用的监控维度。

级别监控什么Exporter
网络网络协议:http、dns、tcp、icmp;网络硬件:路由器,交换机等BlockBox Exporter;SNMP Exporter
主机资源用量node exporter
容器资源用量cAdvisor
应用(包括Library)延迟,错误,QPS,内部状态等代码中集成Prmometheus Client
中间件状态资源用量,以及服务状态代码中集成Prmometheus Client
编排工具集群资源用量,调度等Kubernetes Components

除了监控的不同级别以外,还有一些常见的监控模式,用于从不同角度检测应用状态:

  • 四个黄金指标:延迟、通讯量、错误发生速率、饱和度
  • RED 方法:请求速率、请求错误(每秒失败的请求数)、请求耗时
  • USE 方法(用于甄别系统性能问题):使用率、饱和度、错误计数

第 3 章:Prometheus 告警处理

基本概念

Prometheus Server 负责存储告警规则并产生告警,而告警的具体处理方式(如发邮件通知用户)则交由 Alertmanager 来做。

Alertmanage 作为一个独立的组件,负责接收并处理来自 Prometheus Server(也可以是其它的客户端程序)的告警信息。Alertmanager 可以对这些告警信息进行进一步的处理,比如消除重复的告警信息对告警信息进行分组并且路由到正确的接受方。Prometheus 内置了对邮件,Slack 等通知方式的支持,同时还支持与 Webhook 的通知集成,以支持更多的可能性,例如可以通过 Webhook 与钉钉或者企业微信进行集成。同时 AlertManager 还提供了静默和告警抑制机制来对告警通知行为进行优化。

告警规则定义

在 Prometheus 配置文件中通过 rule_files 指定一组告警规则文件的访问路径。
设置规则后,Prometheus 会根据 global.evaluation_interval 定义的时间周期计算报警规则中定义的 PromQL 表达式。如果表达式能够找到匹配的时间序列,则会对每条序列产生一个告警实例。
Prometheus 告警配置规则如下:

1
2
3
4
5
6
7
8
9
10
11
groups:
- name: example
rules:
- alert: HighErrorRate
expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
for: 1m
labels:
severity: page
annotations:
summary: High request latency for {{ $labels.job }}:{{ $value }}
description: description info

其中,annotations 定义的标注部分内容可以进行模板化: 可以访问告警实例中标签的值,而 可以访问表达式算出的样本值。

假设 Prom Server 每 10s 计算一次,则当第一个满足条件的报警出现时,Prom Server 会产生一条报警,但会处于 PENDING 状态,而不会触发;在经过由 for 指定的一段时间后,如果报警条件依然满足,则报警状态变为 FIRING,触发报警。

Alertmanager 使用方法

部署

和 Prometheus Server 一样,Alertmanager 也是单文件可执行程序,也有官方提供的 Docker 镜像。
Alertmanager 的配置主要包含两部分:路由(route)和接收器(Receiver)。所有的告警信息都会从配置中的顶级路由进入路由树,根据路由规则将告警信息发送给相应的接收器。
配置成功后,在 Prom Server 的配置中添加内容,将 Prom Server 和 Alertmanager 关联起来。

1
2
3
4
alerting:
alertmanagers:
- static_configs:
targets: ['localhost:9093']

配置路由

Alertmanager 的路由为树状结构。
在匹配规则时,可以通警告名称(alertname)和报警标签(labelname)来进行匹配,匹配方式支持完全匹配和正则匹配。
如果 continue 的值为 true,则会将报警交由下一层节点,否则将会直接在当前节点进行处理。
路由配置格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[ receiver: <string> ]
[ continue: <boolean> | default = false ]

match:
[ <labelname>: <labelvalue>, ... ]

match_re:
[ <labelname>: <regex>, ... ]

# 分组规则,如果多条报警的的某些标签值相等,则这些报警将会被归为一组
[ group_by: '[' <labelname>, ... ']' ]
[ group_wait: <duration> | default = 30s ]
[ group_interval: <duration> | default = 5m ]
[ repeat_interval: <duration> | default = 4h ]

routes: # 路由树的子节点
[ - <route> ... ]

配置接收器

Alertmanager 默认支持多种接收器,如邮件,Slack 等,具体看文档,同时也有多种基于 Webhook 的继承方式(如 Telegram Bot 和钉钉机器人),具体看文档,如果想自己制作 Webhook 接收器,只需要了解 Webhook 格式即可。。

配置报警具体消息时,可以使用 Go 模板来进行自定义。

配置抑制 / 静默机制

抑制机制的作用:用户在收到一条报警通知后,可以通过规则来屏蔽掉后续的其它报警,以免收到过多垃圾信息,影响问题分析。
栗子:如果集群炸了,那我们只需要收到“集群爆炸了”的报警消息就足够了,后面的一万个“A 服务不可用”“B 服务响应延迟 xxx 秒”的报警都不需要再报出来了。
抑制规则是长期规则,需要在 Alertmanager 配置文件中进行配置。

如果只想临时关掉某些报警,管理员可以通过 Alertmanager 的 UI 来临时屏蔽满足规则的报警通知。
Alertmanager 的 UI 中可以创建静默规则,管理员可以配置报警匹配规则以及静默时间。

杂项

如果某些语句 / 报警规则的计算成本很高,那现场计算可能会导致 Prometheus 响应超时。此时可以通过配置 Recording Rules 来在后台提前对结果进行运算。
这个功能有点像数据库索引…

第 4 章:使用 Exporter

基本概念

广义上讲,所有可以向 Prometheus 提供监控样本数据的程序都可以称作做 Exporter. 一个运行 Exporter 的实例称为一个 Target.
Prometheus 社区提供了非常丰富的 Exporter 实现,涵盖了从基础设施,中间件以及网络等各个方面的监控功能。

Exporter 应通过 HTTP API 为 Prometheus Server 提供符合格式规范的内容。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# HELP HTTP Response amount
# TYPE http_res_amount counter
http_res_amount{code="200"} 123
http_res_amount{code="400"} 12

# HELP Example Histogram
# TYPE example_histogram histogram
example_histogram_bucket{le="0.1"} 1
example_histogram_bucket{le="+inf"} 2
example_histogram_sum 5
example_histogram_count 3

# HELP Example Summary
# TYPE example_sumary sumary
example_sumary{quantile="0.1"} 1
example_sumary{quantile="0.5"} 2
example_sumary_sum 5
example_sumary_count 3

Exporter 举例

书中详细提到了三个 Exporter,分别是监控容器状态的 cAdvidor、监控 MySQL 运行状态的 MySQLD Exporter 和监控网络状态的 Blackbox Exporter.

这里不详细展开讲,可以直接看文档看花眼

集成 Exporter

除了单独运行的 Exporter 程序外,我们还可以在自己的应用中集成一个 Exporter.
Prometheus 官方和社区为多种语言提供了集成支持,使用这些库,则可以方便地在应用中提供 HTTP API,并在程序运行过程中进行指标收集。

第 5 章:可视化一切

Prometheus 提供了一个 Console Template,可以通过 Go 模板来配置任意控制台界面。
不过,这种配置方法非常麻烦,所以我们更推荐使用 Grafana 来创建美观的 Dashboard.

Grafana 设置

基本概念:

  • 数据源(Data Source)
  • 仪表盘(Dashboard)
  • 行(Row)
  • 面板(Panel)

创建 Dashboard

Grafana 支持多种 Panel,我常用的有 Singlestat 和 Graph.

Singlestat 用于展示当前状态,如 CPU 使用率等,而 Graph 用于展示指标随时间的变化。
Graph 创建时,如果设置的查询条件返回了多个指标,则可以画出多个图表,这一点非常棒。

第 6 章:集群与高可用

文件存储

使用本地存储:Prometheus 将所有数据按照时间范围分片存储,按照两小时为一个时间窗口,每两小时的数据存在一个块中,每个块中包含该时间窗口内的所有样本数据、元数据文件以及索引文件。
通过时间窗口的形式保存数据,有利于 Prometheus 根据特定时间段进行数据查询。

使用远程存储(Remote Storage):为了满足持久化需求,Prometheus 可以使用外部存储方式存储样本数据。但 Prometheus 没有内置存储适配工具,而是提供两个标准接口,让用户通过这两个接口将数据保存到任意存储服务中。
两个标准接口为 remote_readremote_write,基于 HTTP 协议,信息传递依赖 protobuf.
同时,Prometheus 社区也提供了部分对于第三方数据库的 Remote Storage 支持。具体可见文档

Prometheus 部署架构及高可用方案

架构

联邦集群:
如果样本来自多个数据中心,则可以在每个数据中心部署单独的 Prometheus Server,然后使用一个中心 Prom Server 从其它 Server 中通过 /federate 接口获取数据。
数据格式符合标准样本格式,所以对于中心 Server 来说,从其它 Server 获取数据和从 Exporter 获取数据实际上没有任何差异。

功能分区:
如果单个集群中需要采集的数据过多,可以在集群中部署多个 Prom Server 实例,然后将不同的监控任务交给不同 Prom Server 处理,再由中心 Server 进行聚合。

Prometheus 高可用部署

  • 基本 HA:部署多个 Server,同时采集相同的 Exporter 目标;
  • 基本 HA + 远程存储:使用远程存储确保数据持久化,若 Server 发生宕机,可以快速恢复数据;
  • 基本 HA + 远程存储 + 联邦集群:添加联邦集群,在任务级别设置功能分区,将不同的采集任务划分至不同的 Server;
    适合大量采集任务,或多数据中心的场景;
  • 按照实例进行功能分区:通过 relabel 设置对所有实例进行 hash,并平均分至多个 Server 进行采集。

高可用方案套餐搭配:

  • 服务可用性:主备 HA
  • 数据持久化:远程存储
  • 水平扩展:联邦集群

Alertmanager 高可用部署

在 Prometheus Server 配置中,可以添加多个 Alertmanager 实例,在报警发生时,Server 会同时通知多个 Alertmanager.
而 Alertmanager 的 Gossip 机制可以保证同一个报警消息不会被重复发送。
这种分布式方案满足了 CAP 理论中的 AP,但不提供数据强一致性保证。

通知流水线:

  1. 判断当前通知是否匹配静默规则;
  2. 根据 Alertmanager 在集群中的顺序等待一段时间,一般为 (index * 5)s
  3. 判断当前消息是否被发送;若已发送则终止流水线;
  4. 发送消息;
  5. 使用 Gossip 机制通知集群中其它实例。

高可用通知流程

第 7 章:Prometheus 服务发现

基础概念

Prometheus 的服务发现原理:集群中存在一个服务注册中心(如 Consul 或 Etcd)保存着所有实例。Prometheus 定时从服务注册中心中获取服务列表,然后依据这个列表去抓取样本。

Prometheus 的服务发现方式

通过 Prometheus Server,可以设置多种服务发现方式,如本地文件服务发现、DNS 服务发现、Kubernetes 服务发现等。
具体可以参考官方配置文档<*_sd_config> 部分。

Relabeling 机制

在 Prometheus 所有 Target 实例中,会包含一些以双下划线开头的 Metadata 标签信息,比如 __address___metrics_path__ 等。
Relabeling 机制可以将这些标签加以利用,如进行标签过滤、改变标签名称以用于查询,或对标签值进行 Hash 以完成功能分区等。

具体可以看文档

第 8 章:监控 Kubernetes

Kuberenetes 监控指标

监控 Kubernetes 平台及应用时,在白盒层面需要关注:

  • 基础设施层(Node)
  • 容器基础设施(Container)
  • 用户应用(Pod)
  • Kubernetes 组件

在黑盒层面需要关注:

  • 内部服务负载均衡(Service)
  • 外部访问入口(Ingress)

在集群中部署 Prometheus 监控

核心组件部署

部署 Prometheus Server 时需要使用 ConfigMap 定义配置文件,并使用 Deployment 进行部署。
Prometheus 在 k8s 集群内的服务发现方式是通过与 Kubernetes API 集成,目前支持 5 种服务发现模式,分别是 Node, Service, Pod, Endpoint, Ingress.

此外,CoreOS 提供了 Prometheus Operator,可以更加便捷的在 k8s 集群中部署及配置 Prometheus.

Exporter 部署

  • Kubernetes 内置了 cAdvisor 的支持,所以 Prometheus 可以直接通过 cAdvisor 获取容器指标;
  • 可以通过 DaemonSet 部署 NodeExporter 以获取每个 Node 的运行状态,注意不是 Deployment
  • 可以通过部署 Blackbox Exporter 来监控 Service 和 Ingress

自定义 Pod 指标抓取

在用户部署 Pod / Deployment / DaemonSet 时,如果需要 Prometheus 进行样本抓取,需要在 Pod Template 的 metadata.annotations 里添加如下内容:

1
2
3
prometheus.io/scrape: 'true'
prometheus.io/port: '9100'
prometheus.io/path: 'metrics'

为了支持 Pod 内指标获取,在配置 Prometheus 时,需要进行 Relabel 配置,以过滤掉不支持样本获取的 Pod:

1
2
3
- source_lables: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true

这次根据整理的内容制作了一份思维导图,可以在这里查看。