vlambda博客
学习文章列表

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》定义警报和录音规则

Defining Alerting and Recording Rules

记录规则是 Prometheus 的一个有用概念。它们允许您加速繁重的查询并启用 PromQL 中的子查询,否则这些查询将非常昂贵。警报规则类似于记录规则,但具有特定于警报的语义。由于测试是任何系统的基本组成部分,您将有机会在本章中学习如何确保记录和警报规则在部署之前按预期运行。 了解这些构造将有助于使 Prometheus 更快、更健壮,并启用其警报功能。

本章将涵盖以下主题:

  • Creating the test environment
  • How does rule evaluation work?
  • Setting up alerting in Prometheus
  • Testing your rules

Creating the test environment

在本章中,我们将专注于 Prometheus 服务器,我们将部署一个新实例,以便我们可以应用所涵盖的概念。

Deployment

让我们首先创建一个新的 Prometheus 实例并将其部署到服务器:

  1. To create a new instance of Prometheus, move into the correct repository path:
cd chapter09/
  1. Ensure that no other test environments are running and spin up this chapter's environment:
vagrant global-status
vagrant up
  1. Validate the successful deployment of the test environment using the following code:
vagrant status

这将输出以下内容:

Current machine states:

prometheus running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.

新实例将可供检查,Prometheus Web 界面可通过 http://192.168.42.10:9090 访问。

您现在可以通过执行以下命令来访问 prometheus 实例:

vagrant ssh prometheus

现在您已连接到 prometheus 实例,您可以验证本章中描述的说明。

Cleanup

完成测试后,请确保您在 chapter09/ 中并执行以下命令:

vagrant destroy -f

不要太担心;如果需要,您可以轻松地再次启动环境。

Understanding how rule evaluation works

Prometheus 允许定期评估 PromQL 表达式并存储它们生成的时间序列;这些被称为规则。正如我们将在本章中看到的,有两种类型的规则。这些规则是记录警报规则。它们共享相同的评估引擎,但目的有所不同,我们将在接下来介绍。

记录规则的评估结果作为配置中指定的时间序列的样本保存到 Prometheus 数据库中。这种类型的规则可以通过预先计算昂贵的查询,将原始数据聚合成一个时间序列,然后可以导出到外部系统(例如通过联合的更高级别的 Prometheus 实例,如 < a href="https://subscription.packtpub.com/book/virtualization-and-cloud/9781789612349/17" linkend="ch17lvl1sec03">第 13 章,Prometheus 的扩展和联合 ),并且可以帮助创建复合范围向量查询(虽然在过去记录规则是这样做的唯一方法,但新的子查询语法为这些查询启用了探索性用例)。

当规则中评估的 PromQL 表达式产生非空结果时,会触发警报规则。它们是在 Prometheus 中完成时间序列警报的机制。警报规则在触发时也会产生新的时间序列,但不要将评估结果作为样本;相反,他们创建一个 ALERTS 指标,其中警报名称和状态作为标签,以及配置中定义的任何其他标签。这将在下一节进一步分析。

Using recording rules

规则与主 Prometheus 配置文件分开定义,后者通过 rule_files 顶级配置键包含。它们会定期评估,并且可以使用 global 内的 evaluation_interval 全局定义间隔(默认为一分钟)。

我们可以通过查看测试环境提供的配置来看到这一点:

vagrant@prometheus:~$ cat /etc/prometheus/prometheus.yml
global:
...
evaluation_interval: 1m
...

rule_files:
- "recording_rules.yml"
...

