首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

prometheus 告警

prometheus 告警

1, prometheus 告警简介

告警能力在Prometheus的架构中被划分成两个独立的部分。如下所示,通过在Prometheus中定义AlertRule(告警规则),Prometheus会周期性的对告警规则进行计算,如果满足告警触发条件就会向Alertmanager发送告警信息。

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

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

在Prometheus中,还可以通过Group(告警组)对一组相关的告警进行统一定义。当然这些定义都是通过YAML文件来统一管理的。

1,1 自定义 prometheus 告警规则

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

1.2 定义告警规则

一条典型的告警规则如下

代码语言:txt
复制
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启动后会自动扫描这些路径下规则文件中定义的内容,并且根据这些规则计算是否向外部发送通知:

代码语言:txt
复制
rule_files:
  [ - <filepath_glob> ... ]

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

代码语言:txt
复制
global:
  [ evaluation_interval: <duration> | default = 1m ]

1.3, 警报触发

prometheus 以一个固定时间间隔来评估所有规则,这个时间由evaluate_interval定义,我们将其设置为 15 秒.在每个评估周期,prometheus 运行每个警报规则中定义的表达式并更新警报状态:

警报的 3 种状态:

Inactive: 警报未激活; Pending: 警报已满足测试表达式条件,但仍在等待 for 子句中指定的持续时间; Firing: 警报以满足测试表达式条件,并且 Pending 的时间已经超过 for 子句中指定的持续时间.

Pending到Firing的转换可以确保警报更有效,且不会来回浮动。没有for子句的警报会自动从Inactive转换为Firing,只需要一个评估周期即可触发。带有for子句的警报将首先转换为Pending,然后转换为Firing,因此至少需要两个评估周期才能触发。

2, 报警组件 AlertManager

2.1 什么是 AlertManager?

我们在前面刚开始的时候也讲了,prometheus 是组件化的工具,从他的官方架构图上我们可以看得出来,prometheus server 只是 负责产生警告,他并不会处理警告,我们这个时候就需要 AlertManager组件来处理和消费告警.

Alertmanager接受到Prometheus的重置后,需要删除重复,分组,相互之间通过路由发送到正确的接收器,电子邮件,Slack,钉钉等。Alertmanager还支持沉默和警报抑制的机制。

2.1.1 分组

分组是指当出现问题时,Altermanager 会收到一个单一的通知,而当系统停机时,可能会成百上千的报警同时生成,这个时候就需要将这些告警信息分组了.

比如,一个机房内运行着 100 台物理机,当这个机房的网络出现问题,很有可能全部机器都不能正常监控了,那么如果每个机器网络不可达都发送消息,那么我们可能 会同时收到 100 条短信,那这个时候就不是报警短信了,他就会变成短信轰炸,那我们如果能把这种同类型\找出同一种影响的短信合并成一条来发送,那这不就美滋滋.

2.1.2 抑制

抑制是指当警报发出后,停止重复发送初始化警报引发其他错误的警报的机制。

例如当警报被触发,通知整个性能不可达,可以配置Alertmanager忽略由该警报触发而产生的所有其他警报,这可以防止通知多个或多个问题不相关的其他警报。

2.1.3 沉默

沉默就是在特定的时间内不要发送警报信息,比如我们公司就设置了 23 点-08 点不发送任何告警信息,因为我们公司的业务是从 9 点-20 点才会有人用.

2.2, 安装部署

代码语言:txt
复制
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 .

2.3 配置文件详解

2.3.1, global全局配置

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

代码语言:txt
复制
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> ... ]

2.3.2, templates 模板

用于定义告警通知时的模板,如HTML模板,邮件模板等;

2.3.3, route 告警路由

根据标签匹配,确定当前告警应该如何处理;我们可以把路由想成一个倒立的数,每个警报都从树的根(基本路由或基本节点)进入.除了基本节点意外,每个路由都有匹配的标准,这些标准应该匹配所有的警报.然后,我们可以定义子路由或者子节点,他们是树的分支,对某些特定的报警感兴趣,然后采取默写特定的操作

代码语言:txt
复制
[ 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参数进行设置。

2.3.4, receivers 接收人

接收人是一个抽象的概念,它可以是一个邮箱也可以是微信,Slack或者Webhook等,接收人一般配合告警路由使用;

2.3.5, inhibit_fules 抑制规则

合理设置抑制规则可以减少垃圾告警的产生

2.4, 为 prometheus[配置 alertmanager

在 prometheus 配置文件里面默认就配置了 alertmanager,只是未启用

代码语言:txt
复制
# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
       - 192.168.111.65:8999

重启 prometheus 之后可以再 web 界面上看到

2.5, 监控 alertmanager

与Prometheus一样,Alertmanager暴露了自身的相关指标。

代码语言:txt
复制
  - job_name: alertmanager
    metrics_path: /metrics
    static_configs:
    - targets: ['192.168.111.65:9093']

2.6, 在 grafana 集成

https://grafana.com/grafana/dashboards/9578

2.7, 集成钉钉发送报警信息

因为给钉钉发送消息,需要发送特定的内容,altermanager 直接发送过去的信息,钉钉机器人是没有办法直接识别的,所以我们这里将会引用https://github.com/timonwong/prometheus-webhook-dingtalk这个开源项目来实现钉钉消息发送.

2.7.1, docker 安装 webhook-dingtalk

代码语言:txt
复制
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,要不然你是没有办法发送过去的

2.7.2, 钉钉机器人配置

就是这个东西了,钉钉机器人给我们提供了 3 中对接方式,我们这里面采用关键字模式,来实现发送消息,具体的可以看钉钉机器人帮助手册

我们先对这个接口地址发送一个 post 请求,看看钉钉机器人是否正常

可以看到钉钉机器人我们是配置好了

2.7.3, 编写 altermanager 配置文件

代码语言:txt
复制
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

然后测试,当我们触发警报会不会成功的发送钉钉报警

2.8 集成qq 邮箱发送邮件

更改 alertmanager 的配置如下

代码语言:txt
复制
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 严重报警邮件"}

然后配置模板文件

代码语言:txt
复制
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 }}

发送效果如下

2.9 集成企业微信

代码语言:txt
复制
- 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,在应用的配置页面可以看到'

配置模板

代码语言:txt
复制
# 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 }}

3, 举例

我们写个简单的,判断当前主机启动的容器数量,

代码语言:txt
复制
[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 的配置文件,引用这个规则文件

代码语言:txt
复制
rule_files:
  - /etc/prometheus/*.rules #映射到 docker 的这个目录下了

然后重启 prometheus,在也页面上查看

告警没有被触发,我们现在在启动一个容器,触发一下告警看看是什么样子

代码语言:txt
复制
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

举报
领券