0%

Prometheus

Prometheus是一套开源的监控、报警、时间序列数据库的组合,起始是由SoundCloud公司开发的。从2016年加入CNCF,2016年6月正式发布1.0版本,2017年底发布了基于全新存储层的2.0版本,能更好地与容器平台、云平台配合,到2018年8月毕业,现在已经成为Kubernetes的官方监控方案,社区活跃,第三方集成非常丰富。

特点

Prometheus是一个开源的完整监控解决方案,其对传统监控系统的测试和告警模型进行了彻底的颠覆,形成了基于中央化的规则计算、统一分析和告警的新模型。 相比于传统监控系统Prometheus具有以下优点:

  • 易于管理:只有一个单独的二进制文件,不存在任何的第三方依赖,采用Pull的方式拉取数据
  • 强大的数据模型:每一条时间序列由指标名称(Metrics Name)以及一组标签(Labels)唯一标识
  • 强大的查询语言PromQL:内置了一个强大的数据查询语言PromQL,可以实现多种查询、聚合
  • 高性能:单实例可以处理数以百万的监控指标、每秒处理数十万的数据点
  • 易扩展:支持sharding和联邦集群,实现多数据中心
  • 易集成:支持多种语言的SDK进行应用程序数据埋点,社区有丰富插件
  • 可视化:自带Prometheus UI,可以进行查询与展示,Grafana也完整支持Prometheus。
  • 开放性:使用sdk采集的数据可以被其他监控系统使用,不一定非要用Prometheus

系统架构

Prometheus从exporter拉取数据,或者间接地通过网关gateway拉取数据(如果在k8s内部署,可以使用服务发现的方式),它默认本地存储抓取的所有数据,并通过一定规则进行清理和整理数据,并把得到的结果存储到新的时间序列中,采集到的数据有两个去向,一个是报警,另一个是可视化。PromQL和其他API可视化地展示收集的数据,并通过Alertmanager提供报警能力。

组件内容

  • Prometheus Server 负责从 Exporter 拉取和存储监控数据,并提供一套灵活的查询语言(PromQL)

    • Retrieval: 采样模块
    • TSDB: 存储模块默认本地存储为tsdb
    • HTTP Server: 提供http接口查询和面板,默认端口为9090
  • Exporters/Jobs 负责收集目标对象(host, container…)的性能数据,并通过 HTTP 接口供 Prometheus Server 获取。支持数据库、硬件、消息中间件、存储系统、http服务器、jmx等。只要符合接口格式,就可以被采集。

  • Short-lived jobs 瞬时任务的场景,无法通过pull方式拉取,需要使用push方式,与PushGateway搭配使用

  • PushGateway 可选组件,主要用于短期的 jobs。由于这类 jobs 存在时间较短,可能在 Prometheus 来 pull 之前就消失了。为此,这次 jobs 可以直接向 Prometheus server 端推送它们的 metrics。这种方式主要用于服务层面的 metrics,对于机器层面的 metrices,需要使用 node exporter。

  • 客户端sdk 官方提供的客户端类库有go、java、scala、python、ruby,其他还有很多第三方开发的类库,支持nodejs、php、erlang等

  • Alertmanager 从 Prometheus server 端接收到 alerts 后,会进行去除重复数据,分组,并路由到对收的接受方式,发出报警。常见的接收方式有:电子邮件,pagerduty,OpsGenie, webhook 等。

  • Service Discovery

    服务发现,Prometheus支持多种服务发现机制:文件,DNS,Consul,Kubernetes,OpenStack,EC2等等。基于服务发现的过程并不复杂,通过第三方提供的接口,Prometheus查询到需要监控的Target列表,然后轮训这些Target获取监控数据。

