Prometheus快速入门

Prometheus 其实就是一个数据监控解决方案,它能帮你简单快速地搭建起一套可视化的监控系统。但这么说还是有点抽象, 下面我举几个简单的例子,帮助大家理解 Prometheus 究竟能做什么?

对于运维人员来说,他们需要监控机器的 CPU、内存、硬盘的使用情况,以此来保证运行在机器上的应用的稳定性。

对于研发人员来说,他们关注某个异常指标的变化情况,从而来保证业务的稳定运行。

对于产品或运营来说,他们更关心产品层面的事情,例如:某个活动参加人数的增长情况,活动积分的发放情况。 img.png

对于上面说到的这些功能,Prometheus 都能够实现。Prometheus 能根据这些收集的数据实现告警功能。

例如:运维希望在 CPU 达到 80% 的时候给值班的运维人员发送邮件,产品希望活动积分发放数量超过 10 万的时候发送告警邮件。 这些都可以通过 Prometheus 实现。

除了数据收集、告警功能之外,Prometheus 还有很多强大的功能,例如:强大的 ProQL 查询、许多客户端库等。

基本原理

Prometheus的基本架构如下图所示: img.png

从上图可以看到,整个 Prometheus 可以分为四大部分,分别是:

  • Prometheus Server: Prometheus组件中的核心部分,负责实现对监控数据的获取,存储以及查询。
  • NodeExporter:业务数据源通过 Pull/Push 两种方式推送数据到 Prometheus Server。
  • AlertManager:报警管理器,Prometheus 通过配置报警规则,如果符合报警规则,那么就将报警推送到 AlertManager, 由其进行报警处理。
  • 可视化监控界面:Prometheus 收集到数据之后,由 WebUI 界面进行可视化图标展示。 目前我们可以通过自定义的 API 客户端进行调用数据展示,也可以直接使用 Grafana 解决方案来展示。

简单地说,Prometheus 的实现架构也并不复杂。其实就是收集数据、处理数据、可视化展示,再进行数据分析进行报警处理。 但其珍贵之处在于提供了一整套可行的解决方案,并且形成了一整个生态,能够极大地降低我们的研发成本。

安装和配置

这一小节可以单独参考《Grafana快速入门》,通过Docker Compose来安装更方便。

PromQL介绍

PromQL(Prometheus Query Language)是 Prometheus 内置的数据查询语言,它能实现对事件序列数据的查询、聚合、 逻辑运算等。它并且被广泛应用在 Prometheus 的日常应用当中,包括对数据查询、可视化、告警处理当中。

简单地说,PromQL 广泛存在于以 Prometheus 为核心的监控体系中。所以需要用到数据筛选的地方,就会用到 PromQL。 例如:监控指标的设置、报警指标的设置等等。

当我们安装了Prometheus后,它提供了一个比较简单的Web UI界面来让我们验证和测试PromQL。

当我们直接使用监控指标名称查询时,可以查询该指标下的所有时间序列。我们这里启动 Prometheus 服务器, 并打开http://IP:9090/graph地址。在查询框中输入promhttp_metric_handler_requests_total并点击执行。

img.png

PromQL 支持户根据时间序列的标签匹配模式来对时间序列进行过滤,目前主要支持两种匹配模式:完全匹配和正则匹配。

完全匹配

PromQL 支持使用 = 和 != 两种完全匹配模式。

  • 等于。通过使用 label=value 可以选择那些标签满足表达式定义的时间序列。
  • 不等于。通过使用 label!=value 则可以根据标签匹配排除时间序列。

例如我们上面查询出了所有指标名称为 promhttp_metric_handler_requests_total 的数据。 这时候我们希望只查看错误的请求,即过滤掉所有 code 标签不是 200 的数据。 那么我们的 PromQL 表达式可以修改为:

1
promhttp_metric_handler_requests_total{code!="200"}

正则匹配

