ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

006 Altermanager配置文件详解

Alertmanager简介

警报一直是整个监控系统中的重要组成部分,Prometheus监控系统中,警报是由独立的2部分组成,数据采集(Prometheus)与警报(Alertmanager)是分离的2个组件。警报规则在 Prometheus 定义,警报规则触发以后,才会将信息转发到给独立的组件Alertmanager ,经过 Alertmanager 对警报的信息处理后,最终通过接收器发送给指定用户。

img

Prometheus Server端通过静态或者动态配置去拉取部署在k8s或云主机上的各种类别的监控指标数据,然后基于PromQL 对这些已经存储在本地存储TSDB 中的指标,定义阈值警报规则 Rules 。Prometheus会根据配置的参数周期性的对警报规则进行计算, 如果满足警报条件(告警规则评估结果为True),生产一条警报信息,将其推送到 Alertmanager 组件。

Alertmanager 收到警报信息之后,会对警告信息进行处理,进行 去重分组 Group 并将它们通过定义好的路由 Routing 规则路由到正确的接收器 receiver, 比如 Email Slack 钉钉、企业微信 Robot(webhook) 企业微信等,最终异常事件 WarningError通知给定义好的接收人,其中如钉钉是基于第三方通知来实现的,对于通知人定义是在钉钉的第三方组件中配置。此外,它还负责静默抑制警报。

image-20210521103240431

【总结】

告警能力在Prometheus的架构中被划分成两个独立的部分。

通过在Prometheus中定义AlertRule(告警规则),Prometheus会周期性的对告警规则进行计算,如果满足告警触发条件就会向Alertmanager发送告警信息。

Alertmanager特性

Prometheus 中, 我们不仅仅可以对单条警报通过 PromQL定义规则,更多时候是对相关的多条警报进行分组后统一定义。下面开始把 Alertmanager 中的分组 Grouping 、抑制 Inhibition、静默 Sliences核心特性进行介绍,便于大家系统性的学习与理解。

Alertmanager除了提供基本的告警通知能力以外,还主要提供了如:分组、抑制以及静默等告警特性:

img

分组

在Prometheus中,还可以通过Group(告警组)对一组相关的告警进行统一定义。

分组机制可以将相似的告警信息合并成一个通知。在某些情况下,比如在系统因大面积故障而触发告警潮时,在这种情况下分组机制可以将这些被触发的告警合并为一个告警通知,避免一次性接受大量的告警通知,被大量的告警噪声淹没,进而导致关键信息的隐没,而无法对问题进行快速定位。

例如,当集群中有数百个正在运行的服务实例,并且为每一个实例设置了告警规则。假如此时发生了网络故障,可能导致大量的服务实例无法连接到数据库,结果就会有数百个告警被发送到Alertmanager。

而作为用户,可能只希望能够在一个通知中中就能查看哪些服务实例收到影响。这时可以按照服务所在集群或者告警名称对告警进行分组,而将这些告警聚合在一起成为一个通知。

告警分组,告警时间,以及告警的接受方式可以通过Alertmanager的配置文件进行配置。

抑制

抑制是指当某一告警发出后,可以停止重复发送由此告警引发的其它告警的机制。

当系统中某个组件或服务故障而触发告警通知后,那些依赖于该组件或服务的其它组件或服务可能也会因此而触发告警,抑制便是避免类似的级联告警的一种特性,从而让用户能将精力集中于真正的故障所在;

例如,当一个交换机发生故障后,该故障会触发告警。此时连接到这个交换机上的其他设备也会因为交换机故障而触发报警,而我们不希望收到这些设备发送的告警,此时就可以使用抑制解决。

抑制机制同样通过Alertmanager的配置文件进行设置。

静默

静默 (Silent) :是指在一个特定的时间窗口内,即便接收到告警通知(符合静默的配置),Alertmanager也不会真正向用户发送告警信息的行为;通常,在系统例行维护期间,需要激活告警系统的静默特性;

静默提供了一个简单的机制可以快速根据标签对告警进行静默处理。静默设置需要在Alertmanager的Web页面上进行设置。

Prometheus告警规则【扩展】

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

定义告警规则

在Prometheus中一条告警规则主要由以下几部分组成:

  • 告警名称:用户需要为告警规则命名,当然对于命名而言,需要能够直接表达出该告警的主要内容
  • 告警规则表达式:告警规则由PromQL进行定义,其实际意义是:当表达式(PromQL)查询结果为true时持续多长时间(During)后出发告警

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

groups:
- name: k8stech.net  #该rule分组的名称,每个分组中有多个rule
  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)。**这个group是一个逻辑概念,便于管理功能相近的一组rule,同时为规则名称提供“名称空间”,但并不会对告警rule进行分组发送之类的作用,这点与Altermanager中route group的作用要有所不同。**一条告警规则主要由以下几部分组成:

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

一个组内的每个告警规则必须有个名称,而且在该组内其名称必须唯一。

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

rule_files:
  [ - <filepath_glob> ... ]

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

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

annotations&label模板化

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

  • 通过$labels.变量名可以访问当前告警实例中指定标签的值。
  • $value则可以获取当前PromQL表达式计算的样本值,即时间序列的值。
# To insert a firing element's label values
{{ $labels.<labelname> }}
# To insert the numeric expression value of the firing element:
{{ $value }}

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

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)"

查看告警状态

如下所示,用户可以通过Prometheus WEB界面中的Alerts菜单查看当前Prometheus下的所有告警规则,以及其当前所处的活动状态。

img

同时对于已经pending或者firing的告警,Prometheus也会将它们存储到时间序列ALERTS{}中。

可以通过表达式,查询告警实例:

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

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

image-20210521152556608

实例:定义主机监控告警

修改Prometheus配置文件prometheus.yml,添加以下配置:

rule_files:
  - /etc/prometheus/rules/*.rules

在目录/etc/prometheus/rules/下创建告警文件hoststats-alert.rules内容如下:

groups:
- name: hostStatsAlert
  rules:
  - alert: hostCpuUsageAlert
    expr: sum(avg without (cpu)(irate(node_cpu{mode!='idle'}[5m]))) by (instance) > 0.85
    for: 1m
    labels:
      severity: page
    annotations:
      summary: "Instance {{ $labels.instance }} CPU usgae high"
      description: "{{ $labels.instance }} CPU usage above 85% (current value: {{ $value }})"
  - alert: hostMemUsageAlert
    expr: (node_memory_MemTotal - node_memory_MemAvailable)/node_memory_MemTotal > 0.85
    for: 1m
    labels:
      severity: page
    annotations:
      summary: "Instance {{ $labels.instance }} MEM usgae high"
      description: "{{ $labels.instance }} MEM usage above 85% (current value: {{ $value }})"

重启Prometheus后访问Prometheus UI http://127.0.0.1:9090/rules可以查看当前以加载的规则文件。

img

切换到Alerts标签http://127.0.0.1:9090/alerts可以查看当前告警的活动状态。

img

此时,我们可以手动拉高系统的CPU使用率,验证Prometheus的告警流程,在主机上运行以下命令:

cat /dev/zero>/dev/null

运行命令后查看CPU使用率情况,如下图所示:

img

Prometheus首次检测到满足触发条件后,hostCpuUsageAlert显示有一条告警处于活动状态。由于告警规则中设置了1m的等待时间,当前告警状态为PENDING,如下图所示:

img

如果1分钟后告警条件持续满足,则会实际触发告警并且告警状态为FIRING,如下图所示:

img

Alertmanager配置概述

在Alertmanager中通过路由(Route)来定义告警的处理方式。路由是一个基于标签匹配的树状匹配结构。根据接收到告警的标签匹配相应的处理方式。这里将详细介绍路由相关的内容。

Alertmanager主要负责对Prometheus产生的告警进行统一处理,因此在Alertmanager配置中一般会包含以下几个主要部分:

  • 全局配置(global):用于定义一些全局的公共参数,如全局的SMTP配置,Slack配置等内容;

Alertmanager 配置文件中,只要全局设置配置了的选项,全部为公共设置,可以让其他设置继承,作为默认值,可以子参数中覆盖其设置。其中 resolve_timeout 用于设置处理超时时间,也是声明警报状态为解决的时间, 这个时间会直接影响到警报恢复的通知时间,需要自行结合实际生产场景来设置主机的恢复时间,默认是5分钟。在全局设置中可以设置smtp服务,同时也支持slack、victorops、pagerduty等,在这里只讲我们常用的Email,钉钉,企业微信, 同时也可以自己使用go语言开发的gubot进行二次开发,对接自定义webhook通知源。

  • 模板(templates):用于定义告警通知时的模板,如HTML模板,邮件模板等;

警报模板可以自定义通知的信息格式,以及其包含的对应警报指标数据,可以自定义Email、企业微信的模板,配置指定的存放位置,这里的模板是指的发送的通知源信息格式模板,比如Email,企业微信。这些模板可以在receiver中被引用。

  • 告警路由(route):根据标签匹配,确定当前告警应该如何处理;

警报路由模块描述了在收到 Prometheus 生成的警报后,将警报信息发送给接收器 receiver 指定的目标地址规则。Alertmanager 对传入的警报信息进行处理,根据所定义的规则与配置进行匹配。对于路由可以理解为树状结构, 设置的第一个route是跟节点,往下的就是包含的子节点,每个警报传进来以后,会从配置的跟节点路由进入路由树,按照深度优先从左向右遍历匹配,当匹配的节点后停止,进行警报处理。

  • 接收人(receivers):接收人是一个抽象的概念,它可以是一个邮箱也可以是微信,Slack或者Webhook等,接收人一般配合告警路由使用;
  • 抑制规则(inhibit_rules):合理设置抑制规则可以减少垃圾告警的产生
global:
  [ smtp_from: <tmpl_string> ]
  [ smtp_smarthost: <string> ]
  [ smtp_auth_username: <string> ]
  [ smtp_auth_password: <secret> ]
  [ smtp_auth_secret: <secret> ]
  [ smtp_require_tls: <bool> | default = true ]
  ......

# Files from which custom notification template definitions are read.
# The last component may use a wildcard matcher, e.g. 'templates/*.tmpl'.
templates:
  [ - <filepath> ... ]

# The root node of the routing tree.
route: <route>

# A list of notification receivers.
receivers:
  - <receiver> ...

# A list of inhibition rules.
inhibit_rules:
  [ - <inhibit_rule> ... ]

例如:

global:
  resolve_timeout: 5m
  # smtp配置
  smtp_from: "prom-alert@example.com"
  smtp_smarthost: 'email-smtp.us-west-2.amazonaws.com:465'
  smtp_auth_username: "user"
  smtp_auth_password: "pass"
  smtp_require_tls: true
  
templates:
  - '/data/alertmanager/templates/*.tmpl'
  
route:
  receiver: test1
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  group_by: [alertname]
  routes:
  # ads webhook
  - receiver: test1
    group_wait: 10s
    match:
      team: ads
  # ops webhook
  - receiver: test2
    group_wait: 10s
    match:
      team: operations
      
receivers:
- name: test1
  email_configs:
  - to: '9935226@qq.com'
    headers: { Subject: "[ads] 报警邮件"} # 接收邮件的标题
  webhook_configs:
  - url: http://localhost:8060/dingtalk/ads/send

- name: test2
  email_configs:
  - to: '9935226@qq.com,9935226@example.com'
    send_resolved: true
    headers: { Subject: "[ops] 报警邮件"} # 接收邮件的标题
  webhook_configs:
  - url: http://localhost:8060/dingtalk/ops/send
  # wx config
  wechat_configs:
  - corp_id: 'wwxxxxxxxxxxxxxx'
    api_url: 'https://qyapi.weixin.qq.com/cgi-bin/'
    send_resolved: true
    to_party: '2'
    agent_id: '1000002'
    api_secret: '1FvHxuGbbG35FYsuW0YyI4czWY/.2'

在全局配置中需要注意的是resolve_timeout,该参数定义了当Alertmanager持续多长时间未接收到告警后标记告警状态为resolved(已解决)。该参数的定义可能会影响到告警恢复通知的接收时间,读者可根据自己的实际场景进行定义,其默认值为5分钟。在接下来的部分,我们将以一些实际的例子解释Alertmanager的其它配置内容。

自定义altermanager告警模板

默认情况下Alertmanager使用了系统自带的默认通知模板,模板源码可以从https://github.com/prometheus/alertmanager/blob/master/template/default.tmpl获得。Alertmanager的通知模板基于Go的模板系统。Alertmanager也支持用户定义和使用自己的模板,一般来说有两种方式可以选择。

每一种reciver都有自己的模板,而且有自己的默认模板,也可以为每种reciver自定义模板,但是各种reciver的模板定义方式不太一样。例如钉钉会在webhook-server中定义模板;slack可以直接在reciver定时时定义模板。下面以slack作为示例演示:

第一种,基于模板字符串。用户可以直接在Alertmanager的配置文件中使用模板字符串,例如:

receivers:
- name: 'slack-notifications'
  slack_configs:
  - channel: '#alerts'
    text: 'https://internal.myorg.net/wiki/alerts/{{ .GroupLabels.app }}/{{ .GroupLabels.alertname }}'

第二种方式,自定义可复用的模板文件。例如,可以创建自定义模板文件custom-template.tmpl,如下所示:

{{ define "slack.myorg.text" }}https://internal.myorg.net/wiki/alerts/{{ .GroupLabels.app }}/{{ .GroupLabels.alertname }}{{ end}}

通过在Alertmanager的全局设置中定义templates配置来指定自定义模板的访问路径:

# Files from which custom notification template definitions are read.
# The last component may use a wildcard matcher, e.g. 'templates/*.tmpl'.
templates:
  [ - <filepath> ... ]
receivers:
- name: 'slack-notifications'
  slack_configs:
  - channel: '#alerts'
    text: '{{ template "slack.myorg.text" . }}'

templates:
- '/etc/alertmanager/templates/myorg.tmpl'

基于标签的告警路由

在Alertmanager的配置中会定义一个基于标签匹配规则的告警路由树,以确定在接收到告警后Alertmanager需要如何对其进行处理:

route: <route>

其中route中则主要定义了告警的路由匹配规则,以及Alertmanager需要将匹配到的告警发送给哪一个receiver,一个最简单的route定义如下所示:

route:
  group_by: ['alertname']
  receiver: 'web.hook'
  
receivers:
- name: 'web.hook'
  webhook_configs:
  - url: 'http://127.0.0.1:5001/'

如上所示:在Alertmanager配置文件中,我们只定义了一个路由,那就意味着所有由Prometheus产生的告警在发送到Alertmanager之后都会通过名为web.hook的receiver接收。这里的web.hook定义为一个webhook地址。当然实际场景下,告警处理可不是这么简单的一件事情,对于不同级别的告警,我们可能会不完全不同的处理方式,因此在route中,我们还可以定义更多的子Route,这些Route通过标签匹配告警的处理方式,route的完整定义如下:

[ receiver: <string> ]
[ group_by: '[' <labelname>, ... ']' ]
[ continue: <boolean> | default = false ]

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

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

[ group_wait: <duration> | default = 30s ]
[ group_interval: <duration> | default = 5m ]
[ repeat_interval: <duration> | default = 4h ]

routes:
  [ - <route> ... ]

路由匹配

每一个告警都会从配置文件中顶级的route进入路由树,需要注意的是顶级的route必须匹配所有告警(即不能有任何的匹配设置match和match_re),每一个路由都可以定义自己的接收人以及匹配规则。子路由的配置可以继承自父路由

路由匹配原则:

默认情况下,告警进入到顶级route后会遍历所有的子节点,直到找到最深的匹配route,并将告警发送到该route定义的receiver中。但如果route中设置continue的值为false,那么告警在匹配到第一个子节点之后就直接停止。如果continue为true,报警则会继续进行后续子节点的匹配。如果当前告警匹配不到任何的子节点,那该告警将会基于当前路由节点的接收器配置方式进行处理。

其中告警的匹配有两种方式可以选择:

  • 第一种方式基于字符串完全匹配验证,通过设置match规则判断当前告警中是否存在标签labelname并且其值等于labelvalue。
  • 第二种方式则基于正则表达式,通过设置match_re验证当前告警标签的值是否满足正则表达式的内容。

如果警报已经成功发送通知, 如果想设置重复发送告警通知之前要等待时间,则可以通过repeat_interval参数进行设置。

告警分组

Alertmanager可以对告警通知进行分组,将多条告警合并为一个通知。默认情况下,所有的告警都组织在一起,而一旦指定分组标签,则altermanager就按这些标签进行分组。这里我们可以使用group_by来定义分组规则。基于告警中包含的标签,如果满足group_by中定义标签名称,那么这些告警将会合并为一个通知发送给接收器。例如group_by: [alertname]可以将alertname相同、但是instance标签不同的的多个告警信息合并到一个通知中。

有的时候为了能够一次性收集和发送更多的相关信息时,可以通过group_wait参数设置等待时间,如果在等待时间内当前group接收到了新的告警,这些告警将会合并为一个通知向receiver发送。而group_interval配置,则用于定义相同的Group之间发送告警通知的时间间隔。

分组主要处理流程:

经过路由匹配之后,每一条告警都到达了最终匹配的router那里,在发送前,会对这些即将发送的告警进行分组。(隐含:先路由,后分组,结论可以启用alertmanager的debug级别日志查看分析)

也就是说一个alert到达根路由后,先不考虑根路由和各个子路由的group参数,而是先根据路由树定位该alert最终匹配的子路由,然后根据匹配的子路由中的group参数进行分组,分组只考虑匹配子路由中的group参数,而不考虑其父路由的group参数。除非该匹配的子路由没有group参数,需要从其父路由中继承。

  1. 接收到Alert,根据labels和路由树判断最终属于哪个Route(可存在多个Route,一个Route有多个Group,一个Group有多个Alert)
  2. 在最终定位的route下,将Alert根据该route中的group配置(如果该route配置中没有group,是可以继承自父route)分配到Group中,没有已经存在的Group则新建Group
  3. 新的Group等待group_wait指定的时间(等待时可能收到同一Group的Alert),根据resolve_timeout判断Alert是否解决,然后发送通知
  4. 已有的Group等待group_interval指定的时间,判断Alert是否解决,当上次发送通知到现在的间隔大于repeat_interval或者Group有更新时会发送通知

举例说明:什么情况下会新建分组?各个参数的含义?

alertmanager的分组功能的配置:

route:
  receiver: admin-receiver
  group_by: ["instance"]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

Group By

上面我们设置了group_by ["instance"],那么,当AlertManager接收到如下告警时

{alertname="NodeCPU",instance="node01",job="node-exporter",serverity="high",...}  time, annotation

便会创建一个Group:{instance="node01"},然后把该条告警加入到这个Group中。接着,如果再接收到一条告警

{alertname="NodeMemory",instance="node01",job="node-exporter",serverity="high",...} time, annotation

也会加入到这个Group中 。

如果接收到另一个告警

{alertname="NodeCPU",instance="node02",job="node-exporter",serverity="high",...}  time, annotation

那么便会创建Group {instance="node02"},然后把这条告警放在这个Group中。(注意:只有当一个新的告警到达,且它不属于任何已经存在的分组时,才会创建新的分组

而如果接到了一个告警,它没有instance=xxx这个Label,那么就会创建一个Group {},把这个告警加入到这个Group中。

未设置Group By

上面我们讨论了设置了Group By后,alertmanager如何对告警进行分组。如果我们没有设置Group By,或设置为group_by: [],那么alertmanager便不会对告警进行分组,所有的告警规则都组织在一起。(下面的猜测可能是错误的:也有可能是对每一个告警中的标签创建一个分组,比如说收到一个告警只有标签instance="node01",那就创建一个分组{instance="node01"},收到一个另一个告警有标签instance="node01"alertname="NodeCPU",就创建一个分组{instance="node01",alertname="NodeCPU"})。

Group wait

上面我们提到当设置了Group By后,alertManager会对告警进行分组。当一条告警到达时,如果它不属于任何一个已存在的分组,alertManager会创建一个新的分组,然后将该告警加入到这个分组中。此时,alertManager并不会立即把这个告警发给Receiver,而是会等待group_wait的时间,如果在这个时间里有属于这个分组的其它告警到达,就会将其加入到该组中。直到group_wait时间后,alertManager会把这一组告警一次性发给Receiver。

注意:是创建一个分组后,才会等待group_wait的时间,等待其他属于这个分组的告警加入。当一个分组里面的告警都已经被“解决”(Resolved)后,这些告警与分组都会删掉,如果再来一个告警,则会重新创建分组。

Group Interval

上面提到一个新的Group创建后,要等待group_wait的时间才会发通知给Receiver。当发送了通知以后,它会等待group_interval的时间,在这段时间内到达该Group的告警,会在group_interval后,会作为一个通知发送给Receiver(这个通知中会包含该分组内所有的告警)。

假设group_interval为5分钟,某个分组最后一次发送通知的时间为T,当[T, T+5m]的时间内该分组没有告警到达,而T+6m时该分组内有一个告警到达,那么这个告警会立即被发送给Receiver,而不会等到T+10m才发送。然后把T+6m作为最后一次发送通知的时间。

Repeat Interval

当alertManager为某个分组发送了一个通知后,如果该分组里面的告警依然存在(即没有被“解决”)且在repeat_interval的时间内没有接收到新的告警,那么在等待repeat_interval时间后,alertManager才会为该分组重新发送一个通知。

比如说,repeat_interval为4小时,T时刻alertManager为某个分组发送了最后一个通知,这个通知包含了多个告警。在[T, T+4h]的时间里,该分组没有接收到新的告警,那么在T+4h时alertmanager才会为该分组重新发送一个通知。

例如,当使用Prometheus监控多个集群以及部署在集群中的应用和数据库服务,并且定义以下的告警处理路由规则来对集群中的异常进行通知。

route: #prometheus的告警先是到达alertmanager的根路由(route),alertmanager的根路路由不能包含任何匹配项,因为根路由是所有告警的入口点。另外,根路由需要匹配一个接收器(receiver),用来处理那些没有匹配到任何子路由的告警(如果没有配置子路由,则全部由根路由发送告警)
  receiver: 'default-receiver'
  group_wait: 30s  #当一个新的告警组被创建时,需要等待’group_wait’后才发送初始通知。这样可以确保在发送等待前能收集更多具有相同标签的告警,最后合并为一个通知发送
  group_interval: 5m
  repeat_interval: 4h
  group_by: [cluster, alertname]  #用于分组聚合,对告警通知按标签(label)进行分组,将具有相同标签(所有告警都会有一个默认标签alertname)的告警通知聚合在一个组,然后作为一个通知发送。如果想完全禁用聚合,可以设置为"group_by:[]"
  routes:
  - receiver: 'database-pager'
    group_wait: 10s
    match_re:
      service: mysql|cassandra
  - receiver: 'frontend-pager'
    group_by: [product, environment]  #子路由中,使用product, environment标签分组,分组与父route中的cluster, alertname标签无关。
    match:
      team: frontend

上例中,Prometheus发送过来的所有告警信息会先到达跟路由,然后根据Alertmanager的配置文件的路由树判断是否可以匹配到某个子路由(此时,不考虑根路由中的group_by: [cluster, alertname]分组配置 )。

如果告警时来源于数据库服务如MySQL或者Cassandra,此时则需要将告警发送给相应的数据库管理员(database-pager)。如何实现?这里定义了一个单独子路由,如果告警中包含service标签,并且service标签值为MySQL或者Cassandra,则向database-pager发送告警通知,由于这里没有定义group_by等属性,这些属性的配置信息将从上级路由继承,database-pager这个子路由节点的分组参数为group_by: [cluster, alertname],即将会接收到的告警按照cluster和alertname进行分组的告警通知。

而某些告警规则来源可能来源于开发团队的定义,这些告警中通过添加标签team来标示这些告警的创建者。在Alertmanager配置文件的告警路由下,定义单独子路由用于处理这一类的告警通知,如果匹配到告警中包含标签team,并且team的值为frontend,Alertmanager将会按照标签product和environment对告警进行分组(无视父路由节点中的group_by: [cluster, alertname]配置)。此时如果应用出现异常,开发团队就能清楚的知道哪一个环境(environment)中的哪一个应用程序出现了问题,可以快速对应用进行问题定位。

默认情况下所有没有匹配到子路由的告警都会发送给集群管理员default-receiver,然后按照group_by: [cluster, alertname]进行分组并发送告警。

对这种匹配验证操作灰常考究个人的逻辑思维能力,这不是人干的事情呀~因此,Prometheus发布了一个 Routing tree editor, 用于检测Alertmanager的配置文件结构配置信息,然后路由调试。

使用方法很简单,就是把 alertmanager.yml 的配置信息复制到这个站点,然后点击 Draw Routing Tree 按钮生成路由结构树, 然后在 Match Label Set 前面输入以 {<label name> = "<value>"} 格式的警报标签,然后点击 Match Label Set 按钮会显示发送状态图。

image-20200806112001144

告警接收器Receiver

在Alertmanager中,路由负责对告警信息进行Receiver匹配,并将向告警接收器发送通知。告警接收器可以通过以下形式进行配置:

receivers:
  - <receiver> ...

每一个receiver具有一个全局唯一的名称,并且对应一个或者多个通知方式,包括email、微信、Slack、钉钉等。:

name: <string>
email_configs:
  [ - <email_config>, ... ]
hipchat_configs:
  [ - <hipchat_config>, ... ]
pagerduty_configs:
  [ - <pagerduty_config>, ... ]
pushover_configs:
  [ - <pushover_config>, ... ]
slack_configs:
  [ - <slack_config>, ... ]
opsgenie_configs:
  [ - <opsgenie_config>, ... ]
webhook_configs:
  [ - <webhook_config>, ... ]
victorops_configs:
  [ - <victorops_config>, ... ]

目前官方内置的第三方通知集成包括:邮件、 即时通讯软件(如Slack、Hipchat)、移动应用消息推送(如Pushover)和自动化运维工具(例如:Pagerduty、Opsgenie、Victorops)。Alertmanager的通知方式中还可以支持Webhook,通过这种方式开发者可以实现更多个性化的扩展支持。

Email

Alertmanager默认支持配置Email,也是最普通的警报通知方式,在Alertmanager组件中内置了SMTP协议。直接可以把前面的alertmanager.yml中的SMTP部分截取出来,然后进行调整与配置。

global:
  resolve_timeout: 5m
  # smtp配置
  smtp_from: "9935226@qq.com" # 发送邮件主题
  smtp_smarthost: 'smtp.qq.com:465' # 邮箱服务器的SMTP主机配置
  smtp_auth_username: "9935226@qq.com" # 登录用户名
  smtp_auth_password: "auth_pass" # 此处的auth password是邮箱的第三方登录授权密码,而非用户密码,尽量用QQ来测试。
  smtp_require_tls: false # 有些邮箱需要开启此配置,这里使用的是163邮箱,仅做测试,不需要开启此功能。
route:
  receiver: ops
  group_wait: 30s # 在组内等待所配置的时间,如果同组内,30秒内出现相同报警,在一个组内出现。
  group_interval: 5m # 如果组内内容不变化,合并为一条警报信息,5m后发送。
  repeat_interval: 24h # 发送报警间隔,如果指定时间内没有修复,则重新发送报警。
  group_by: [alertname]  # 报警分组
  routes:
      - match:
          team: operations
        group_by: [env,dc]
        receiver: 'ops'
      - receiver: ops # 路由和标签,根据match来指定发送目标,如果 rule的lable 包含 alertname, 使用 ops 来发送
        group_wait: 10s
        match:
          team: operations
# 接收器指定发送人以及发送渠道
receivers:
# ops分组的定义
- name: ops
  email_configs:
  - to: '9935226@qq.com,xxxxx@qq.com' # 如果想发送多个人就以 ','做分割,写多个邮件人即可。
    send_resolved: true
    headers:
      from: "警报中心"
      subject: "[operations] 报警邮件"
      to: "小煜狼皇"

上面,发件箱相关的信息在global段中配置,也可以在reciver中配置:

receivers:
- name: email
  email_configs:
  - send_resolved: true
    to: 9935226@qq.com
    from: 9935226@qq.com
    hello: localhost
    smarthost: smtp.qq.com:587
    auth_username: 9935226@qq.com
    auth_password: <secret>
    headers:
      From: 9935226@qq.com
      Subject: '{{ template "rancher.title" . }}'
      To: 9935226@qq.com
    html: '{{ template "email.text" . }}'
    require_tls: true

配置完成后,直接重启Alertmanager组件,使配置生效,然后使用前面node-down触发一次警报来看下发送结果。

收到的警报信息:

img

当警报接触以后收到的恢复信息。

img

企业微信

首先你要具有企业微信管理员的权限,如果没有可以自己注册一个,进行测试,我这里有自行注册的企业微信

第一步登录进入以后,在应用管理中新建应用。

企业微信应用

第二步,创建应用,信息填写如下,上传应用logo随意。

企业微信应用信息

创建成功以后如下图。

微信应用信息

这时候需要把 AgentIdSecret 记录下来,对于你的这种Secret信息,最好管理好,我的用过就会删除,所以不用担心安全隐患。

IDkey
AgentId1000004
SecretF-fzpgsabmfiFt7_4QRQwWEl8eyx7evO12sRYe_Q5vA

第三步,现在我们来用新建的企业微信应用在Alertmanager配置,可以配置全局,也可以对单独需要发送的接收器,因为警报需要分级,所以需要单独处理,在这里使用的的单独的配置,需要知道 企业ID ,以及 部门ID

企业ID 通过企业信息获取

企业ID

部门ID 通过通讯录获取

部门ID

# 企业微信配置
  wechat_configs:
  - corp_id: 'wwxxxxx' # 企业ID是唯一标识
    api_url: 'https://qyapi.weixin.qq.com/cgi-bin/' # 企业微信api接口,统一定义
    send_resolved: true # 设置发送警报恢复信息
    to_party: '2' # 部门id,比如我的叫警报组,因此显示的是2,如果你DB组,就可能会是3,WEB组就是4,依次类推,另外需要接收警报的相关人员必须在这个部门里。
    agent_id: '1000004' # 新建应用的agent_id
    api_secret: 'F-fzpgsabmfiFt7_4QRQwWEl8eyx7evO12sRYe_Q5vA' # 新建应用的Secret

这时候我们重启Alertmanager,然后使用之前的方式来触发模拟警报,看看发送是不是已经没有问题了,这时我们的企业微信中、Email都可以收到警报了,这里的警报已经被我用模块处理过了。可读性会更高。

cat wechat.tmpl
## wechat模板
{{ define "wechat.default.message" }}
{{ if gt (len .Alerts.Firing) 0 -}}
Alerts Firing:
{{ range .Alerts }}
警报级别:{{ .Labels.status }}

警报类型:{{ .Labels.alertname }}

故障主机: {{ .Labels.instance }}

警报主题: {{ .Annotations.summary }}

警报详情: {{ .Annotations.description }}

⏱ : {{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}
{{- end }}
{{- end }}

{{ if gt (len .Alerts.Resolved) 0 -}}
Alerts Resolved:
{{ range .Alerts }}
警报级别:{{ .Labels.status }}

警报类型:{{ .Labels.alertname }}

故障主机: {{ .Labels.instance }}

警报主题: {{ .Annotations.summary }}

警报详情: {{ .Annotations.description }}

⏱ : {{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}
⏲ : {{ (.EndsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}
{{- end }}
{{- end }}
{{- end }}

Firing警报:

企业微信警报信息

下面是Resolve的警报:

企业恢复信息

钉钉机器人(Webhook)

对于钉钉大家都已经很熟悉了,大部分企业都已经启用钉钉办公了,同时其推出的免费的webhook机器人也很受大家的欢迎。我们这里讲一下借助第三方开源组件如何对钉钉集成警报功能。

首先需要在钉钉创建机器人,然后在白名单中添加关键字信息与ip限制等安全设置,这个只要你有群,你就可以在群里面建,非常简单,这里就不做演示了

先把Prometheus-webhook-Dingtalk组件装好。

mkdir -p /etc/prometheus-webhook-dingtalk/template/
cd /etc/prometheus-webhook-dingtalk/
wget https://github.com/timonwong/prometheus-webhook-dingtalk/releases/download/v1.4.0/prometheus-webhook-dingtalk-1.4.0.linux-amd64.tar.gz
tar xf prometheus-webhook-dingtalk-1.4.0.linux-amd64.tar.gz
mv prometheus-webhook-dingtalk-1.4.0.linux-amd64/* /etc/prometheus-webhook-dingtalk/
mv prometheus-webhook-dingtalk /bin/
cat <<EOF> /lib/systemd/system/prometheus-webhook-dingtalk.service 
[Unit]
Description=prometheus-webhook-dingding
Documentation=https://prometheus.io/
After=network.target

[Service]
Type=simple
User=prometheus
ExecStart=/bin/prometheus-webhook-dingtalk --web.listen-address=":8070" --web.enable-ui --config.file="/etc/prometheus-webhook-dingtalk/config.yml"
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF
## 启动服务
systemctl enable prometheus-webhook-dingtalk.service
systemctl start prometheus-webhook-dingtalk.service

配置文件

## Request timeout
# timeout: 5s

## Customizable templates path
## Customizable templates path
templates:
  # - contrib/templates/legacy/template.tmpl
  # 自定义模板路径
  - /etc/prometheus-webhook-dingtalk/template/default.tmpl

## 你还可以使用' default_message '覆盖默认模板
## 下面的示例使用v0.3.0中的“legacy”模板
# default_message:
#   title: '{{ template "legacy.title" . }}'
#   text: '{{ template "legacy.content" . }}'

## Targets, previously was known as "profiles"
# 定义的webhook,钉钉创建的webhook token
targets:
# 如果有多个分组就可以在这里定义多个接口
  ops:
    url: https://oapi.dingtalk.com/robot/send?access_token=a4feed2322222222222222222222222
  web:
    url: https://oapi.dingtalk.com/robot/send?access_token=a4feed2325c1333333333333333333333

定义模板:

cd /etc/prometheus-webhook-dingtalk/template
cat default.tmpl
{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }}
{{ define "__alertmanagerURL" }}{{ .ExternalURL }}/#/alerts?receiver={{ .Receiver }}{{ end }}

{{ define "__text_alert_list" }}{{ range . }}
**Labels**
{{ range .Labels.SortedPairs }}> - {{ .Name }}: {{ .Value | markdown | html }}
{{ end }}
**Annotations**
{{ range .Annotations.SortedPairs }}> - {{ .Name }}: {{ .Value | markdown | html }}
{{ end }}
**Source:** [{{ .GeneratorURL }}]({{ .GeneratorURL }})
{{ end }}{{ end }}

{{/* Firing */}}