其大概的工作流程是:

  • Prometheus server 定期从配置好的 jobs 或者 exporters 中拉 metrics,或者从 Pushgateway 拉取 metrics,或者从其他的 Prometheus server 中拉 metrics。
  • Prometheus server 在本地存储收集到的 metrics,并运行已定义好的 alert.rules,记录新的时间序列或者向 Alertmanager 推送警报。
  • Alertmanager 根据配置文件,对接收到的警报进行处理,发出告警。
  • 在图形界面中,可视化采集数据。

Push与Pull

Prometheus采集数据是用的pull也就是拉模型,通过HTTP协议去采集指标,只要应用系统能够提供HTTP接口就可以接入监控系统,相比于私有协议或二进制协议来说开发、简单。优点主要是:

  • 开发任何新功能,你甚至可以在电脑上查看你的监控
  • 如果目标实例挂掉,你可以很快知道
  • 你可以手动指定目标实例,并且在浏览器中查看他的健康状态

总体来说,Pull模式比Push模式更好一些,在监控系统中这也不是一个很重要的点。 如果要使用push的方式,可以使用Pushgateway的方式,如定时任务的采集。

对于定时任务这种短周期的指标采集,如果采用pull模式,可能造成任务结束了,Prometheus还没有来得及采集,这个时候可以使用加一个中转层,客户端推数据到Push Gateway缓存一下,由Prometheus从push gateway pull指标过来。(需要额外搭建Push Gateway,同时需要新增job去从gateway采数据)

推的代表有 ElasticSearch,InfluxDB,OpenTSDB 等,需要你从程序中将指标使用 TCP,UDP 等方式推送至相关监控应用,只是使用 TCP 的话,一旦监控应用挂掉或存在瓶颈,容易对应用本身产生影响,而使用 UDP 的话,虽然不用担心监控应用,但是容易丢数据。

拉的代表,主要代表就是 Prometheus,让我们不用担心监控应用本身的状态。而且,可以利用 DNS-SRV 或者 Consul 等服务发现功能就可以自动添加监控。

当然,InfluxDB 加上 collector,或者 ES 加上 metricbeat 也可以变为 『拉』,而 Prometheus 加上 Push Gateway 也可以变为 『推』。

更多区别可以参考下图:

img

存储机制

Prometheus有着非常高效的时间序列数据存储方法,每个采样数据仅仅占用3.5byte左右空间,上百万条时间序列,30秒间隔,保留60天,大概花了200多G(引用官方PPT)。

Prometheus内部主要分为三大块,Retrieval是负责定时去暴露的目标页面上去抓取采样指标数据,Storage是负责将采样数据写磁盘,PromQL是Prometheus提供的查询语言模块。

Prometheus内置了一个基于本地存储的时间序列数据库。在Prometheus设计上,使用本地存储可以降低Prometheus部署和管理的复杂度同时减少高可用(HA)带来的复杂性。 在默认情况下,用户只需要部署多套Prometheus,采集相同的Targets即可实现基本的HA。同时由于Promethus高效的数据处理能力,单个Prometheus Server基本上能够应对大部分用户监控规模的需求。

同时为了适应数据持久化的问题,Prometheus提供了remote_write和remote_read的特性,支持将数据存储到远端和从远端读取数据。通过将监控与数据分离,Prometheus能够更好地进行弹性扩展。

部署使用

ConfigMap

当使用Deployment管理和部署应用程序时,用户可以方便了对应用进行扩容或者缩容,从而产生多个Pod实例。为了能够统一管理这些Pod的配置信息,在Kubernetes中可以使用ConfigMaps资源定义和管理这些配置,并且通过环境变量或者文件系统挂载的方式让容器使用这些配置。

这里将使用ConfigMaps管理Prometheus的配置文件,创建prometheus-config.yml文件,并写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']

Deployment