PromQL 还可以使用正则表达式作为匹配条件,并且可以使用多个匹配条件。

  • 正向匹配。使用 label=~regx 表示选择那些标签符合正则表达式定义的时间序列。
  • 反向匹配。使用 label!~regx 进行排除。

例如所有 code 标签以 50 开头的记录,那么我的表达式为:

1
promhttp_metric_handler_requests_total{code=~"50\\d+"}。

范围查询

我们上面直接通过类似 promhttp_metric_handler_requests_total 表达式查询时间序列时, 同一个指标同一标签只会返回一条数据。 这样的表达式我们称之为瞬间向量表达式,而返回的结果称之为瞬间向量。

而如果我们想查询一段时间范围内的样本数据,那么我们就需要用到区间向量表达式,其查询出来的结果称之为区间向量。 时间范围通过时间范围选择器 [] 进行定义。例如,通过以下表达式可以选择最近5分钟内的所有样本数据:

1
promhttp_metric_handler_requests_total{}[5m]

通过查询结果可以看到,此时我们查询出了所有的样本数据,而不再是一个样本数据的统计值。

除了使用m表示分钟以外,PromQL的时间范围选择器支持其它时间单位:

1
2
3
4
5
6
s - 秒
m - 分钟
h - 小时
d - 天
w - 周
y - 年

时间位移操作

在瞬时向量表达式或者区间向量表达式中,都是以当前时间为基准:

1
2
3
4
# 瞬时向量表达式,选择当前最新的数据
promhttp_metric_handler_requests_total{}
# 区间向量表达式,选择以当前时间为基准,5分钟内的数据
promhttp_metric_handler_requests_total{}[5m]

如果我们想查询 5 分钟前的瞬时样本数据,或昨天一天的区间内的样本数据呢? 这个时候我们就可以使用位移操作, 位移操作的关键字为 offset。

1
2
3
4
5
6
# 查询 5 分钟前的最新数据
http_request_total{} offset 5m
# 往前移动 1 天,查询 1 天前的数据
# 例如现在是 2020-10-07 00:00:00
# 那么这个表达式查询的数据是:2020-10-05 至 2020-10-06 的数据
http_request_total{}[1d] offset 1d

聚合操作

一般情况下,我们通过 PromQL 查询到的数据都是很多的。PromQL 提供的聚合操作可以用来对这些时间序列进行处理, 形成一条新的时间序列。

以我们的 promhttp_metric_handler_requests_total 指标为例,不加任何条件我们查询到的数据有3条。 这 3 条数据的 value 总和为 2091。那么我们使用下面两个聚合操作表达式来查询,看看结果对不对。

第一个表达式,计算一共有几条数据,结果为3。

1
count(promhttp_metric_handler_requests_total)

二个表达式,计算所有数据的 value 总和,结果为2093:

1
sum(promhttp_metric_handler_requests_total)

可以看到 count 的数值是一致的,都是 8。但是 sum 的数值有误差,这是因为我们两次查询的时间间隔内, 某些记录的数值发生了变化。

标量

在 PromQL 中,标量是一个浮点型的数字值,没有时序。例如:10。

需要注意的是,当使用表达式count(http_requests_total),返回的数据类型,依然是瞬时向量。 用户可以通过内置函数scalar()将单个瞬时向量转换为标量。

字符串

在 PromQL 中,字符串是一个简单的字符串值。直接使用字符串作为 PromQL 表达式,则会直接返回字符串。

1
"this is a string"

PromQL 操作符

PromQL 还支持丰富的操作符,用户可以使用这些操作符对进一步的对事件序列进行二次加工。这些操作符包括: 数学运算符,逻辑运算符,布尔运算符等等。

数学运算符比较简单,就是简单的加减乘除等。PromQL支持的所有数学运算符如下所示:

  • + (加法)
  • - (减法)
  • * (乘法)
  • / (除法)
  • % (求余)
  • ^ (幂运算)

布尔运算符支持用户根据时间序列中样本的值,对时间序列进行过滤。例如如果我们想筛选出请求次数超过 20 次的接口呢?

1
prometheus_http_requests_total > 20

