Alertmanager主要负责对Prometheus产生的告警进行统一处理,因此在Alertmanager配置中一般会包含以下几个主要部分:
-
全局配置(global):用于定义一些全局的公共参数,如全局的SMTP配置,Slack配置等内容;
-
模板(templates):用于定义告警通知时的模板,如HTML模板,邮件模板等;
-
告警路由(route):根据标签匹配,确定当前告警应该如何处理;
-
接收人(receivers):接收人是一个抽象的概念,它可以是一个邮箱也可以是微信,Slack或者Webhook等,接收人一般配合告警路由使用;
-
抑制规则(inhibit_rules):合理设置抑制规则可以减少垃圾告警的产生
其完整配置格式如下:
global:
[ resolve_timeout: <duration> | default = 5m ]
[ smtp_from: <tmpl_string> ]
[ smtp_smarthost: <string> ]
[ smtp_hello: <string> | default = "localhost" ]
[ smtp_auth_username: <string> ]
[ smtp_auth_password: <secret> ]
[ smtp_auth_identity: <string> ]
[ smtp_auth_secret: <secret> ]
[ smtp_require_tls: <bool> | default = true ]
[ slack_api_url: <secret> ]
[ victorops_api_key: <secret> ]
[ victorops_api_url: <string> | default = "https://alert.victorops.com/integrations/generic/20131114/alert/" ]
[ pagerduty_url: <string> | default = "https://events.pagerduty.com/v2/enqueue" ]
[ opsgenie_api_key: <secret> ]
[ opsgenie_api_url: <string> | default = "https://api.opsgenie.com/" ]
[ hipchat_api_url: <string> | default = "https://api.hipchat.com/" ]
[ hipchat_auth_token: <secret> ]
[ wechat_api_url: <string> | default = "https://qyapi.weixin.qq.com/cgi-bin/" ]
[ wechat_api_secret: <secret> ]
[ wechat_api_corp_id: <string> ]
[ http_config: <http_config> ]
templates:
[ - <filepath> ... ]
route: <route>
receivers:
- <receiver> ...
inhibit_rules:
[ - <inhibit_rule> ... ]
在全局配置中需要注意的是resolve_timeout
,该参数定义了当Alertmanager持续多长时间未接收到告警后标记告警状态为resolved(已解决)。该参数的定义可能会影响到告警恢复通知的接收时间,读者可根据自己的实际场景进行定义,其默认值为5分钟。
路由匹配
每一个告警都会从配置文件中顶级的route进入路由树,需要注意的是顶级的route必须匹配所有告警(即不能有任何的匹配设置match和match_re),每一个路由都可以定义自己的接受人以及匹配规则。默认情况下,告警进入到顶级route后会遍历所有的子节点,直到找到最深的匹配route,并将告警发送到该route定义的receiver中。
但如果route中设置continue的值为false,那么告警在匹配到第一个子节点之后就直接停止。如果continue为true,报警则会继续进行后续子节点的匹配。如果当前告警匹配不到任何的子节点,那该告警将会基于当前路由节点的接收器配置方式进行处理。
其中告警的匹配有两种方式可以选择。一种方式基于字符串验证,通过设置match规则判断当前告警中是否存在标签labelname并且其值等于labelvalue。第二种方式则基于正则表达式,通过设置match_re验证当前告警标签的值是否满足正则表达式的内容。
如果警报已经成功发送通知, 如果想设置发送告警通知之前要等待时间,则可以通过repeat_interval参数进行设置。
告警分组
在之前的部分有讲过,Alertmanager可以对告警通知进行分组,将多条告警合合并为一个通知。这里我们可以使用group_by来定义分组规则。基于告警中包含的标签,如果满足group_by中定义标签名称,那么这些告警将会合并为一个通知发送给接收器。
有的时候为了能够一次性收集和发送更多的相关信息时,可以通过group_wait参数设置等待时间,如果在等待时间内当前group接收到了新的告警,这些告警将会合并为一个通知向receiver发送。
而group_interval配置,则用于定义相同的Group之间发送告警通知的时间间隔。
例如,当使用Prometheus监控多个集群以及部署在集群中的应用和数据库服务,并且定义以下的告警处理路由规则来对集群中的异常进行通知。
route:
receiver: 'default-receiver'
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
group_by: [cluster, alertname]
routes:
- receiver: 'database-pager'
group_wait: 10s
match_re:
service: mysql|cassandra
- receiver: 'frontend-pager'
group_by: [product, environment]
match:
team: frontend
默认情况下所有的告警都会发送给集群管理员default-receiver,因此在Alertmanager的配置文件的根路由中,对告警信息按照集群以及告警的名称对告警进行分组。
如果告警时来源于数据库服务如MySQL或者Cassandra,此时则需要将告警发送给相应的数据库管理员(database-pager)。这里定义了一个单独子路由,如果告警中包含service标签,并且service为MySQL或者Cassandra,则向database-pager发送告警通知,由于这里没有定义group_by等属性,这些属性的配置信息将从上级路由继承,database-pager将会接收到按cluster和alertname进行分组的告警通知。
而某些告警规则来源可能来源于开发团队的定义,这些告警中通过添加标签team来标示这些告警的创建者。在Alertmanager配置文件的告警路由下,定义单独子路由用于处理这一类的告警通知,如果匹配到告警中包含标签team,并且team的值为frontend,Alertmanager将会按照标签product和environment对告警进行分组。此时如果应用出现异常,开发团队就能清楚的知道哪一个环境(environment)中的哪一个应用程序出现了问题,可以快速对应用进行问题定位。
补充
告警的匹配有两种方式可以选择。
1、一种方式基于字符串验证,通过设置match规则判断当前告警中是否存在标签labelname并且其值等于labelvalue。
2、第二种方式则基于正则表达式,通过设置match_re验证当前告警标签的值是否满足正则表达式的内容。
示例一:根据服务名称匹配
route:
group_by: ['alertname'] #定义分组,根据label标签进行分组
group_wait: 10s #分组等待时间,也就是说在10秒内同一个组中有没有一起报警的,如果有则同时发出报警邮件,如果没有则分开发
group_interval: 10s #告警时间间隔
repeat_interval: 1h #重复告警间隔,也就是触发的一个告警在1h内没有处理则再次发一封邮件。
continue: false #若路由上的continue字段的值为false,则遇到第一个匹配的路由分支后即终止。否则,将继续匹配后续的子节点;
receiver: 'webhook1' #默认邮箱
routes: #启用一个子路由
- receiver: 'webhook1' #接收者为webhook1
group_wait: 10s #分组等待时间
match_re: #匹配一个正则
service: mysql|db #service标签包含mysql和db的统一发送给dba的邮箱
continue: false #若路由上的continue字段的值为false,则遇到第一个匹配的路由分支后即终止。否则,将继续匹配后续的子节点;
- receiver: 'webhook2' #接收者为webhook2
group_wait: 10s #分组时间
match:
serverity: error #将serverity标签值包含error的发送给yunwei的邮箱
continue: false #若路由上的continue字段的值为false,则遇到第一个匹配的路由分支后即终止。否则,将继续匹配后续的子节点;
receivers:
- name: webhook1
webhook_configs:
- url: http://xx.xx.xx.xx:8060/dingtalk/webhook/send
send_resolved: true #警报被解决之后是否通知
- name: webhook2
webhook_configs:
- url: http://xx.xx.xx.xx:8060/dingtalk/webhook1/send
send_resolved: true #警报被解决之后是否通知
示例二:根据告警规则名称匹配 ( rule.yml里面定义的 alert: KubernetesNodeReady)
route:
group_by: ['instance'] #根据 instance 标签分组
continue: true #为true则还需要去匹配子路由。
receiver: receiver-01
routes:
- receiver: 'receiver-01'
match:
alertname: 'InstanceDown' #告警的名字是InstanceDown则发送给receiver-03
- receiver: 'webchat'
match_re:
alertname: 'Cpu.*' #告警的名字以Cpu开头的则发送给webchat
- receiver: 'dingtalk'
match:
alertname: 'InstanceDown' #告警的名字是InstanceDown则发送给dingtalk
receivers:
- name: 'receiver-01'
email_configs:
- to: '1111@qq.com'
- name: 'webchat'
webhook_configs:
- url: 'http://xx.xx.xx.xx:5000'
send_resolved: true
- name: 'dingtalk'
webhook_configs:
- url: 'http://xx.xx.xx.xx:8060/dingtalk/webhook1/send'
send_resolved: true
示例三:同一个告警信息多通道告警发送
route:
group_by: ['alertname']
group_wait: 30s
group_interval: 60s
repeat_interval: 24h
receiver: webchat
routes:
- receiver: wechat
group_wait: 10s
continue: true #当消息发送给微信后,继续匹配,就能把消息在发送到钉钉
- receiver: dingtalk
group_wait: 10s
receivers:
- name: 'wechat'
webhook_configs:
- url: 'http://192.168.11.60:8999/webhook?key=自己的key'
- name: 'dingtalk'
webhook_configs:
- url: 'http://192.168.11.60:8060/dingtalk/webhook1/send'
告警分组
Alertmanager可以对告警通知进行分组,将多条告警合合并为一个通知。这里我们可以使用group_by来定义分组规则。基于告警中包含的标签,如果满足group_by中定义标签名称,那么这些告警将会合并为一个通知发送给接收器。
(alertname = KubernetesNodeReady 比如一条告警下面可能有多个实例挂了,这些都会组合成一条信息发出来)
route:
receiver: 'default-receiver'
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
group_by: [cluster, alertname]
routes:
- receiver: 'database-pager'
group_wait: 10s
match_re:
service: mysql|cassandra
- receiver: 'frontend-pager'
group_by: [product, environment]
match:
team: frontend
webhook-dingtalk配置文件
说明:当 receives 为钉钉时 (webhook_configs),它的告警模板不是在 alertmanager 的配置文件中指定的,而是在钉钉插件 prometheus-webhook-dingtalk 中指定的。
钉钉告警模板如下:
[root@host-monitor webhook-dingtalk]# cat template.tmpl
{{ define "__subject" }}
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}]{{ end }}
{{ define "__text_alert_list" }}{{ range . }}
---
{{ if .Labels.owner }}@{{ .Labels.owner }}{{ end }}
**告警主机:** {{ .Labels.instance }}
**告警级别:** {{ .Labels.severity | upper }}
**告警时间:** {{ dateInZone "2006.01.02 15:04:05" (.StartsAt) "Asia/Shanghai" }}
**事件信息:**
{{ range .Annotations.SortedPairs }} - {{ .Name }}: {{ .Value | markdown | html }}
{{ end }}
**事件标签:**
{{ range .Labels.SortedPairs }}{{ if and (ne (.Name) "severity") (ne (.Name) "summary") }} - {{ .Name }}: {{ .Value | markdown | html }}
{{ end }}{{ end }}
{{ end }}
{{ end }}
{{ define "__text_resolved_list" }}{{ range . }}
---
{{ if .Labels.owner }}@{{ .Labels.owner }}{{ end }}
**告警主机:** {{ .Labels.instance }}
**告警级别:** {{ .Labels.severity | upper }}
**告警时间:** {{ dateInZone "2006.01.02 15:04:05" (.StartsAt) "Asia/Shanghai" }}
**恢复时间:** {{ dateInZone "2006.01.02 15:04:05" (.EndsAt) "Asia/Shanghai" }}
**事件信息:**
{{ range .Annotations.SortedPairs }} - {{ .Name }}: {{ .Value | markdown | html }}
{{ end }}
**事件标签:**
{{ range .Labels.SortedPairs }}{{ if and (ne (.Name) "severity") (ne (.Name) "summary") }} - {{ .Name }}: {{ .Value | markdown | html }}
{{ end }}{{ end }}
{{ end }}
{{ end }}
{{ define "default.title" }}
{{ template "__subject" . }}
{{ end }}
{{ define "default.content" }}
{{ if gt (len .Alerts.Firing) 0 }}
**========侦测到{{ .Alerts.Firing | len }}个故障========**
{{ template "__text_alert_list" .Alerts.Firing }}
{{ end }}
{{ if gt (len .Alerts.Resolved) 0 }}
**========恢复{{ .Alerts.Resolved | len }}个故障========**
{{ template "__text_resolved_list" .Alerts.Resolved }}
{{ end }}
{{ end }}
{{ define "ding.link.title" }}{{ template "default.title" . }}{{ end }}
{{ define "ding.link.content" }}{{ template "default.content" . }}{{ end }}
{{ template "default.title" . }}
{{ template "default.content" . }}