当ConfigMap资源创建成功后,我们就可以通过Volume挂载的方式,将Prometheus的配置文件挂载到容器中。 这里我们通过Deployment部署Prometheus Server实例,创建prometheus-deployment.yml文件,并写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
name: prometheus
name: prometheus
namespace: monitoring
spec:
replicas: 1
template:
metadata:
labels:
app: prometheus
spec:
containers:
- name: prometheus
image: prom/prometheus:v2.19.0
command:
- /bin/prometheus
args:
- --config.file=/etc/config/prometheus.yml
ports:
- containerPort: 9090
protocol: TCP
volumeMounts:
- mountPath: /etc/config
name: prometheus-config
volumes:
- name: prometheus-config
configMap:
name: prometheus-config

Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: monitoring
labels:
name: prometheus
spec:
ports:
- name: prometheus
protocol: TCP
port: 9090
targetPort: 9090
selector:
app: prometheus

Ingress

由于之前配置了 Traefik,所以这里的 Prometheus 服务不使用 NodePort 方式暴露,在本地设置好 prometheus.houmin 的 host之后,在浏览器中访问:http://prometheus.houmin:<TraefikPort>/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: prometheus
namespace: monitoring
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: prometheus.houmin
http:
paths:
- path: /
backend:
serviceName: prometheus
servicePort: prometheus

PromQL查询

Prometheus除了存储数据外,还提供了一种强大的功能表达式语言 PromQL,允许用户实时选择和汇聚时间序列数据。

表达式的结果可以在浏览器中显示为图形,也可以显示为表格数据,或者由外部系统通过 HTTP API 调用。通过PromQL用户可以非常方便地查询监控数据,或者利用表达式进行告警配置。比如集群中网络使用:

1
sum by (name) (rate(container_network_receive_bytes_total{image!=""}[1m]))

Metric类型

关于时间序列存储,可以参考:https://www.infoq.cn/article/database-timestamp-01

Prometheus会将所有采集到的样本数据以时间序列(time-series)的方式保存在内存数据库TSDB中,并且定时保存到硬盘上。time-series是按照时间戳和值的序列顺序存放的,我们称之为向量(vector)。每条time-series通过指标名称(metrics name)和一组标签集(labelset)命名。

在time-series中的每一个点称为一个样本(sample),样本由以下三部分组成:

  • 指标(metric):metric name和描述当前样本特征的labelsets;
  • 时间戳(timestamp):一个精确到毫秒的时间戳;
  • 样本值(value): 一个folat64的浮点型数据表示当前样本的值。

如某一时刻的node_cpu指标为459.71

1
node_cpu{app="node-exporter",cpu="cpu0",instance="192.168.0.4:9100",job="kubernetes-service-endpoints",kubernetes_name="node-exporter",kubernetes_namespace="kube-system",mode="guest"}     459.71

Prometheus定义了4中不同的指标类型(metric type):

  • Counter 计数器
1
2
3
4
计数器,只增不减,如http_requests_total请求总数

例如,通过rate()函数获取HTTP请求量的增长率:
rate(http_requests_total[5m])
  • Gauge 仪表盘
1
2
3
当前状态,可增可减。如kube_pod_status_ready当前pod可用数
可以获取样本在一段时间返回内的变化情况,如:
delta(kube_pod_status_ready[2h])
  • Histogram 直方图
1
2
3
Histogram 由 <basename>_bucket{le="<upper inclusive bound>"},<basename>_bucket{le="+Inf"}, <basename>_sum,<basename>_count 组成,主要用于表示一段时间范围内对数据进行采样(通常是请求持续时间或响应大小),并能够对其指定区间以及总数进行统计,通常它采集的数据展示为直方图。

例如 Prometheus server 中 prometheus_local_storage_series_chunks_persisted, 表示 Prometheus 中每个时序需要存储的 chunks 数量,我们可以用它计算待持久化的数据的分位数。
  • Summary 摘要
1
2
3
4
5
Summary 和 Histogram 类似,由 <basename>{quantile="<φ>"},<basename>_sum,<basename>_count 组成,主要用于表示一段时间内数据采样结果(通常是请求持续时间或响应大小),它直接存储了 quantile 数据,而不是根据统计区间计算出来的。

例如 Prometheus server 中 prometheus_target_interval_length_seconds。