{{ define "default.__text_alert_list" }}{{ range . }}

**Trigger Time:** {{ dateInZone "2006.01.02 15:04:05" (.StartsAt) "Asia/Shanghai" }}

**Summary:** {{ .Annotations.summary }}

**Description:** {{ .Annotations.description }}

**Graph:** [📈 ]({{ .GeneratorURL }})

**Details:**
{{ range .Labels.SortedPairs }}{{ if and (ne (.Name) "severity") (ne (.Name) "summary") }}> - {{ .Name }}: {{ .Value | markdown | html }}
{{ end }}{{ end }}
{{ end }}{{ end }}

{{/* Resolved */}}

{{ define "default.__text_resolved_list" }}{{ range . }}

**Trigger Time:** {{ dateInZone "2006.01.02 15:04:05" (.StartsAt) "Asia/Shanghai" }}

**Resolved Time:** {{ dateInZone "2006.01.02 15:04:05" (.EndsAt) "Asia/Shanghai" }}

**Summary:** {{ .Annotations.summary }}

**Graph:** [📈 ]({{ .GeneratorURL }})

**Details:**
{{ range .Labels.SortedPairs }}{{ if and (ne (.Name) "severity") (ne (.Name) "summary") }}> - {{ .Name }}: {{ .Value | markdown | html }}
{{ end }}{{ end }}
{{ end }}{{ end }}

