跳转至

报警规则

现在我们只是把 AlertManager 容器运行起来了,也和 Prometheus 进行了关联,但是现在我们并不知道要做什么报警,因为没有任何地方告诉我们要报警,所以我们还需要配置一些报警规则来告诉我们对哪些数据进行报警。

添加报警规则

警报规则允许你基于 Prometheus 表达式语言的表达式来定义报警报条件,并在触发警报时发送通知给外部的接收者。

同样在 Prometheus 的配置文件中添加如下报警规则配置:

rule_files:
  - /etc/prometheus/rules.yml

其中 rule_files 就是用来指定报警规则的,这里我们同样将 rules.yml 文件用 ConfigMap 的形式挂载到 /etc/prometheus 目录下面即可,比如下面的规则:(alert-rules.yml)

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: kube-mon
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s
      scrape_timeout: 15s
      evaluation_interval: 30s  # 默认情况下每分钟对告警规则进行计算
    alerting:
      alertmanagers:
      - static_configs:
        - targets: ["alertmanager:9093"]
    rule_files:
    - /etc/prometheus/rules.yml
  ...... # 省略prometheus其他部分
  rules.yml: |
    groups:
    - name: test-node-mem
      rules:
      - alert: NodeMemoryUsage
        expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100 > 20
        for: 2m
        labels:
          team: node
        annotations:
          summary: "{{$labels.instance}}: High Memory usage detected"
          description: "{{$labels.instance}}: Memory usage is above 20% (current value is: {{ $value }})"

上面我们定义了一个名为 NodeMemoryUsage 的报警规则,一条报警规则主要由以下几部分组成:

  • alert:告警规则的名称
  • expr:是用于进行报警规则 PromQL 查询语句
  • for:评估等待时间(Pending Duration),用于表示只有当触发条件持续一段时间后才发送告警,在等待期间新产生的告警状态为pending
  • labels:自定义标签,允许用户指定额外的标签列表,把它们附加在告警上
  • annotations:指定了另一组标签,它们不被当做告警实例的身份标识,它们经常用于存储一些额外的信息,用于报警信息的展示之类的

for 属性

这个参数主要用于降噪,很多类似响应时间这样的指标都是有抖动的,通过指定 Pending Duration,我们可以过滤掉这些瞬时抖动,可以让我们能够把注意力放在真正有持续影响的问题上。

为了让告警信息具有更好的可读性,Prometheus 支持模板化 labelannotations 中的标签的值,通过 $labels.变量 可以访问当前告警实例中指定标签的值,$value 则可以获取当前 PromQL 表达式计算的样本值。

为了方便演示,我们将的表达式判断报警临界值设置为 20,重新更新 ConfigMap 资源对象,由于我们在 Prometheus 的 Pod 中已经通过 Volume 的形式将 prometheus-config 这个一个 ConfigMap 对象挂载到了 /etc/prometheus 目录下面,所以更新后,该目录下面也会出现 rules.yml 文件,所以前面配置的 rule_files 路径也是正常的,更新完成后,重新执行 reload 操作,这个时候我们去 Prometheus 的 Dashboard 中切换到 alerts 路径下面就可以看到有报警配置规则的数据了:

alertmanager test rules

页面中出现了我们刚刚定义的报警规则信息,而且报警信息中还有状态显示,一个报警信息在生命周期内有下面 3 种状态:

  • pending: 表示在设置的阈值时间范围内被激活了
  • firing: 表示超过设置的阈值时间被激活了
  • inactive: 报警规则没有得到满足或者已经过期(还没触发或者已经修复)

这里的 pending 状态是说报警规则已经得到满足了,但是持续时间还不够,这里的持续时间是通过配置文件中的 for 来设置的,如果没有设置,那么就不存在 pending 状态,而是报警规则一得到满足就会发送报警,如果设置了这个字段,那么就会进入 pending 状态,并且在持续时间足够的时候,才会发送出去。

同时对于已经 pending 或者 firing 的告警,Prometheus 也会将它们存储到时间序列 ALERTS{} 中。当然我们也可以通过表达式去查询告警实例:

ALERTS{alertname="<alert name>", alertstate="pending|firing", <additional alert labels>}

样本值为1表示当前告警处于活动状态(pending 或者 firing),当告警从活动状态转换为非活动状态时,样本值则为 0。

我们这里的状态现在是 firing 就表示这个报警已经被激活了,我们这里的报警信息有一个 team=node 这样的标签,而最上面我们配置 alertmanager 的时候就有如下的路由配置信息了:

routes:
  - receiver: email
    group_wait: 10s
    match:
      team: node

所以我们这里的报警信息会被 email 这个接收器来进行报警,我们上面配置的是邮箱,所以正常来说这个时候我们会收到一封如下的报警邮件:

alertmanager email receiver

我们可以看到收到的邮件内容中包含一个 View In AlertManager 的链接,我们同样可以通过 NodePort 的形式去访问到 AlertManager 的 Dashboard 页面:

☸ ➜ kubectl get svc -n kube-mon
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
alertmanager   NodePort    10.98.1.195     <none>        9093:31194/TCP      141m