Histogram 需要通过 <basename>_bucket 计算 quantile, 而 Summary 直接存储了 quantile 的值。

基础查询

PromQL是Prometheus内置的数据查询语言,其提供对时间序列数据丰富的查询,聚合以及逻辑运算能力的支持。如http_requests_total指标,你可以通过附加一组标签,并用{}括起来,来进一步筛选这些时间序列。下面这个例子只选择有http_requests_total名称的、有prometheus工作标签的、有canary组标签的时间序列:

1
http_requests_total{job="prometheus",group="canary"}

如果条件为空,可以写为:http_requests_total{}

另外,也可以也可以将标签值反向匹配,或者对正则表达式匹配标签值。如操作符:

1
2
3
4
=:选择正好相等的字符串标签
!=:选择不相等的字符串标签
=~:选择匹配正则表达式的标签(或子标签)
!=:选择不匹配正则表达式的标签(或子标签)

范围查询

类似http_requests_total{job="prometheus",group="canary"}的方式,得到的是瞬时值,如果想得到一定范围内的值,可以使用范围查询。

时间范围通过时间范围选择器[]进行定义。例如,通过以下表达式可以选择最近5分钟内的所有样本数据,如:http_request_total{}[5m]。除了分钟,支持的单位有:

  • s - 秒
  • m - 分钟
  • h - 小时
  • d - 天
  • w - 周
  • y - 年

偏移查询

如:查询http_requests_total在当前时刻的一周的速率:

1
rate(http_requests_total{} offset 1w)

偏移修饰符允许更改查询中单个即时向量和范围向量的时间偏移量,例如,以下表达式返回相对于当前查询时间5分钟前的http_requests_total值:

1
http_requests_total offset 5m

等价于

1
http_requests_total{job="prometheus"}[5m]

请注意,偏移量修饰符始终需要跟随选择器,即以下是正确的:

1
sum(http_requests_total{method="GET"} offset 5m) // GOOD.

下面是错误的:

1
sum(http_requests_total{method="GET"}) offset 5m // INVALID.

操作符

Prometheus 的查询语言支持基本的逻辑运算和算术运算

二元算术运算:

  • +加法
  • -减法
  • *乘法
  • / 除法
  • % 模
  • ^ 幂等

运算中用到的基础数据类型:

  • 瞬时向量(Instant vector) - 一组时间序列,每个时间序列包含单个样本,它们共享相同的时间戳。也就是说,表达式的返回值中只会包含该时间序列中的最新的一个样本值。而相应的这样的表达式称之为瞬时向量表达式。
  • 区间向量(Range vector) - 一组时间序列,每个时间序列包含一段时间范围内的样本数据。
  • 标量(Scalar) - 一个浮点型的数据值。
  • 字符串(String) - 一个简单的字符串值。

二元运算操作符支持 scalar/scalar(标量/标量)、vector/scalar(向量/标量)、和 vector/vector(向量/向量) 之间的操作。

在两个标量之间进行数学运算,得到的结果也是标量。

例如,如果我们想根据 node_disk_bytes_written 和 node_disk_bytes_read 获取主机磁盘IO的总量,可以使用如下表达式:

1
node_disk_bytes_written + node_disk_bytes_read

或者node的内存数GB

1
node_memory_free_bytes_total / (1024 * 1024)

布尔运算

  • == (相等)
  • != (不相等)
  • > (大于)
  • < (小于)
  • >= (大于等于)
  • <= (小于等于)

如:获取http_requests_total请求总数是否超过10000,返回0和1,1则报警

1
2
http_requests_total > 10000 # 结果为 truefalse
http_requests_total > bool 10000 # 结果为 10

集合运算

  • and (并且)
  • or (或者)
  • unless (排除)

优先级

四则运算有优先级,promql的复杂运算也有优先级

例如,查询主机的CPU使用率,可以使用表达式:

1
100 * (1 - avg (irate(node_cpu{mode='idle'}[5m])) by(job) )