{{/* Default */}}
{{ define "default.title" }}{{ template "__subject" . }}{{ end }}
{{ define "default.content" }}#### \[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}\] **[{{ index .GroupLabels "alertname" }}]({{ template "__alertmanagerURL" . }})**
{{ if gt (len .Alerts.Firing) 0 -}}

![Firing-img](https://is3-ssl.mzstatic.com/image/thumb/Purple20/v4/e0/23/cf/e023cf56-0623-0cdf-afce-97ae90eabfda/mzl.uplmrpgi.png/320x0w.jpg)

**Alerts Firing**
{{ template "default.__text_alert_list" .Alerts.Firing }}
{{- end }}
{{ if gt (len .Alerts.Resolved) 0 -}}

![Resolved-img](https://is3-ssl.mzstatic.com/image/thumb/Purple18/v4/41/72/99/4172990a-f666-badf-9726-6204a320c16e/mzl.dypdixoy.png/320x0w.png)

**Alerts Resolved**
{{ template "default.__text_resolved_list" .Alerts.Resolved }}
{{- end }}
{{- end }}

{{/* Legacy */}}
{{ define "legacy.title" }}{{ template "__subject" . }}{{ end }}
{{ define "legacy.content" }}#### \[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}\] **[{{ index .GroupLabels "alertname" }}]({{ template "__alertmanagerURL" . }})**
{{ template "__text_alert_list" .Alerts.Firing }}
{{- end }}

{{/* Following names for compatibility */}}
{{ define "ding.link.title" }}{{ template "default.title" . }}{{ end }}
{{ define "ding.link.content" }}{{ template "default.content" . }}{{ end }}

在Alertmanager中配置警报

# 接收器指定发送人以及发送渠道
receivers:
# ops分组的定义
- name: ops
  email_configs:
  - to: '9935226@qq.com,10000@qq.com'
    send_resolved: true
    headers: { Subject: "[operations] 报警邮件"} # 接收邮件的标题
  # 钉钉配置
  webhook_configs:
  - url: http://localhost:8070/dingtalk/ops/send # 这里是在钉钉开源组件中的接口,如果单独定义的receiver需要对应你的分组与钉钉机器人的webhook token
    # 企业微信配置
  wechat_configs:
  - corp_id: 'ww5421dksajhdasjkhj'
    api_url: 'https://qyapi.weixin.qq.com/cgi-bin/'
    send_resolved: true
    to_party: '2'
    agent_id: '1000002'
    api_secret: 'Tm1kkEE3RGqVhv5hO-khdakjsdkjsahjkdksahjkdsahkj'
# web
- name: web
  email_configs:
  - to: '9935226@qq.com'
    send_resolved: true
    headers: { Subject: "[web] 报警邮件"} # 接收邮件的标题
  webhook_configs:
  - url: http://localhost:8070/dingtalk/web/send

继续使用上面的触发模拟警报,此时会同时让三个警报都接受到警报信息,我们这里只是为了调试,往往一个警报通知就可以满足需求了,对于重要业务还是需要使用电话以及短信提醒。

钉钉Firing警报:

钉钉警报信息

钉钉Resolve警报:

钉钉恢复信息

警报通知模板

Prometheus 创建警报转发给 Alertmanager,Alertmanager会根据不同的 Label 向不同的 Receiver 发送警报通知,如Email、钉钉、企业微信、飞书、短信等等。所有Receiver都一个接收模板,然后通过模板格式化以后发送警报信息给 Receiver。Alertmanager 自带的模板是基于 Go 语言的 template 模板,用户可以根据自己的需求去定义自己需要的模板,上面我给出的模板已经足够大家的基础使用了。

下面介绍下通常自定义模板中会需要用到的一些参数说明

名称数据类型描述
Receiverstring接受警报通知的接收器名称
Statusstring警报状态,例如:Firing或Resolved的通知
AlertAlert警报通知的真实内容,警报中的所有列表
GroupLablesKV包含警报通知的组标签
CommandLabelsKV所有警报的公共标签,包含GroupLabels的所有标签
CommandAnnotationsKV注释,比如自定义的一些字符串
ExternalURLstring警报信息中的Alertmanager地址

上面说的KV类型是一组使用不标示标签与注释的Key/Value字符串对,可以在Alertmanager中的默认模板中看到其定义。default.tmpl

其中邮件中所显示的 View In AlertManager ,Receiver 与 ExternalURL的定义其实就是模板中的 .ExternalURL.Receiver

{{ define "__alertmanager" }}AlertManager{{ end }}

{{ define "__alertmanagerURL" }}{{ .ExternalURL }}/#/alerts?receiver={{ .Receiver | urlquery }}{{ end }}
...

在收到的邮箱警报中可以看到 View In AlertManager 的链接地址是:http://192.168.1.220:19093/#/alerts?receiver=ops

对于Alert的类型,警报列表的字段还包含了如下参数与定义、描述

名称数据类型描述
Statusstring定义警报状态是已经解决或处于触发状态
LabelKV包含警报中的标签
AnnotationsKV警报的一组注释
StartsAttime.Time警报触发时间
EndsAttime.Time警报结束时间,只在警报结束的时间时设置
GeneratorURLstring警报规则的连接URL,也就是Prometheus中的Rules查询地址

对于警报中的通知模板首先要熟悉go语言的template语法以及HTML简单的基础知识,然后把上面相关的元数据的一些信息了解清楚,就可以自己调整模板了,如果你实在懒的改,我调整好的模板可以直接拿去用,把对应的指标、标签名字改一下就可以用了。

以下是我自己修改了一下的模板警报格式,大家可以看看,这个是通过官方的 default.tmpl 修改的。

Email模板警报信息

开源警报组件推荐

  • Prometheus-Webhook-Dingtalk

一个开源的第三方警报插件,针对钉钉机器人 webhook 做集成,Go语言编写,现在迭代的已经很不错了,可能有一些功能还是有些限制,比如针对 Markdown @某个人无法实现,原因是因钉钉自身API没有支持这个功能。

  • Alertmanager-wechatrobot-webhook

这个开源组件是将Alertmanger Webhook 消息转换为可以接收消息的企业微信机器人,也是go语言编写,Alertmanager 默认已经集成企业微信配置,如果有特殊需求,需要使用企业微信机器人的可以看看这个。

  • PrometheusAlert全家桶

如果有对短信、电话警报等其他需求的同学,推荐这个开源警报组件,Go语言编写,Web框架是 Beego ,支持将收到的这些消息发送到钉钉,微信,飞书,腾讯短信,腾讯电话,阿里云短信,阿里云电话,华为短信,容联云电话等,这里就不细讲了。

img

告警通知屏蔽

Alertmanager提供了方式可以帮助用户控制告警通知的行为,包括预先定义的抑制机制临时定义的静默规则

抑制机制(inhibit_rules)

Alertmanager的抑制机制可以避免当某种问题告警产生之后用户接收到大量由此问题导致的一系列的其它告警通知。

例如,当集群不可用时,用户可能只希望接收到一条告警,告诉他这时候集群出现了问题,而不是大量的如集群中的应用异常、中间件服务异常的告警通知。

再例如,IDC托管机柜中,若每一个机柜接入层仅仅是单台交换机,那么该机柜接入交换机故障会造成机柜中服务器非 up 状态警报。再有服务器上部署的应用服务不可访问也会触发警报。这时候,可以通过在Alertmanager配置忽略由于交换机故障而造成的此机柜中的所有服务器及其应用不可达而产生的警报。

在Alertmanager配置文件中,使用inhibit_rules定义一组告警的抑制规则:

inhibit_rules:
  [ - <inhibit_rule> ... ]

每一条抑制规则的具体配置如下:

target_match:
  [ <labelname>: <labelvalue>, ... ]
target_match_re:
  [ <labelname>: <regex>, ... ]

source_match:
  [ <labelname>: <labelvalue>, ... ]
source_match_re:
  [ <labelname>: <regex>, ... ]

[ equal: '[' <labelname>, ... ']' ]

如果已经发送的告警通知匹配到source_matchtarget_match和source_match_re规则,当有新的告警规则如果满足target_match或者target_match_re定义的匹配规则,并且已发送的告警与新产生的告警中equal定义的标签完全相同,则启动抑制机制,新的告警不会发送。

source_match匹配到的告警会抑制target_match匹配到的告警。

例如,定义如下抑制规则:

# 同一个主机上的服务,如果是因为nodedown导致的critical告警,则会被抑制。
- source_match:
    alertname: NodeDown
    severity: critical
  target_match:
    severity: critical
  equal:
    - node

例如当集群中的某一个主机节点异常宕机导致告警NodeDown被触发,同时在告警规则中定义了告警级别severity=critical。由于主机异常宕机,该主机上部署的所有服务、中间件会不可用并触发报警。根据抑制规则的定义,如果有新的告警级别为severity=critical,并且告警中标签node的值与NodeDown告警的相同,则说明新的告警是由NodeDown导致的,则启动抑制机制停止向接收器发送通知。

例如:在多k8s监控的场景中,对于同一个k8s集群(cluster)中的同一个服务(service)的相同告警(alertname),如果critical级别的告警已经触发了,warning级别的报警就会抑制。

    inhibit_rules:
    - source_match:
        severity: 'critical'
      target_match:
        severity: 'warning'
      equal: ['alertname', 'cluster', 'service']

临时静默处理

除了基于抑制机制可以控制告警通知的行为以外,用户或者管理员还可以直接通过Alertmanager的UI临时屏蔽特定的告警通知(例如维护期间,可以静默告警。将告警静默后,就不会发送邮件等告警通知了)。

Silences 提供了一个简单的机制,根据标签快速对警报进行静默处理;对传进来的警报进行匹配检查,如果接收到的警报符合静默的配置,Alertmanager则不会向receiver发送警报通知。

静默操作不能在Alertmanager配置文件中配置,静默是需要在WEB UI界面中设置临时屏蔽指定,或使用amtool命令行进行配置。

进入Alertmanager UI,点击”New Silence”显示如下内容:

img

用户可以通过该UI定义新的静默规则的开始时间以及持续时间,通过Matchers部分可以设置多条匹配规则(字符串匹配或者正则匹配)。填写当前静默规则的创建者以及创建原因后,点击”Create”按钮即可。

通过”Preview Alerts”可以查看预览当前匹配规则匹配到的告警信息。静默规则创建成功后,Alertmanager会开始加载该规则并且设置状态为Pending,当规则生效后则进行到Active状态。

img

当静默规则生效以后,从Alertmanager的Alerts页面下用户将不会看到该规则匹配到的告警信息。

img

对于已经生效的规则,用户可以通过手动点击”Expire“按钮使当前规则过期。

此外,临时静默也可以通过命令行进行配置:

image-20200630152545648

使用Recoding Rules优化性能

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

定义Recoding rules

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

rule_files:
  [ - <filepath_glob> ... ]

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

groups:
  [ - <rule_group> ]

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

groups:
  - name: example
    rules:
    - record: job:http_inprogress_requests:sum
      expr: sum(http_inprogress_requests) by (job)

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

# 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。

# 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定义:

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

alertmanager配置文件完整示例

示例1:官方示例

global:
  # The smarthost and SMTP sender used for mail notifications.
  smtp_smarthost: 'localhost:25'
  smtp_from: 'alertmanager@example.org'
  smtp_auth_username: 'alertmanager@example.org'
  smtp_auth_password: xxx
  smtp_require_tls: false  #默认为true

#根路由必须没有任何匹配器,因为它是所有警报的入口点。 它需要配置一个接收器,以便将与任何子路由都不匹配的警报发送给某人。
#默认路由
route:
  receiver: 'team-X-mails'

  # The labels by which incoming alerts are grouped together. For example,
  # multiple alerts coming in for cluster=A and alertname=LatencyHigh would
  # be batched into a single group.
  # 分组标签
  group_by: ['alertname', 'cluster']

  # When a new group of alerts is created by an incoming alert, wait at
  # least 'group_wait' to send the initial notification.
  # This way ensures that you get multiple alerts for the same group that start
  # firing shortly after another are batched together on the first
  # notification.
  group_wait: 30s

  # When the first notification was sent, wait 'group_interval' to send a batch
  # of new alerts that started firing for that group.
  group_interval: 5m

  # If an alert has successfully been sent, wait 'repeat_interval' to
  # resend them.
  repeat_interval: 3h

  # All the above attributes are inherited by all child routes and can
  # overwritten on each.

  # The child route trees.
  routes:
  # This routes performs a regular expression match on alert labels to
  # catch alerts that are related to a list of services.
  - match_re:
      service: ^(foo1|foo2|baz)$
    receiver: team-X-mails

    # The service has a sub-route for critical alerts, any alerts
    # that do not match, i.e. severity != critical, fall-back to the
    # parent node and are sent to 'team-X-mails'
    routes:
    - match:
        severity: critical
      receiver: team-X-pager

  - match:
      service: files
    receiver: team-Y-mails

    routes:
    - match:
        severity: critical
      receiver: team-Y-pager

  # This route handles all alerts coming from a database service. If there's
  # no team to handle it, it defaults to the DB team.
  - match:
      service: database

    receiver: team-DB-pager
    # Also group alerts by affected database.
    group_by: [alertname, cluster, database]

    routes:
    - match:
        owner: team-X
      receiver: team-X-pager

    - match:
        owner: team-Y
      receiver: team-Y-pager


# Inhibition rules allow to mute a set of alerts given that another alert is
# firing.
# We use this to mute any warning-level notifications if the same alert is
# already critical.
inhibit_rules:
- source_match:
    severity: 'critical'
  target_match:
    severity: 'warning'
  # Apply inhibition if the alertname is the same.
  # 必须在源警报和目标警报中具有相等值的标签才能使抑制生效。
  # CAUTION: 
  #   If all label names listed in `equal` are missing 
  #   from both the source and target alerts,
  #   the inhibition rule will apply!
  equal: ['alertname']  


receivers:
- name: 'team-X-mails'
  email_configs:
  - to: 'team-X+alerts@example.org, team-Y+alerts@example.org'

- name: 'team-X-pager'
  email_configs:
  - to: 'team-X+alerts-critical@example.org'
  pagerduty_configs:
  - routing_key: <team-X-key>

- name: 'team-Y-mails'
  email_configs:
  - to: 'team-Y+alerts@example.org'

- name: 'team-Y-pager'
  pagerduty_configs:
  - routing_key: <team-Y-key>

- name: 'team-DB-pager'
  pagerduty_configs:
  - routing_key: <team-DB-key>

示例2:案例配置

## Alertmanager 配置文件
global:
  resolve_timeout: 5m
  # smtp配置
  smtp_from: "prom-alert@example.com"
  smtp_smarthost: 'email-smtp.us-west-2.amazonaws.com:465'
  smtp_auth_username: "user"
  smtp_auth_password: "pass"
  smtp_require_tls: true
# email、企业微信的模板配置存放位置,钉钉的模板会单独讲如果配置。
templates:
  - '/data/alertmanager/templates/*.tmpl'
# 路由分组
route:
  receiver: ops  #默认的receiver,没有被路由匹配到的告警使用该receiver
  group_wait: 30s # 在组内等待所配置的时间,如果同组内,30秒内出现相同报警,在一个组内出现。
  group_interval: 5m # 如果组内内容不变化,合并为一条警报信息,5m后发送。
  repeat_interval: 24h # 发送报警间隔,如果指定时间内没有修复,则重新发送报警。
  group_by: [alertname]  # 报警分组
  routes:
  # ops
  - match:
      team: operations
    receiver: ops # 路由和标签,根据match来指定发送目标,如果 rule的lable包含"team=operations", 使用ops来发送
    group_wait: 10s
    group_by: [env,dc]
  # db
  - receiver: db # 路由和标签,根据match来指定发送目标,如果 rule的lable包含"team=db", 使用"db"来发送
    group_wait: 10s
    match:
      team: db
  - match_re:
      service: nginx|apache
    receiver: 'web'  # 分组继承父极路由,即 group_by: [alertname]
  - match_re:
      service: hbase|spark
    receiver: 'hadoop'
  - match_re:
      serice: mysql|mongodb
    receiver: 'db'
  - receiver: ops
    group_wait: 10s
    match:
      status: 'High'
      
# 接收器 :指定发送人以及发送渠道
receivers:
# ops分组的定义,使用了多种通知工具:email、钉钉、wcaht
- name: ops
  # email
  email_configs:
  - to: '9935226@qq.com,10000@qq.com'
    send_resolved: true
    headers: 
      subject: "[operations] 报警邮件"
      from: "警报中心"
      to: "小煜狼皇" 
  # 钉钉配置
  webhook_configs:
  - url: http://localhost:8070/dingtalk/ops/send
  # 企业微信配置
  wechat_configs:
  - corp_id: 'ww5421dksajhdasjkhj'
    api_url: 'https://qyapi.weixin.qq.com/cgi-bin/'
    send_resolved: true
    to_party: '2'
    agent_id: '1000002'
    api_secret: 'Tm1kkEE3RGqVhv5hO-khdakjsdkjsahjkdksahjkdsahkj'

# web
- name: web
  email_configs:
  - to: '9935226@qq.com'
    send_resolved: true
    headers: { Subject: "[web] 报警邮件"} # 接收邮件的标题
  webhook_configs:
  - url: http://localhost:8070/dingtalk/web/send
  - url: http://localhost:8070/dingtalk/ops/send
  
# db
- name: db
  email_configs:
  - to: '9935226@qq.com'
    send_resolved: true
    headers: { Subject: "[db] 报警邮件"} # 接收邮件的标题
  webhook_configs:
  - url: http://localhost:8070/dingtalk/db/send
  - url: http://localhost:8070/dingtalk/ops/send
  
# hadoop
- name: hadoop
  email_configs:
  - to: '9935226@qq.com'
    send_resolved: true
    headers: { Subject: "[hadoop] 报警邮件"} # 接收邮件的标题
  webhook_configs:
  - url: http://localhost:8070/dingtalk/hadoop/send
  - url: http://localhost:8070/dingtalk/ops/send

# 抑制器配置
inhibit_rules: # 抑制规则
  - source_match: # 源标签警报触发时抑制含有目标标签的警报,在当前警报匹配 status: 'High'
      status: 'High'  # 此处的抑制匹配一定在最上面的route中配置不然,会提示找不key。
    target_match:
      status: 'Warning' # 目标标签值正则匹配,可以是正则表达式如: ".*MySQL.*"
    equal: ['alertname','operations', 'instance'] # 确保这个配置下的标签内容相同才会抑制,也就是说警报中必须有这三个标签值才会被抑制。

示例3:简单配置

global:
  resolve_timeout: 5m
route:
  receiver: webhook
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  group_by: [alertname]
  routes:
  - receiver: webhook
    group_wait: 10s
    match:
      team: node
receivers:
- name: webhook
  webhook_configs:
  - url: 'http://apollo/hooks/dingtalk/'
    send_resolved: true
  - url: 'http://apollo/hooks/prome/'
    send_resolved: true

示例4:rancher

global:
  resolve_timeout: 5m
  http_config: {}
  smtp_hello: localhost
  slack_api_url: <secret>
  pagerduty_url: https://events.pagerduty.com/v2/enqueue
  hipchat_api_url: https://api.hipchat.com/
  opsgenie_api_url: https://api.opsgenie.com/
  wechat_api_url: https://qyapi.weixin.qq.com/cgi-bin/
  victorops_api_url: https://alert.victorops.com/integrations/generic/20131114/alert/
route:
  receiver: rancherlabs
  routes:
  - receiver: c-4cvw9:etcd-alert
    group_by:
    - group_id
    match:
      group_id: c-4cvw9:etcd-alert
    continue: true
    routes:
    - group_by:
      - rule_id
      - component_name
      match:
        rule_id: c-4cvw9:etcd-alert_etcd-system-service
      continue: true
      group_wait: 10m
      group_interval: 3m
      repeat_interval: 1h
    - group_by:
      - rule_id
      match:
        rule_id: c-4cvw9:etcd-alert_no-leader
      continue: true
    - group_by:
      - rule_id
      match:
        rule_id: c-4cvw9:etcd-alert_db-over-size
      continue: true
    - group_by:
      - rule_id
      match:
        rule_id: c-4cvw9:etcd-alert_high-number-of-leader-changes
      continue: true
    group_wait: 3m
    group_interval: 3m
    repeat_interval: 1h
  - receiver: c-4cvw9:kube-components-alert
    group_by:
    - group_id
    match:
      group_id: c-4cvw9:kube-components-alert
    continue: true
    routes:
    - group_by:
      - rule_id
      - component_name
      match:
        rule_id: c-4cvw9:kube-components-alert_controllermanager-system-service
      continue: true
    - group_by:
      - rule_id
      - component_name
      match:
        rule_id: c-4cvw9:kube-components-alert_scheduler-system-service
      continue: true
    group_wait: 3m
    group_interval: 3m
    repeat_interval: 1h
  - receiver: c-4cvw9:node-alert
    group_by:
    - group_id
    match:
      group_id: c-4cvw9:node-alert
    continue: true
    routes:
    - group_by:
      - rule_id
      match:
        rule_id: c-4cvw9:node-alert_high-cpu-load
      continue: true
    - group_by:
      - rule_id
      match:
        rule_id: c-4cvw9:node-alert_node-disk-running-full
      continue: true
    - group_by:
      - rule_id
      match:
        rule_id: c-4cvw9:node-alert_high-memmory
      continue: true
    group_wait: 3m
    group_interval: 3m
    repeat_interval: 1h
  group_wait: 1m
  group_interval: 10s
  repeat_interval: 1h
receivers:
- name: rancherlabs
  slack_configs:
  - send_resolved: false
    http_config: {}
    api_url: <secret>
    channel: '#alert'
    username: '{{ template "slack.default.username" . }}'
    color: '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}'
    title: '{{ template "slack.default.title" . }}'
    pretext: '{{ template "slack.default.pretext" . }}'
    text: '{{ template "slack.default.text" . }}'
    footer: '{{ template "slack.default.footer" . }}'
    fallback: '{{ template "slack.default.fallback" . }}'
    callback_id: '{{ template "slack.default.callbackid" . }}'
    icon_emoji: '{{ template "slack.default.iconemoji" . }}'
    icon_url: '{{ template "slack.default.iconurl" . }}'
- name: c-4cvw9:etcd-alert
  email_configs:
  - send_resolved: true
    to: 9935226@qq.com
    from: 9935226@qq.com
    hello: localhost
    smarthost: smtp.qq.com:587
    auth_username: 9935226@qq.com
    auth_password: <secret>
    headers:
      From: 9935226@qq.com
      Subject: '{{ template "rancher.title" . }}'
      To: 9935226@qq.com
    html: '{{ template "email.text" . }}'
    require_tls: true
- name: c-4cvw9:kube-components-alert
  webhook_configs:
  - send_resolved: true
    http_config: {}
    url: http://webhook-dingtalk.cattle-prometheus:8060/dingtalk/webhook1/send
- name: c-4cvw9:node-alert
  webhook_configs:
  - send_resolved: true
    http_config: {}
    url: http://webhook-dingtalk.cattle-prometheus:8060/dingtalk/webhook1/send
templates:
- /etc/alertmanager/config/notification.tmpl

alertmanager集群高可用部署

集群高可用

参考前面章节二进制部署部分。

参考文档:

https://www.kancloud.cn/pshizhsysu/prometheus/1803808