从上面的图中我们可以看到,value 的值还是具体的数值。但如果我们希望对符合条件的数据,value 变为 1。 不符合条件的数据,value 变为 0。那么我们可以使用bool修饰符。

1
prometheus_http_requests_total > bool 20

目前,Prometheus支持以下布尔运算符如下:

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

通过集合运算,可以在两个瞬时向量与瞬时向量之间进行相应的集合操作。目前,Prometheus支持以下集合运算符:

  • and 与操作:该集合中的元素同时在 vector1 和 vector2 中都存在
  • or 或操作:该集合中包含 vector1 和 vector2 中的所有元素
  • unless 排除操作:首先取 vector1 集合的所有元素,然后排除掉所有在 vector2 中存在的元素

在PromQL操作符中优先级由高到低依次为:

  • ^
  • *, /, %
  • +, -
  • ==, !=, <=, <, >=, >
  • and, unless
  • or

PromQL 聚合操作

Prometheus 还提供了聚合操作符,这些操作符作用于瞬时向量。可以将瞬时表达式返回的样本数据进行聚合, 形成一个新的时间序列。目前支持的聚合函数有:

  • sum (求和)
  • min (最小值)
  • max (最大值)
  • avg (平均值)
  • stddev (标准差):标准差(Standard Deviation)常用来描述数据的波动大小。
  • stdvar (标准方差)
  • count (计数)
  • count_values (对value进行计数)
  • bottomk (后n条时序):bottomk 用于对样本值进行排序,返回当前样本值后 N 位的时间序列。
  • topk (前n条时序):topk 用于对样本值进行排序,返回当前样本值前 N 位的时间序列。
  • quantile (分位数)

PromQL 内置函数

PromQL 提供了大量的内置函数,可以对时序数据进行丰富的处理。例如 irate() 函数可以帮助我们计算监控指标的增长率, 不需要我们去手动计算。

计算主机节点最近两分钟内的平均CPU使用率:

1
rate(node_cpu[2m])

需要注意的是使用rate或者increase函数去计算样本的平均增长速率,容易陷入「长尾问题」当中, 其无法反应在时间窗口内样本数据的突发变化。

例如,对于主机而言在 2 分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致 CPU 占用 100% 的情况, 但是通过计算在时间窗口内的平均增长率却无法反应出该问题。

为了解决该问题,PromQL提供了另外一个灵敏度更高的函数 irate(v range-vector)。 irate 同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。 irate 函数是通过区间向量中最后两个样本数据来计算区间向量的增长速率。

这种方式可以避免在时间窗口范围内的「长尾问题」,并且体现出更好的灵敏度, 通过 irate 函数绘制的图标能够更好的反应样本数据的瞬时变化状态。

1
irate(node_cpu[2m])

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

在一般情况下,系统管理员为了确保业务的持续可用运行,会针对服务器的资源设置相应的告警阈值。 例如,当磁盘空间只剩512MB时向相关人员发送告警通知。 这种基于阈值的告警模式对于当资源用量是平滑增长的情况下是能够有效的工作的。

但是如果资源不是平滑变化的呢? 比如有些某些业务增长,存储空间的增长速率提升了高几倍。 这时,如果基于原有阈值去触发告警,当系统管理员接收到告警以后可能还没来得及去处理问题,系统就已经不可用了。

因此阈值通常来说不是固定的,需要定期进行调整才能保证该告警阈值能够发挥去作用。那么还有没有更好的方法吗?

PromQL 中内置的 predict_linear(v range-vector, t scalar) 函数可以帮助系统管理员更好的处理此类情况, predict_linear 函数可以预测时间序列v在t秒后的值。

它基于简单线性回归的方式,对时间窗口内的样本数据进行统计,从而可以对时间序列的变化趋势做出预测。 例如,基于2小时的样本数据,来预测主机可用磁盘空间的是否在4个小时候被占满,可以使用如下表达式:

1
predict_linear(node_filesystem_free{job="node"}[2h], 4 * 3600) < 0