其中irate是PromQL中的内置函数,用于计算区间向量中时间序列每秒的即时增长率 在PromQL操作符中优先级由高到低依次为:

  1. ^
  2. *, /, %
  3. +, -
  4. ==, !=, <=, <, >=, >
  5. and, unless
  6. or

匹配模式(联合查询)

与数据库中的join类似,promsql有两种典型的匹配查询:

  • 一对一(one-to-one)
  • 多对一(many-to-one)或一对多(one-to-many)

例如当存在样本:

1
2
3
4
5
6
7
8
9
method_code:http_errors:rate5m{method="get", code="500"}  24
method_code:http_errors:rate5m{method="get", code="404"} 30
method_code:http_errors:rate5m{method="put", code="501"} 3
method_code:http_errors:rate5m{method="post", code="500"} 6
method_code:http_errors:rate5m{method="post", code="404"} 21

method:http_requests:rate5m{method="get"} 600
method:http_requests:rate5m{method="del"} 34
method:http_requests:rate5m{method="post"} 120

使用 PromQL 表达式:

1
method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m

该表达式会返回在过去 5 分钟内,HTTP 请求状态码为 500 的在所有请求中的比例。如果没有使用 ignoring(code),操作符两边表达式返回的瞬时向量中将找不到任何一个标签完全相同的匹配项。

因此结果如下:

{method=”get”} 0.04 // 24 / 600 {method=”post”} 0.05 // 6 / 120

同时由于 method 为 put 和 del 的样本找不到匹配项,因此不会出现在结果当中。

多对一模式

例如,使用表达式:

1
method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m

该表达式中,左向量 method_code:http_errors:rate5m 包含两个标签 method 和 code。而右向量 method:http_requests:rate5m 中只包含一个标签 method,因此匹配时需要使用 ignoring 限定匹配的标签为 code。

在限定匹配标签后,右向量中的元素可能匹配到多个左向量中的元素 因此该表达式的匹配模式为多对一,需要使用 group 修饰符 group_left 指定左向量具有更好的基数。

最终的运算结果如下:

1
{method="get", code="500"} 0.04 // 24 / 600 {method="get", code="404"} 0.05 // 30 / 600 {method="post", code="500"} 0.05 // 6 / 120 {method="post", code="404"} 0.175 // 21 / 120
1
提醒:group 修饰符只能在比较和数学运算符中使用。在逻辑运算 and,unless 和 or 操作中默认与右向量中的所有元素进行匹配。

聚合操作

Prometheus 还提供了下列内置的聚合操作符,这些操作符作用域瞬时向量。可以将瞬时表达式返回的样本数据进行聚合,形成一个具有较少样本值的新的时间序列。

  • sum (求和)
  • min (最小值)
  • max (最大值)
  • avg (平均值)
  • stddev (标准差)
  • stdvar (标准差异)
  • count (计数)
  • count_values (对 value 进行计数)
  • bottomk (样本值最小的 k 个元素)
  • topk (样本值最大的k个元素)
  • quantile (分布统计)

这些操作符被用于聚合所有标签维度,或者通过 without 或者 by 子语句来保留不同的维度。

  • without 用于从计算结果中移除列举的标签,而保留其它标签。
  • by 则正好相反,结果向量中只保留列出的标签,其余标签则移除。

通过 without 和 by 可以按照样本的问题对数据进行聚合。

例如:如果指标 http_requests_total 的时间序列的标签集为 application, instance, 和 group,我们可以通过以下方式计算所有 instance 中每个 application 和 group 的请求总量:

1
sum(http_requests_total) without (instance)

等价于

1
sum(http_requests_total) by (application, group)

如果只需要计算整个应用的 HTTP 请求总量,可以直接使用表达式:

1
sum(http_requests_total)

count_values 用于时间序列中每一个样本值出现的次数。count_values 会为每一个唯一的样本值输出一个时间序列,并且每一个时间序列包含一个额外的标签。