rule_files 采用路径列表,可以是相对于主要 Prometheus 配置或绝对路径的路径。此外,glob 可用于匹配文件名(不是目录);例如,/etc/prometheus/rules/*.yml。 Prometheus 不会自动获取规则文件中的更改,因此需要重新加载(如 第 5 章运行 Prometheus 服务器)。如果在规则文件中发现任何错误,Prometheus 将无法重新加载,并将继续使用之前的配置运行。但是,如果服务器重新启动,它将无法启动。为确保不会发生这种情况,promtool 可用于提前测试错误(如 第 8 章疑难解答和验证) - 在使用自动化部署规则时强烈建议这样做。

就像 prometheus.yml 配置文件一样,rules 文件也是以 YAML 格式定义的。实际的格式很容易理解:

groups:
- name: <group_name_1>
interval: <evaluation_interval>
rules:
- record: <rule_name_1>
expr: <promql_expression_1>
labels:
<label_name> : <label_value>
...
- record: <rule_name_N>
expr: <promql_expression_N>
...
- name: <group_name_N>
...

每个文件在 groups 键下定义一个或多个规则组。每个组都有一个 name、一个可选的评估 interval(默认为主 Prometheus 配置文件中定义的全局评估间隔)和一个 rules< /kbd>。每个规则都指示 Prometheus 将评估在 expr 中定义的 PromQL 表达式的结果记录到指定的度量名称中,可以选择在存储结果之前添加或覆盖系列标签集,方法是将它们设置在 labels. 每个组中的规则按照它们声明的顺序依次进行评估,这意味着一个规则生成的时间序列可以安全地用于同一组内的后续规则中。规则生成的样本将具有与规则组评估时间对应的时间戳。下图说明了前面提到的过程:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》定义警报和录音规则
Figure 9.1: The Rule Manager is the Prometheus internal subsystem responsible for the periodic evaluation of rules
according to their group's evaluation interval, as well as managing the alerting life cycle

让我们看一下本章测试环境中可用的录制规则:

vagrant@prometheus:~$ cat /etc/prometheus/recording_rules.yml

该文件有两个规则组,分别命名为 recording_rulesdifferent_eval_interval

...
- name: recording_rules
rules:
- record: instance:node_cpu:count
expr: count without (cpu) (count without (mode) (node_cpu_seconds_total))
...

第一个规则组由单个记录规则组成,它使用全局评估间隔,从节点导出器中获取 node_cpu_seconds_total 指标来计算 虚拟机中可用的 CPU 内核数 (VM),并将结果记录到名为 instance:node_cpu:count 的新时间序列中。

第二个规则组比较忙;它显示了组的自定义评估间隔和使用组中先前规则生成的时间序列的记录规则。我们不会详细介绍这些规则的作用,因为它们将作为以下规则命名约定部分的示例,但可以在此处查看评估间隔:

...
- name: different_eval_interval
interval: 5s
...

通过在第二个规则组中声明评估间隔,我们将覆盖 prometheus.ymlglobal 部分中的配置集——该组中的规则将在规定的频率。这样做仅用于演示目的;通常不鼓励设置不同的时间间隔,原因与抓取作业相同:当使用具有不同采样率的序列时,查询可能会产生错误的结果,并且必须定期 跟踪哪些序列具有什么变得无法管理。

Prometheus 在网络用户界面 (UI) 中提供了一个状态页面,用户可以在其中检查加载的规则组及其随附的录制规则、录制状态、录制时长对每个的最后一次评估,以及它们是在多长时间前运行的。您可以通过进入 Status | 找到此页面。顶栏上的规则

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》定义警报和录音规则
Figure 9.2: Prometheus web interface showing the /rules endpoint

有了这些信息,我们现在有了如何创建记录规则的基础知识。接下来,我们将探讨 Prometheus 社区就记录规则达成的命名约定。

Naming convention for recording rules

记录规则名称验证遵循与指标名称相同的正则表达式,因此从技术上讲,规则可以与任何其他指标命名相同。然而,在命名记录规则时有明确的标准可以更容易地在抓取的指标中识别它们,知道它们是从哪些指标派生的,并了解对它们应用了哪些聚合。

Prometheus 社区倾向于为记录规则制定明确的命名约定。这是基于多年大规模运行 Prometheus 的经验。如果使用得当,这将实现上述所有优势。

记录规则命名的推荐约定由三部分组成,用冒号分隔,格式如下:level:metric:operations。第一部分表示规则的聚合级别,这意味着它将列出存在且相关的标签/维度(通常用下划线分隔);第二部分是作为规则基础的度量名称;第三部分列出了应用于度量的聚合操作。

本章介绍的录制规则都遵循这个约定,让我们看一下测试环境中可用的第二个规则组:

- record: handler_instance:prometheus_http_request_duration_seconds_sum:rate5m
expr: >
rate(prometheus_http_request_duration_seconds_sum[5m])

- record: handler_instance:prometheus_http_request_duration_seconds_count:rate5m
expr: >
rate(prometheus_http_request_duration_seconds_count[5m])

- record: handler:prometheus_http_request_duration_seconds:mean5m
expr: >
sum without (instance) (
handler_instance:prometheus_http_request_duration_seconds_sum:rate5m
)
/
sum without (instance) (
handler_instance:prometheus_http_request_duration_seconds_count:rate5m
)

看第一条规则的命名,我们可以很容易理解该规则是基于 prometheus_http_request_duration_seconds_sum 度量的; rate5m 表示 rate() 被应用于五分钟的范围向量,并且存在的有趣标签是 handlerinstance

相同的逻辑适用于第二条规则,但这次使用 prometheus_http_request_duration_seconds_count 指标。然而,第三条规则更微妙一些。因为它将 _sum 除以延迟事件的 _count,所以它有效地表示了 Prometheus 服务的 HTTP 请求的五分钟平均延迟。当我们聚合 instance 标签时,level 部分通过仅将 handler 作为相关维度来反映这一点。最后要注意的是,该规则的指标名称现在是 prometheus_http_request_duration_seconds,因为它既不代表总和也不代表计数,但仍然可以清楚地了解该规则基于哪些指标.

命名记录规则可能是一项艰巨的任务,需要在精确表示所有起作用的因素和足够简洁以使度量名称易于管理之间取得平衡。当您发现自己无法立即清楚如何根据其表达式命名记录规则时,可以遵循的一个好的经验法则是确保知道此命名约定的其他人可以将规则绑定到使用的度量标准,应该存在哪些标签/尺寸,以及应用了哪些转换。

Setting up alerting in Prometheus

到目前为止,我们已经介绍了 PromQL 如何在查询收集的数据方面发挥重要作用,但是当我们需要不断评估表达式以便在满足定义的条件时触发事件时,我们会立即进入警报。我们在第 1 章监控基础。需要明确的是,Prometheus 不负责发布电子邮件、Slack 或任何其他形式的通知;那是另一个服务的责任。该服务通常是 Alertmanager,我们将在 第 11 章 了解和扩展 Alertmanager。 Prometheus 利用警报规则的力量来推送警报,我们将在接下来介绍。

What is an alerting rule?

警报规则很像带有一些附加定义的记录规则。他们甚至可以毫无问题地共享同一个规则组。最大的区别在于,在触发时,它们会通过带有 JSON 有效负载的 HTTP POST 发送到外部端点以进行进一步处理。扩展术语活动,在这种情况下,我们讨论的是当前状态何时不同于所需状态,这归结为表达式返回一个或多个样本的时间。

警报规则(例如记录规则)依赖于在定义的时间间隔内评估的 PromQL 表达式。此间隔可以是全局配置的,也可以是特定规则组的本地间隔。在每个间隔迭代中,都会验证触发的警报以确保它们仍然处于活动状态;如果没有,则认为已解决。

对于我们的表达式返回的每个样本,它都会触发警报。记住这一点很重要,因为宽松的 PromQL 表达式会生成大量警报,因此请尽可能将它们汇总。

Configuring alerting rules

为了演示如何创建和理解警报规则,我们将指导您完成整个过程。这不仅会涉及 Prometheus 主配置文件,还会涉及规则文件以及服务器的 Web 界面如何处理警报。

Prometheus server configuration file

在本章的测试环境中,我们可以找到以下 Prometheus 配置:

vagrant@prometheus:~$ cat /etc/prometheus/prometheus.yml 
global:
...
evaluation_interval: 1m
...
rule_files:
- "recording_rules.yml"
- "alerting_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- “prometheus:5001”
...

在此配置中,需要注意三个组件:

  • evaluation_interval: This is responsible for defining the global evaluation interval for recording and alerting rules, which can be overridden at the rule group level using the interval keyword.
  • rule_files: This is the file location where Prometheus can read the configured recording and/or alerting rules.
  • alerting: This is the endpoint(s) where Prometheus sends alerts for further processing.

在警报部分,我们配置了 “prometheus:5001”。在这个端点后面,只有一个名为 alertdump 的小服务,它在端口 5001 上侦听 HTTP POST 请求,并将其负载转储到日志中文件。这将有助于剖析 Prometheus 在警报触发时发送的内容。

Rule file configuration

之前,我们看了一下Prometheus的配置文件;我们现在将转到提供的警报规则示例,我们可以在以下代码段中看到:

vagrant@prometheus:~$ cat /etc/prometheus/alerting_rules.yml 
groups:
- name: alerting_rules
rules:
- alert: NodeExporterDown
expr: up{job="node"} != 1
for: 1m
labels:
severity: "critical"
annotations:
description: "Node exporter {{ $labels.instance }} is down."
link: "https://example.com"

让我们更仔细地看一下 NodeExporterDown 警报定义。我们可以将配置分为五个不同的部分:alertexprforlabels注释。我们现在将在下表中逐一介绍:

部分

说明

强制

警报

要使用的警报名称

是的

表达式

要评估的 PromQL 表达式

是的

发送警报前确保警报被触发的时间,默认为 0

标签

用户定义的键值对

注释

用户定义的键值对

The Prometheus community typically uses CamelCase for alert naming.
Prometheus 不执行验证来检查警报名称是否已被使用,因此两个或多个警报可能共享相同的名称但评估不同的表达式。这可能会导致问题,例如跟踪触发了哪个特定警报,或为警报编写测试。

NodeExporterDown 规则只会在 job=”node” 选择器的 up 指标不是 1 时触发超过一分钟,我们现在将通过停止 Node Exporter 服务来测试:

vagrant@prometheus:~$ sudo systemctl stop node-exporter
vagrant@prometheus:~$ sudo systemctl status node-exporter

...
Mar 05 20:49:40 prometheus systemd[1]: Stopping Node Exporter...
Mar 05 20:49:40 prometheus systemd[1]: Stopped Node Exporter.

我们现在强制激活警报。这将强制警报经历三种不同的状态:

订购

说明

1

不活跃

尚未挂起或解雇

2

待办的

还没有足够长的时间来开始射击

3

射击

活动 for 超过定义的 for 子句阈值

转到 Prometheus 服务器 Web 界面上的 /alerts 端点,我们可以可视化 NodeExporterDown 警报的三种不同状态。首先,警报处于非活动状态,如下图所示:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》定义警报和录音规则
Figure 9.3: The NodeExporterDown alert is inactive

然后,我们可以看到警报处于待处理状态。这意味着,当警报条件已被触发时,Prometheus 将继续检查每个评估周期是否继续触发该条件,直到 for 持续时间过去。下图说明了挂起状态; 注意 Show annotations 复选框被选中,它会展开警报注释:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》定义警报和录音规则
Figure 9.4: The NodeExporterDown alert is pending

最后,我们可以看到警报转向开火。这意味着警报的活动时间超过了 for 子句定义的持续时间——在本例中为 1 分钟:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》定义警报和录音规则
Figure 9.5: The NodeExporterDown alert is firing

当警报触发时,Prometheus 将 JSON 有效负载发送到配置的警报服务端点,在我们的例子中,该端点是 alertdump 服务,它被配置为记录到 /vagrant/cache/alerting.log 文件。这使得很容易理解正在发送的信息类型,并且可以按如下方式进行验证:

vagrant@prometheus:~$ cat /vagrant/cache/alerting.log
[
{
"labels": {
"alertname": "NodeExporterDown",
"dc": "dc1",
"instance": "prometheus:9100",
"job": "node",
"prom": "prom1",
"severity": "critical"
},
"annotations": {
"description": "Node exporter prometheus:9100 is down.",
"link": "https://example.com"
},
"startsAt": "2019-03-04T21:51:15.04754979Z",
"endsAt": "2019-03-04T21:58:15.04754979Z",
"generatorURL": "http://prometheus:9090/graph?g0.expr=up%7Bjob%3D%22node%22%7D+%21%3D+1&g0.tab=1"
}
]

现在我们已经了解了如何配置一些警报规则并验证了 Prometheus 发送到配置的警报系统的内容,让我们探索如何使用标签和注释来丰富这些警报的上下文信息。

Labels and annotations

在警报规则定义中,有两个可选部分:标签和注释。标签定义了警报的身份,并且可以根据所处的评估周期进行更改;如果他们这样做,它将改变警报标识。为了证明这一点,我们将介绍 ALERTS 指标,该指标跟踪所有活动警报及其标签。如下图所示,我们有一个名为 alertstate 的标签,它跟踪警报状态以及从 pendingfiring 的转换:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》定义警报和录音规则
Figure 9.6: The ALERTS metric

要记住的是在标签中使用样本值的问题。尽管在技术上是可行的,但这也是一个非常糟糕的主意。这样做会在每次值更改时更改警报标识,因此,将始终重新启动定义的 for 倒计时,从而导致永远不会进入 firing 状态。

另一方面,注释不属于警报的标识,因此不存储在 ALERTS 指标中。这些对于使用更多上下文和信息来丰富警报很有用。正如我们在示例中看到的,注释也使用 Go 模板语言进行模板化。通过使用 {{ .Labels.instance }} 模板语法, 我们正在访问可用的警报标签,选择 instance 标签,并使用它在注释 description 字段中的值。如果需要,可以使用 {{ .Value }} 获得触发样本的值。

The Golang template .Labels and .Value variables are also available as $labels and $value for convenience.

以下片段显示了我们示例中的警报规则

   annotations:
description: "Node exporter {{ .Labels.instance }} is down."
link: "https://example.com"

这将在 firing 时产生以下渲染结果:

       "annotations": {
"description": "Node exporter prometheus:9100 is down.",
"link": "https://example.com"
},
You can find more information regarding Golang templating at https://golang.org/pkg/text/template/.

Delays on alerting

在前面的主题中,我们讨论了警报所经历的三种状态;但是在计算警报触发所需的总时间时,还有更多内容。首先,有抓取间隔(在我们的示例中,为 30 秒,尽管为了清晰起见,抓取和评估间隔通常应该相同),然后我们有规则评估间隔(在我们的示例中,它被全局定义为 1分钟),最后,警报规则的 for 子句中定义了 1 分钟。如果我们将所有这些变量放在一起,在最坏的情况下,此警报被视为 触发 的时间可能需要长达 2 分 30 秒。下图说明了这个示例情况:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》定义警报和录音规则
Figure 9.7: Alert delay visualized

所有这些延迟都只是在普罗米修斯方面。处理发送的警报的外部服务可能有其他约束,这可能会使全局延迟直到发送通知的时间更长。

在 Prometheus 2.4.0 之前, 待定firing 状态在重新启动后不会持续存在,这可能会进一步延长警报延迟。这是通过实施一个新的指标来解决的,称为 ALERTS_FOR_STATE,存储警报状态。您可以在以下位置找到 Prometheus 2.4.0 的发行说明 https://github.com/prometheus/prometheus/releases/tag/v2.4.0

Testing your rules

第 8 章中,疑难解答和验证,我们了解了 promtool 必须提供的功能,但测试除外。 test rules 子命令可以模拟多个时间序列的样本的周期性摄取,使用这些序列来评估记录和警报规则,然后测试记录的序列是否与配置为预期结果的结果相匹配。现在我们了解了记录和警报规则,我们将看看如何通过创建单元测试并使用 promtool 来验证我们的规则来确保它们按预期运行。

Recording rules tests

Prometheus 二进制发行版中包含的 promtool 工具允许我们定义测试用例来验证我们编写的规则是否符合预期。本章的测试环境还附带了一套针对我们迄今为止探索过的规则的预构建测试。你可以看看这里的配置:

vagrant@prometheus:~$ cat /etc/prometheus/tests.yml

该文件对本章中介绍的所有记录和警报规则进行了测试。尽管您不需要在单个文件中定义每个测试(事实上,为每个规则组创建一个测试文件以保持事物井井有条),但为了简单起见,在本例中这样做了。现在,让我们只分析记录规则,因为它们更容易掌握。测试文件的顶级配置键定义了要加载的规则文件和测试的默认评估间隔,当它们没有明确声明自己的规则评估时,它控制记录和警报规则评估的周期:

rule_files:
- /etc/prometheus/recording_rules.yml
...
evaluation_interval: 1m

虽然 测试文件中的 rule_files 配置键可能看起来与主 Prometheus 配置文件中的相同,它不支持通配符(使用文件名通配符)。

在这些全局配置之后是测试用例的定义,在 tests 键下。您可以定义多个测试包,每个测试包都有自己的模拟抓取间隔、收集的系列和正在测试的规则。让我们看一下文件中定义的第一个测试,我们在其中添加了一些注释以使其更易于理解:

tests:

interval 设置在我们的模拟时间序列中生成间隔样本的时间:

- interval: 15s

input_series 列表定义了要生成的时间序列以及在模拟收集间隔的每次迭代中要生成的值:

    input_series:
- series: 'node_cpu_seconds_total{cpu="0",instance="prometheus:9100",job="node",mode="user"}'
values: '1 1 1 1 1 1 1 1 1 1'
- series: 'node_cpu_seconds_total{cpu="1",instance="prometheus:9100",job="node",mode="user"}'
values: '1 1 1 1 1 1 1 1 1 1'
- series: 'node_cpu_seconds_total{cpu="0",instance="example:9100",job="node",mode="idle"}'
values: '1 1 1 1 1 1 1 1 1 1'
- series: 'node_cpu_seconds_total{cpu="0",instance="example:9100",job="node",mode="system"}'
values: '1 1 1 1 1 1 1 1 1 1'

要测试的 PromQL 表达式列表定义为 promql_expr_test

promql_expr_test:

每个 expr 定义一个特定的表达式:

- expr: instance:node_cpu:count

通过设置 eval_time 设置该表达式将运行的时间点,并且应通过将该表达式作为 exp_samples 运行来返回预期的样本:

        eval_time: 1m
exp_samples:
- labels: 'instance:node_cpu:count{instance="prometheus:9100", job="node"}'
value: 2
- labels: 'instance:node_cpu:count{instance="example:9100", job="node"}'
value: 1

在这个测试包中,我们可以看到每 15 秒针对相同的指标 node_cpu_seconds_total 生成四个时间序列。由于这些系列的实际值与此记录规则无关(它只计算每个实例的 CPU 数量),因此为每个样本设置了 1 的值。请注意当前标签的变化,即 prometheus:9100 实例报告两个 CPU 的指标,而 example:9100 报告一个。实际测试只是验证,当在 t=1m 处评估 instance:node_cpu:count 表达式时(好像在生成的集合开始后经过了 1 分钟),返回的样本应显示每个实例的正确 CPU 计数。

我们现在准备使用以下指令执行测试:

vagrant@prometheus:/etc/prometheus$ promtool test rules tests.yml 
Unit Testing: tests.yml
SUCCESS

这可确保配置的记录规则按照我们预期的方式运行。您可以尝试通过从 instance:node_cpu:count 测试包中的 prometheus:9100 实例中删除输入系列之一来打破测试。当您再次运行测试时,将显示以下内容,因为其中一个测试现在失败:

vagrant@prometheus:/etc/prometheus$ promtool test rules tests.yml 
Unit Testing: tests.yml
FAILED:
expr:'instance:node_cpu:count', time:1m0s,
exp:"{__name__=\"instance:node_cpu:count\", instance=\"example:9100\", job=\"node\"} 1E+00, {__name__=\"instance:node_cpu:count\", instance=\"example:9100\", job=\"node\"} 1E+00, {__name__=\"instance:node_cpu:count\", instance=\"prometheus:9100\", job=\"node\"} 2E+00",
got:"{__name__=\"instance:node_cpu:count\", instance=\"example:9100\", job=\"node\"} 1E+00, {__name__=\"instance:node_cpu:count\", instance=\"example:9100\", job=\"node\"} 1E+00, {__name__=\"instance:node_cpu:count\", instance=\"prometheus:9100\", job=\"node\"} 1E+00"

这个输出告诉我们的是 promtool 期待定义的样本集,但返回了不同的样本集。您可以看到,正如我们配置的那样,记录规则现在只为 prometheus:9100 实例报告一个 CPU。这让我们确信规则的行为完全符合我们的要求。

第二个记录规则组的测试基本相同,但它们展示了一个强大的符号来生成更丰富的输入序列:

  - interval: 5s
input_series:
- series: 'prometheus_http_request_duration_seconds_count{handler="/",instance="localhost:9090",job="prometheus"}'
values: '0+5x60'
- series: 'prometheus_http_request_duration_seconds_sum{handler="/",instance="localhost:9090",job="prometheus"}'
values: '0+1x60'

这称为扩展符号。这是声明随时间生成时间序列值的公式的简洁方式。它采用 A+BxCA-BxC 的形式,其中 A 是起始值,B是序列值在每次迭代中应该具有的增加量(当以 + 开头时)或减少(当以 - 开头时),并且 C 是应该应用这个增加或减少的迭代次数。

回到我们的示例,0+5x60 将扩展为以下系列:

0 5 10 15 20 … 290 295 300

在声明输入时间序列的值时,您可以将文字值与扩展符号混合和匹配。这使您可以轻松创建复杂的行为。举个例子:

0 1 1 0 1+0x3 0 1 1 0 1 1 0 0 0 1 1 0+0x3 1

这将扩展为以下内容:

0 1 1 0 1 1 1 1 0 1 1 0 1 1 0 0 0 1 1 0 0 0 0 1

测试是避免不可预见问题的基础,根据目前介绍的信息,您现在可以生成自己的单元测试来记录规则。接下来,我们将继续处理单元测试,但这次专门与警报规则相关。

Alerting rules tests

警报规则的单元测试与用于记录规则的单元测试非常相似。我们将使用本章前面提供的示例警报来执行有关如何配置警报测试以及如何验证它们的演练。如前所述,本章的测试环境附带了一套针对此处介绍的规则的测试,包括我们感兴趣的警报规则。再次,您可以使用以下命令查看测试文件:

vagrant@prometheus:~$ cat /etc/prometheus/tests.yml

仅关注警报组件,我们可以看到我们首先定义了警报规则的位置:

rule_files:
- /etc/prometheus/alerting_rules.yml

默认规则评估间隔在同一文件中的记录和警报规则之间共享:

evaluation_interval: 1m

警报测试方便地在它自己的测试组中,所以让我们看一下它的完整定义:

  - interval: 1m
input_series:
- series: 'up{job="node",instance="prometheus:9100"}'
values: '1 1 1 0 0 0 0 0 0 0'
- series: 'up{job="prometheus",instance="prometheus:9090"}'
values: '1 0 1 1 1 0 1 1 1 1'

测试组定义与前面解释的相同,除了 alert_rule_test 部分,我们在这里定义警报测试。在这个例子中需要注意的是,我们的测试规则永远不应该选择第二个输入序列,因为定义的警报是专门匹配的 job="node"

    alert_rule_test:
- alertname: NodeExporterDown
eval_time: 3m
- alertname: NodeExporterDown
eval_time: 4m
exp_alerts:
- exp_labels:
instance: "prometheus:9100"
job: "node"
severity: "critical"
exp_annotations:
description: "Node exporter prometheus:9100 is down."
link: "https://example.com"
It's not mandatory to have alert_rule_test and promql_expr_test in separate test blocks; you may have both in the same test group when you have recording and alerting rules using the same input time series and with the same evaluation interval.

alert_rule_test 部分列出了应该在相对于测试运行的模拟开始时间 (eval_time) 评估哪些警报 (alertname)。如果预计警报会在那个时候触发,则应定义一个额外的 exp_alerts 部分,列出预期标签 (exp_labels) 和注释 (exp_annotations< /kbd>) 应该存在于警报的每个实例中。将 exp_alerts 部分留空意味着警报不会在给定时间触发。

第一个警报测试将在第三分钟执行,并且由于我们之前提供的匹配系列在那个时刻返回值 1,因此在 alerting_rules.yml 不会触发——这意味着警报中定义的表达式不会返回任何数据。

第二条警报规则将在第四分钟 执行并返回数据,因为我们提供的匹配系列具有样本值 0 在那个特定的时刻。警报规则返回的所有标签都需要明确检查。测试还必须检查警报返回的所有描述,以及完全展开的任何模板化变量。

我们现在可以使用以下指令运行测试:

vagrant@prometheus:~$ promtool test rules /etc/prometheus/tests.yml 
Unit Testing: /etc/prometheus/tests.yml
SUCCESS

作为额外的步骤,尝试将第二个警报的描述从 prometheus:9100 更改为类似 prometheus:9999 的内容,然后再次运行测试。您应该得到以下输出:

vagrant@prometheus:~$ promtool test rules /etc/prometheus/tests.yml 
Unit Testing: /etc/prometheus/tests.yml
FAILED:
alertname:NodeExporterDown, time:4m0s,
exp:"[Labels:{alertname=\"NodeExporterDown\", instance=\"prometheus:9100\", job=\"node\", severity=\"critical\"} Annotations:{description=\"Node exporter prometheus:9999 is down.\", link=\"https://example.com\"}]",
got:"[Labels:{alertname=\"NodeExporterDown\", instance=\"prometheus:9100\", job=\"node\", severity=\"critical\"} Annotations:{description=\"Node exporter prometheus:9100 is down.\", link=\"https://example.com\"}]"

虽然此警报非常简单且易于确定它会在哪些条件下触发,但警报规则测试为您提供了保证,即当您无法在环境中合理重现的条件发生时,警报将触发。

Summary

在本章中,我们有机会观察到生成导数时间序列的不同方法。当需要经常性的繁重查询时,记录规则有助于提高监控系统的稳定性和性能,方法是通过将它们预先计算到查询起来相对便宜的新时间序列中。警报规则为警报带来了 PromQL 的强大功能和灵活性;它们可以触发复杂和动态阈值的警报,以及使用单个警报规则针对多个实例甚至不同的应用程序。掌握警报中的延迟是如何引入的,现在可以帮助您根据自己的需要调整它们,但请记住,一点延迟比嘈杂的警报要好。最后,我们探索了如何为我们的规则创建单元测试并在 Prometheus 服务器运行之前验证它们。

下一章将介绍监控的另一个组成部分:可视化。我们将深入研究 Grafana,这是 Prometheus 驱动的仪表板的社区首选选择。

Questions

  1. What are the primary uses for recording rules?
  2. Why should you avoid setting different evaluation intervals in rule groups?
  3. If you were presented with the instance_job:latency_seconds_bucket:rate30s metric, what labels would you expect to find and what would be the expression used to record it?
  4. Why is using the sample value of an alert in the alert labels a bad idea?
  5. What is the pending state of an alert?
  6. How long would an alert wait between being triggered and transitioning to the firing state when the for clause is not specified?
  7. How can you test your rules without using Prometheus?