然后通过 <任一Node节点>:31194 进行访问,我们就可以查看到 AlertManager 的 Dashboard 页面,在这个页面中我们可以进行一些操作,比如过滤、分组等等,里面还有两个新的概念:Inhibition(抑制)Silences(静默)

  • Inhibition:如果某些其他警报已经触发了,则对于某些警报,Inhibition 是一个抑制通知的概念。例如:一个警报已经触发,它正在通知整个集群是不可达的时,Alertmanager 则可以配置成关心这个集群的其他警报无效。这可以防止与实际问题无关的数百或数千个触发警报的通知,Inhibition 需要通过上面的配置文件进行配置。
  • Silences:静默是一个非常简单的方法,可以在给定时间内简单地忽略所有警报。Silences 基于 matchers 配置,类似路由树。来到的警告将会被检查,判断它们是否和活跃的 Silences 相等或者正则表达式匹配。如果匹配成功,则不会将这些警报发送给接收者。

何时报警

在 Prometheus 与 Alertmanager 中有很多地方都涉及到报警时机的配置,那么我的报警规则具体会在什么时候报警呢?

首先在 Prometheus 的全局配置中有一个 evaluation_interval 属性,该属性表示的是间隔多长时间对报警规则进行评估,比如我们这里配置的 30s,那么每 30s 会去验证下我们的报警规则是否达到了阈值。

此时我们的报警状态处于 inactive,如果评估后达到了阈值,则会变成 pending 状态,这个时候报警还没有发送给 Alertmanager,什么时候触发需要依赖报警规则中的 for 属性配置了,比如我们配置成 1m,那么也就是在后续 1 分钟内的评估如果都达到了阈值,那么就会变成 pending 状态,并将报警发送给 Alertmanager,后续的操作就是 Alertmanager 来处理了。

所以有的场景下我们的监控图表上面已经有部分指标达到了告警的阈值了,但是并不一定会触发告警规则,比如我们上面的规则中,设置的是 1 分钟的 Pending Duration,对于下图这种情况就不会触发告警,因为持续时间太短,没有达到一分钟:

Pending Duration

报警具体什么时候发送就要看我们的报警路由规则如何配置的了,核心是下面几个属性:

group_by: [instance] # 报警分组
group_wait: 30s # 在组内等待所配置的时间,如果同组内,30秒内出现相同报警,在一个组内出现。
group_interval: 5m # 每个分组中最多每5分钟发送一条警报
repeat_interval: 1h # 发送报警间隔,如果指定时间内没有修复,则重新发送报警。

Alertmanager 会将收到的报警规则按照 group_by 进行分组。当一个报警触发之后,如果之前没有分组,那么就会创建一个分组,当创建完之后会等待 group_wait 这么长的时间才会发送,这里不会马上发送就是需要积攒一定数量的报警,防止报警数量过多,形成报警风暴。

当一个报警触发之后,如果这个报警属于某个 group,但是这个 group 因为距离第一个 alert 已经足够 group_wait 时间了,发送过一次了,那么就会等待 group_interval 时间之后再次发送新的报警 group 了。

所以,如果报警已经从 Prometheus 送到 Alertmanager 了,那么最多可能等待 max(group_wait,group_interval)的时间。

假如一个相同的警报一直 FIRING,Alertmanager 并不会一直发送警报,而会等待一段时间,这个等待时间就是 repeat_interval,显然,不同类型警报的发送频率也是不一样的。

比如现在我们添加两条如下所示的报警规则:

groups:
  - name: test-node-mem
    rules: # 具体的报警规则
      - alert: NodeMemoryUsage # 报警规则的名称
        expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100 > 30
        for: 1m
        labels:
          team: node
        annotations:
          summary: "{{$labels.instance}}: High Memory usage detected"
          description: "{{$labels.instance}}: Memory usage is above 30% (current value is: {{ $value }})"
  - name: test-node-cpu
    rules:
      - alert: NodeCpuUsage
        expr: ((1 - sum(increase(node_cpu_seconds_total{mode="idle"}[1m])) by (instance) / sum(increase(node_cpu_seconds_total[1m])) by (instance) ) * 100) > 5
        for: 2m
        labels:
          team: node
        annotations:
          summary: "{{$labels.instance}}: High CPU usage detected"
          description: "{{$labels.instance}}: CPU usage is above 5% (current value is: {{ $value }})"

并在 Alertmanager 中配置路由的时候设置根据 instance 进行分组:

routes:
  - receiver: email
    group_wait: 10s
    group_by: ["instance"]
    match:
      team: node

正常情况下会先触发 NodeMemoryUsage 这条报警,然后触发 NodeCpuUsage,如果两天报警都属于同一个 instance,则会被分到同一个组中,如下所示:

分组

同一个分组的报警会发送一条,如果组内有新的报警信息出现,则会等待 group_interval 时间再次发送,如果没有新的变化,并且报警信息还是 FIRING 状态,则需要等待 repeat_interval 时间后再次发送。

分组报警