这个标签的名字由聚合参数指定,同时这个标签值是唯一的样本值。

例如要计算运行每个构建版本的二进制文件的数量:

1
2
3
4
5
6
count_values("version", build_version)
返回结果如下:

{count="641"} 1
{count="3226"} 2
{count="644"} 4

topk 和 bottomk

则用于对样本值进行排序,返回当前样本值前 n 位,或者后 n 位的时间序列。

获取 HTTP 请求数前 5 位的时序样本数据,可以使用表达式:

1
topk(5, http_requests_total)

quantile 用于计算当前样本数据值的分布情况 quantile(φ, express) ,其中 0 ≤ φ ≤ 1

1
2
3
4
5
6
7
8
例如,当 φ 为 0.5 时,即表示找到当前样本数据中的中位数:


quantile(0.5, http_requests_total)
返回结果如下:


{} 656

内置函数

Prometheus 提供了其它大量的内置函数,可以对时序数据进行丰富的处理。如上文提到的irate

1
100 * (1 - avg (irate(node_cpu{mode='idle'}[5m])) by(job) )

常用的有:

两分钟内的平均CPU使用率:

1
rate(node_cpu[2m])

1
irate(node_cpu[2m])
1
2
3
4
5
6
7
8
9
10
11
需要注意的是使用rate或者increase函数去计算样本的平均增长速率,容易陷入“长尾问题”当中,
其无法反应在时间窗口内样本数据的突发变化。
例如,对于主机而言在2分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致CPU占用100%的情况,
但是通过计算在时间窗口内的平均增长率却无法反应出该问题。

为了解决该问题,PromQL提供了另外一个灵敏度更高的函数irate(v range-vector)。

irate同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。

irate函数是通过区间向量中最后两个两本数据来计算区间向量的增长速率。
这种方式可以避免在时间窗口范围内的“长尾问题”,并且体现出更好的灵敏度,通过irate函数绘制的图标能够更好的反应样本数据的瞬时变化状态。

irate函数相比于rate函数提供了更高的灵敏度,不过当需要分析长期趋势或者在告警规则中,irate的这种灵敏度反而容易造成干扰。

因此在长期趋势分析或者告警中更推荐使用rate函数。

完整的函数列表为:

  • abs()
  • absent()
  • ceil()
  • changes()
  • clamp_max()
  • clamp_min()
  • day_of_month()
  • day_of_week()
  • days_in_month()
  • delta()
  • deriv()
  • exp()
  • floor()
  • histogram_quantile()
  • holt_winters()
  • hour()
  • idelta()
  • increase()
  • irate()
  • label_join()
  • label_replace()
  • ln()
  • log2()
  • log10()
  • minute()
  • month()
  • predict_linear()
  • rate()
  • resets()
  • round()
  • scalar()
  • sort()
  • sort_desc()
  • sqrt()
  • time()
  • timestamp()
  • vector()
  • year()
  • _over_time()

API访问

Prometheus当前稳定的HTTP API可以通过/api/v1访问

错误状态码:

  • 404 Bad Request:当参数错误或者缺失时。
  • 422 Unprocessable Entity 当表达式无法执行时。
  • 503 Service Unavailiable 当请求超时或者被中断时。

所有的API请求均使用以下的JSON格式:

1
2
3
4
5
6
7
8
9
{
"status": "success" | "error",
"data": <data>,


// 为error时,有如下报错信息
"errorType": "<string>",
"error": "<string>"
}

通过HTTP API我们可以分别通过/api/v1/query和/api/v1/query_range查询PromQL表达式当前或者一定时间范围内的计算结果。

瞬时数据查询

URL请求参数:

  • query=:PromQL表达式。
  • time=:用于指定用于计算PromQL的时间戳。可选参数,默认情况下使用当前系统时间。
  • timeout=:超时设置。可选参数,默认情况下使用-query,timeout的全局设置。
