告警能力在Prometheus的架构中被划分成两个独立的部分。如下所示,通过在Prometheus中定义AlertRule(告警规则),Prometheus会周期性的对告警规则进行计算,如果满足告警触发条件就会向Alertmanager发送告警信息。
在Prometheus中一条告警规则主要由以下几部分组成:
告警名称:用户需要为告警规则命名,当然对于命名而言,需要能够直接表达出该告警的主要内容 告警规则:告警规则实际上主要由PromQL进行定义,其实际意义是当表达式(PromQL)查询结果持续多长时间(During)后出发告警
在Prometheus中,还可以通过Group(告警组)对一组相关的告警进行统一定义。当然这些定义都是通过YAML文件来统一管理的。
Prometheus中的告警规则允许你基于PromQL表达式定义告警触发条件,Prometheus后端对这些触发规则进行周期性计算,当满足触发条件后则会触发告警通知。默认情况下,用户可以通过Prometheus的Web界面查看这些告警规则以及告警的触发状态。当Promthues与Alertmanager关联之后,可以将告警发送到外部服务如Alertmanager中并通过Alertmanager可以对这些告警进行进一步的处理。
一条典型的告警规则如下
groups:
- name: example
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).一条告警规则主要由以下几部分组成:
alert: 告警规则的名称 expr: 基于 PromQL 表达式告警触发条件,用于计算是否有时间序列满足该条件 for: 评估等待时间,可选参数.用于表示只有当触发条件持续一段时间后才发送告警,在等待期间新产生告警的状态为 pending labels: 自定义标签,允许用户指定要附加到告警上的一组附加标签 annotations: 用于指定一组附加信息,比如用于描述告警详细信息的文字等,annotations 的内容在告警产生时会一同作为参数发送到 alertmanager.
为了能够让Prometheus能够启用定义的告警规则,我们需要在Prometheus全局配置文件中通过rule_files指定一组告警规则文件的访问路径,Prometheus启动后会自动扫描这些路径下规则文件中定义的内容,并且根据这些规则计算是否向外部发送通知:
rule_files:
[ - <filepath_glob> ... ]
默认情况下Prometheus会每分钟对这些告警规则进行计算,如果用户想定义自己的告警计算周期,则可以通过evaluation_interval
来覆盖默认的计算周期:
global:
[ evaluation_interval: <duration> | default = 1m ]
prometheus 以一个固定时间间隔来评估所有规则,这个时间由evaluate_interval
定义,我们将其设置为 15 秒.在每个评估周期,prometheus 运行每个警报规则中定义的表达式并更新警报状态:
警报的 3 种状态:
Inactive: 警报未激活; Pending: 警报已满足测试表达式条件,但仍在等待 for 子句中指定的持续时间; Firing: 警报以满足测试表达式条件,并且 Pending 的时间已经超过 for 子句中指定的持续时间.
Pending到Firing的转换可以确保警报更有效,且不会来回浮动。没有for子句的警报会自动从Inactive转换为Firing,只需要一个评估周期即可触发。带有for子句的警报将首先转换为Pending,然后转换为Firing,因此至少需要两个评估周期才能触发。
我们在前面刚开始的时候也讲了,prometheus 是组件化的工具,从他的官方架构图上我们可以看得出来,prometheus server 只是 负责产生警告,他并不会处理警告,我们这个时候就需要 AlertManager组件来处理和消费告警.
Alertmanager接受到Prometheus的重置后,需要删除重复,分组,相互之间通过路由发送到正确的接收器,电子邮件,Slack,钉钉等。Alertmanager还支持沉默和警报抑制的机制。
分组是指当出现问题时,Altermanager 会收到一个单一的通知,而当系统停机时,可能会成百上千的报警同时生成,这个时候就需要将这些告警信息分组了.
比如,一个机房内运行着 100 台物理机,当这个机房的网络出现问题,很有可能全部机器都不能正常监控了,那么如果每个机器网络不可达都发送消息,那么我们可能 会同时收到 100 条短信,那这个时候就不是报警短信了,他就会变成短信轰炸,那我们如果能把这种同类型\找出同一种影响的短信合并成一条来发送,那这不就美滋滋.
抑制是指当警报发出后,停止重复发送初始化警报引发其他错误的警报的机制。
例如当警报被触发,通知整个性能不可达,可以配置Alertmanager忽略由该警报触发而产生的所有其他警报,这可以防止通知多个或多个问题不相关的其他警报。
沉默就是在特定的时间内不要发送警报信息,比如我们公司就设置了 23 点-08 点不发送任何告警信息,因为我们公司的业务是从 9 点-20 点才会有人用.
docker pull prom/alertmanager
mkdir -p /usr/local/prometheus/alertmanager
mkdir -p /usr/local/prometheus/alertmanager/conf
docker run -id --name alertmanager prom/alertmanager
docker cp alertmanager:/etc/alertmanager/alertmanager.yml /usr/local/prometheus/alertmanager/conf/
docker rm -f alertmanager
docker run -id --name alertmanager -p 9093:9093 -v /usr/local/prometheus/alertmanager/:/alertmanager -v /usr/local/prometheus/alertmanager/conf/alertmanager.yml:/etc/alertmanager/alertmanager.yml prom/alertmanager
搭建完成之后我们可以访问http://localhost:9093
我们可以使用这个界面来查看当前警报并管理维护窗口,以及报警抑制(silence).
alertmanage 一起附带的还有个命令行 工具 amtool,允许查询警报,管理 silence .
用于定义一些全局的公共参数,如全局的SMTP配置,Slack配置等内容;
global:
# 默认的 SMTP 头部.
[ smtp_from: <tmpl_string> ]
# 用于发送邮件的主机地址和端口(SMTP 的地址)
[ smtp_smarthost: <string> ]
# SMTP 服务器默认的主机名
[ smtp_hello: <string> | default = "localhost" ]
# 使用CRAM-MD5,LOGIN和PLAIN进行SMTP验证。如果为空,则Alertmanager不会对SMTP服务器进行身份验证。
[ smtp_auth_username: <string> ]
# 使用 Login 身份验证的密码
[ smtp_auth_password: <secret> ]
# 使用 PLAIN 的密码验证
[ smtp_auth_identity: <string> ]
# 使用CRAM-MD5的密码
[ smtp_auth_secret: <secret> ]
# 是否开启 TLS 验证
[ smtp_require_tls: <bool> | default = true ]
# 用于 slack 通知的 API url
[ 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 客户端的配置
[ http_config: <http_config> ]
#定义了当Alertmanager持续多长时间未接收到告警后标记告警状态为resolved(已解决)。
# 该参数的定义可能会影响到告警恢复通知的接收时间,读者可根据自己的实际场景进行定义,其默认值为5分钟。
[ resolve_timeout: <duration> | default = 5m ]
# 定义了告警通知时的模板
# 可以采用通配符的形式去包含模板 'templates/*.tmpl'.
templates:
[ - <filepath> ... ]
# 根据标签匹配,确定当前告警应该如何处理;
route: <route>
# 接收人
receivers:
- <receiver> ...
# 抑制规则
inhibit_rules:
[ - <inhibit_rule> ... ]
用于定义告警通知时的模板,如HTML模板,邮件模板等;
根据标签匹配,确定当前告警应该如何处理;我们可以把路由想成一个倒立的数,每个警报都从树的根(基本路由或基本节点)进入.除了基本节点意外,每个路由都有匹配的标准,这些标准应该匹配所有的警报.然后,我们可以定义子路由或者子节点,他们是树的分支,对某些特定的报警感兴趣,然后采取默写特定的操作
[ receiver: <string> ]
# 通过什么来进行分组,可以支持多个标签,这个为 prometheus 里面的 labels
[ group_by: '[' <labelname>, ... ']' ]
# 警报是否应继续匹配后续的同级节点。
[ continue: <boolean> | default = false ]
# 警报必须满足的一组相等匹配器才能匹配节点。
match:
[ <labelname>: <labelvalue>, ... ]
# 警报必须满足以匹配节点的一组正则表达式匹配器。
match_re:
[ <labelname>: <regex>, ... ]
# 等待时间,如果在等待时间内当前 group 接收到了新的告警,
# 这些告警将会合并为一个通知想 receiver 发送
[ group_wait: <duration> | default = 30s ]
# 相同的 group 之间发送告警通知的时间间隔
[ group_interval: <duration> | default = 5m ]
# How long to wait before sending a notification again if it has already
# been sent successfully for an alert. (Usually ~3h or more).
[ 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参数进行设置。
接收人是一个抽象的概念,它可以是一个邮箱也可以是微信,Slack或者Webhook等,接收人一般配合告警路由使用;
合理设置抑制规则可以减少垃圾告警的产生
在 prometheus 配置文件里面默认就配置了 alertmanager,只是未启用
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
- 192.168.111.65:8999
重启 prometheus 之后可以再 web 界面上看到
与Prometheus一样,Alertmanager暴露了自身的相关指标。
- job_name: alertmanager
metrics_path: /metrics
static_configs:
- targets: ['192.168.111.65:9093']
https://grafana.com/grafana/dashboards/9578
因为给钉钉发送消息,需要发送特定的内容,altermanager 直接发送过去的信息,钉钉机器人是没有办法直接识别的,所以我们这里将会引用https://github.com/timonwong/prometheus-webhook-dingtalk
这个开源项目来实现钉钉消息发送.
docker run -d -p 8060:8060 --name webhook timonwong/prometheus-webhook-dingtalk:latest --ding.profile="test=https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXXXXXXX"
这个里面 access_token 换成自己钉钉机器人的 token,要不然你是没有办法发送过去的
就是这个东西了,钉钉机器人给我们提供了 3 中对接方式,我们这里面采用关键字模式,来实现发送消息,具体的可以看钉钉机器人帮助手册
我们先对这个接口地址发送一个 post 请求,看看钉钉机器人是否正常
可以看到钉钉机器人我们是配置好了
route:
receiver: webhook
group_wait: 30s
group_interval: 5m
repeat_interval: 5m
group_by: ['alertname']
routes:
- receiver: webhook
group_wait: 10s
receivers:
- name: 'webhook'
webhook_configs:
- send_resolved: false
url: http://192.168.111.65:8060/dingtalk/test/send
然后测试,当我们触发警报会不会成功的发送钉钉报警
更改 alertmanager 的配置如下
global:
resolve_timeout: 5m
smtp_smarthost: smtp.qq.com:465
smtp_from: 18163201@qq.com
smtp_auth_username: 18163201
smtp_auth_identity: 18163201
smtp_auth_password: oitsncuccbjcbjca
smtp_require_tls: false
templates:
- '/alertmanager/templates/*.tmpl'
route:
- receiver: email
group_wait: 10s
match:
level: serious
receivers:
- name: 'email'
email_configs:
- to: zsf18163201@163.com
send_resolved: true
html: '{{ template "alert.html" . }}'
headers: { Subject: "Prometheus 严重报警邮件"}
然后配置模板文件
mkdir -p /alertmanager/templates/
# cat templates/email.tmpl
{{ define "alert.html" }}
{{ range .Alerts }}
=========start==========<br>
告警程序: prometheus_alert <br>
告警级别: {{ .Labels.level }} 级 <br>
告警类型: {{ .Labels.alertname }} <br>
故障主机: {{ .Labels.instance }} <br>
告警主题: {{ .Annotations.summary }} <br>
告警详情: {{ .Annotations.description }} <br>
触发时间: {{ .StartsAt.Format "2019-08-04 16:58:15" }} <br>
=========end==========<br>
{{ end }}
{{ end }}
发送效果如下
- name: 'wechat'
wechat_configs:
- send_resolved: false #恢复是否发送消息
corp_id: 'wwde5d94e8b21c9678' #'企业id,在企业的配置页面可以看到'
to_user: '@all'
message: '{{ template "wechat.default.message" . }}'
agent_id: 1000002 #'应用的AgentId,在应用的配置页面可以看到'
api_secret: rdoP88Oz7ZSickdxDpaMcQQ-7QnpiedtHNUfshOoH2I #'应用的secret,在应用的配置页面可以看到'
配置模板
# cat wechat.tmpl
{{ define "wechat.default.message" }}
{{- if gt (len .Alerts.Firing) 0 -}}
{{- range $index, $alert := .Alerts -}}
{{- if eq $index 0 -}}
告警类型: {{ $alert.Labels.alertname }}
告警级别: {{ $alert.Labels.severity }}
=====================
{{- end }}
===告警详情===
告警详情: {{ $alert.Annotations.message }}
故障时间: {{ $alert.StartsAt.Format "2006-01-02 15:04:05" }}
===参考信息===
{{ if gt (len $alert.Labels.instance) 0 -}}故障实例ip: {{ $alert.Labels.instance }};{{- end -}}
{{- if gt (len $alert.Labels.namespace) 0 -}}故障实例所在namespace: {{ $alert.Labels.namespace }};{{- end -}}
{{- if gt (len $alert.Labels.node) 0 -}}故障物理机ip: {{ $alert.Labels.node }};{{- end -}}
{{- if gt (len $alert.Labels.pod_name) 0 -}}故障pod名称: {{ $alert.Labels.pod_name }}{{- end }}
=====================
{{- end }}
{{- end }}
{{- if gt (len .Alerts.Resolved) 0 -}}
{{- range $index, $alert := .Alerts -}}
{{- if eq $index 0 -}}
告警类型: {{ $alert.Labels.alertname }}
告警级别: {{ $alert.Labels.severity }}
=====================
{{- end }}
===告警详情===
告警详情: {{ $alert.Annotations.message }}
故障时间: {{ $alert.StartsAt.Format "2006-01-02 15:04:05" }}
恢复时间: {{ $alert.EndsAt.Format "2006-01-02 15:04:05" }}
===参考信息===
{{ if gt (len $alert.Labels.instance) 0 -}}故障实例ip: {{ $alert.Labels.instance }};{{- end -}}
{{- if gt (len $alert.Labels.namespace) 0 -}}故障实例所在namespace: {{ $alert.Labels.namespace }};{{- end -}}
{{- if gt (len $alert.Labels.node) 0 -}}故障物理机ip: {{ $alert.Labels.node }};{{- end -}}
{{- if gt (len $alert.Labels.pod_name) 0 -}}故障pod名称: {{ $alert.Labels.pod_name }};{{- end }}
=====================
{{- end }}
{{- end }}
{{- end }}
我们写个简单的,判断当前主机启动的容器数量,
[root@zhangsf conf]# cat docker.rules
groups:
- name: example
rules:
- alert: dockerNumber
expr: count(container_last_seen{name!=""}) >=8 #当容器数量大于等于 8 的时候告警
for: 1m
labels:
docker: number
annotations:
summary: check docker runing number
description: description info
更改 prometheus 的配置文件,引用这个规则文件
rule_files:
- /etc/prometheus/*.rules #映射到 docker 的这个目录下了
然后重启 prometheus,在也页面上查看
告警没有被触发,我们现在在启动一个容器,触发一下告警看看是什么样子
docker run -id --name test nginx:1.14.2
Prometheus首次检测到满足触发条件后,dockerNumber显示由一条告警处于活动状态。由于告警规则中设置了1m的等待时间,当前告警状态为PENDING,如下图所示
如果1分钟后告警条件持续满足,则会实际触发告警并且告警状态为FIRING,如下图所示
如果不设置 for
值就不会经过 pending,直接到 firing
然后我们到 alertmanager 上查看
告警信息已经传递到 alertmanager 上了.
根据上面这个设置我们算一下报警需要花费的时间.
1, 节点的 docker 数量在不断的变化,每隔一段时间由scrape_interval
定义的时间被 prometheus 抓取一次,现在默认是 15s
2, 然后抓取的值之后根据evaluation_interval
的指标来评估报警规则,默认是 15s
3, 当报警表达式满足时,(对于我们来说就是 docker 容器的数量大于等于 8 个),会把 dockerNumber 警报从Inactive
转变成Pending
状态,
4, 然后经过 for 设定的时间,这里是 1 分钟,如果表达式还成立,就会从Pending
转变为Firing
状态,并将报警信息推送到 alertmanager.如果表达式不成立,则会从Pending
转换到Inactive
.
所以这个报警周期耗时= 15s + 15s + 1m = 1m30s
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有