1
$ curl 'http://localhost:9090/api/v1/query?query=up&time=2015-07-01T20:10:51.781Z'

返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"status" : "success",
"data" : {
"resultType" : "vector",
"result" : [
{
"metric" : {
"__name__" : "up",
"job" : "prometheus",
"instance" : "localhost:9090"
},
"value": [ 1435781451.781, "1" ]
},
{
"metric" : {
"__name__" : "up",
"job" : "node",
"instance" : "localhost:9100"
},
"value" : [ 1435781451.781, "0" ]
}
]
}
}

区间查询

URL请求参数:

  • query=: PromQL表达式。
  • start=: 起始时间。
  • end=: 结束时间。
  • step=: 查询步长。
  • timeout=: 超时设置。可选参数,默认情况下使用-query,timeout的全局设置。
1
$ curl 'http://localhost:9090/api/v1/query_range?query=up&start=2015-07-01T20:10:30.781Z&end=2015-07-01T20:11:00.781Z&step=15s'

返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
"status" : "success",
"data" : {
"resultType" : "matrix",
"result" : [
{
"metric" : {
"__name__" : "up",
"job" : "prometheus",
"instance" : "localhost:9090"
},
"values" : [
[ 1435781430.781, "1" ],
[ 1435781445.781, "1" ],
[ 1435781460.781, "1" ]
]
},
{
"metric" : {
"__name__" : "up",
"job" : "node",
"instance" : "localhost:9091"
},
"values" : [
[ 1435781430.781, "0" ],
[ 1435781445.781, "0" ],
[ 1435781460.781, "1" ]
]
}
]
}
}

Alert Rules

Prometheus中的告警规则允许你基于PromQL表达式定义告警触发条件,Prometheus后端对这些触发规则进行周期性计算,当满足触发条件后则会触发告警通知。默认情况下,用户可以通过Prometheus的Web界面查看这些告警规则以及告警的触发状态。当Promthues与Alertmanager关联之后,可以将告警发送到外部服务如Alertmanager中并通过Alertmanager可以对这些告警进行进一步的处理。

定义告警规则

一条典型的告警规则如下所示:

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: 10m
labels:
severity: page
annotations:
summary: High request latency
description: description info

在告警规则文件中,我们可以将一组相关的规则设置定义在一个group下。在每一个group中我们可以定义多个告警规则(rule)。一条告警规则主要由以下几部分组成:

  • alert:告警规则的名称。
  • expr:基于PromQL表达式告警触发条件,用于计算是否有时间序列满足该条件。
  • for:评估等待时间,可选参数。用于表示只有当触发条件持续一段时间后才发送告警。在等待期间新产生告警的状态为pending。
  • labels:自定义标签,允许用户指定要附加到告警上的一组附加标签。
  • annotations:用于指定一组附加信息,比如用于描述告警详细信息的文字等,annotations的内容在告警产生时会一同作为参数发送到Alertmanager。

为了能够让Prometheus能够启用定义的告警规则,我们需要在Prometheus全局配置文件中通过rule_files指定一组告警规则文件的访问路径,Prometheus启动后会自动扫描这些路径下规则文件中定义的内容,并且根据这些规则计算是否向外部发送通知:

1
2
rule_files:
[ - <filepath_glob> ... ]

默认情况下Prometheus会每分钟对这些告警规则进行计算,如果用户想定义自己的告警计算周期,则可以通过evaluation_interval来覆盖默认的计算周期:

1
2
global:
[ evaluation_interval: <duration> | default = 1m ]

模板化

一般来说,在告警规则文件的annotations中使用summary描述告警的概要信息,description用于描述告警的详细信息。同时Alertmanager的UI也会根据这两个标签值,显示告警信息。为了让告警信息具有更好的可读性,Prometheus支持模板化label和annotations的中标签的值。

通过$labels.<labelname>变量可以访问当前告警实例中指定标签的值。$value则可以获取当前PromQL表达式计算的样本值。

1
2
3
4
# To insert a firing element's label values:
{{ $labels.<labelname> }}
# To insert the numeric expression value of the firing element:
{{ $value }}

例如,可以通过模板化优化summary以及description的内容的可读性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
groups:
- name: example
rules:
# Alert for any instance that is unreachable for >5 minutes.
- alert: InstanceDown
expr: up == 0
for: 5m
labels:
severity: page
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."

# Alert for any instance that has a median request latency >1s.
- alert: APIHighRequestLatency
expr: api_http_request_latencies_second{quantile="0.5"} > 1
for: 10m
annotations:
summary: "High request latency on {{ $labels.instance }}"
description: "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)"

实战

配置 Alert Rules,修改 Prometheus 配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- /etc/config/recording_rules.yml
- /etc/config/alerting_rules.yml
- /etc/config/rules
- /etc/config/alerts
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'container'
static_configs:
- targets: ['cadvisor:8080']
alerting_rules.yml: |
groups:
- name: hostStatsAlert
rules:
- alert: hostMemUsageAlert
expr: (sum(node_memory_MemTotal_bytes) - sum(node_memory_MemFree_bytes + node_memory_Buffers_bytes+node_memory_Cached_bytes)) / sum(node_memory_MemTotal_bytes) > 0.55
for: 1m
labels:
severity: page
annotations:
summary: "Instance {{ $labels.instance }} MEM usgae high"
description: "{{ $labels.instance }} MEM usage above 85% (current value: {{ $value }})"
alerts: |
{}
recording_rules.yml: |
{}
rules: |
{}

重启Prometheus后访问Prometheus UI 可以查看当前以加载的规则文件:

切换到Alerts标签可以查看当前告警的活动状态。

现在只是在 Prometheus Web 页面查看警告,通过配置 Alert Manager 可以实现连接钉钉/Slack等平台报警。

Recording Rules

通过PromQL可以实时对Prometheus中采集到的样本数据进行查询,聚合以及其它各种运算操作。而在某些PromQL较为复杂且计算量较大时,直接使用PromQL可能会导致Prometheus响应超时的情况。这时需要一种能够类似于后台批处理的机制能够在后台完成这些复杂运算的计算,对于使用者而言只需要查询这些运算结果即可。Prometheus通过Recoding Rule规则支持这种后台计算的方式,可以实现对复杂查询的性能优化,提高查询效率。

在Prometheus配置文件中,通过rule_files定义recoding rule规则文件的访问路径。

1
2
rule_files:
[ - <filepath_glob> ... ]

每一个规则文件通过以下格式进行定义:

1
2
groups:
[ - <rule_group> ]

一个简单的规则文件可能是这个样子的:

1
2
3
4
5
groups:
- name: example
rules:
- record: job:http_inprogress_requests:sum
expr: sum(http_inprogress_requests) by (job)

rule_group的具体配置项如下所示:

1
2
3
4
5
6
7
8
# The name of the group. Must be unique within a file.
name: <string>

# How often rules in the group are evaluated.
[ interval: <duration> | default = global.evaluation_interval ]

rules:
[ - <rule> ... ]

与告警规则一致,一个group下可以包含多条规则rule。

1
2
3
4
5
6
7
8
9
10
11
# The name of the time series to output to. Must be a valid metric name.
record: <string>

# The PromQL expression to evaluate. Every evaluation cycle this is
# evaluated at the current time, and the result recorded as a new set of
# time series with the metric name as given by 'record'.
expr: <string>

# Labels to add or overwrite before storing the result.
labels:
[ <labelname>: <labelvalue> ]

根据规则中的定义,Prometheus会在后台完成expr中定义的PromQL表达式计算,并且将计算结果保存到新的时间序列record中。同时还可以通过labels为这些样本添加额外的标签。

这些规则文件的计算频率与告警规则计算频率一致,都通过global.evaluation_interval定义:

1
2
global:
[ evaluation_interval: <duration> | default = 1m ]

参考资料