暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Prometheus系列3 - AlertManager专场

栋总侃技术 2021-07-02
2501

提示:这篇文章的精华在最后一节,文章比较长,希望读者能够耐心读完

这几天由于都在找 Prometheus 高可用的集群的解决方案,所以断更了几天。后续的文章也会带来这方面研究的成果,来和大家一起讨论。

今天我们暂时从 Kubernetes 话题切换到 Prometheus系列。了解在Prometheus中产生的一条告警需要经过哪些环节?AlertMananger扮演的是怎样的角色?同时了解 AlertManager 中提供哪些机制避免报警泛滥?

我们首先看下下面的AlertManager的结构图,了解大致的数据走向:

  • prometheus server产生告警并调用AlertManager的接口下发告警

  • 如果消息被静默了则进入静默管理,等过了静默时间判断是否需要发送

  • 如果消息被被静默,则走正常的消息流程

    • 分组(Group)

    • 抑制(Inhibitor)

    • 静默(silence)【再次静默拦截】

    • 路由发送

Prometheus 产生告警

  • 配置文件的rule_files节点指定配置文件

    rule_files:
    - node.rules
    - mysql.rules
    - redis.rules

    可指定多个文件,为便于管理,也可将所有的报警合并到一个文件中。

    查看 mysql.rules 中定义的一条报警规则:

      - name: MySQLStatsAlert
      rules:
      - alert: MySQL 服务宕机
      expr: mysql_up == 0
      for: 1m
          labels:
      severity: critical
      annotations:
            summary: "MySQL{{ $labels.instance }}已宕机"
      • name表示Group的名称,在MySQLStatsAlert分组下还有其他的报警项,例如连接数、慢查询个数等设置的阈值。

      • rules为告警项列表,其中alert为“MySQL 服务宕机”是其中一条报警的名称。

      • expr、for表示满足该表达式且长达多长时间会产生告警。expr可以为多条表达式的 or、and逻辑操作的结果。例如以下表达式表示内存不足10%且剩余空间不足100M产生告警。

        node_memory_MemTotal_bytes/node_memory_MemTotal_bytes <0.1 and node_memory_MemTotal_bytes < 100*1024*1024
        • labels、annotations为自定义的显示内容。

        在Prometheus中一条告警规则有三个状态:

        inactive:还未被触发;

        pending:已经触发,但是还未达到for设定的时间;

        firing:触发且达到设定时间。

        这里需要注意在pending状态如果触发条件又不满足了(恢复了),将会回归inactive状态。

        在 Prometheus 中为 firing状态的告警需要发送给 AlertManager 来真正的发送出报警通知。在 Prometheus 的配置中需要指定 AlertManager的地址,如下所示:

          alerting:
          alertmanagers:
          - scheme: http
          static_configs:
          - targets:
          - 127.0.0.1:9093

          AlertManager告警分组、抑制、静默

          当报警到达AlertManager后,AlertManager并不会就简单的将告警立刻发送出去,而是会通过一些手段来避免告警泛滥。

          分组(Group)

          首先是对消息分组,将多条告警消息聚合成一条消息。

          而分组的条件取决于AlertManager的router.group_by配置,在了解该配置前,我们需要先了解 Prometheus告警规则的labels字段,例如我们在上面的例子中的定义:

            ...
            labels:
              severity: critical
            ...

            这里我们只有一个报警级别的label,我们还可以根据自己的需求增加其他自定义的label。例如,同时指定了服务名称和报警的资源类型.

              labels:
                severity: critical
                server: mysql
                type: cpu

              而AlertManager的router.group_by配置正是依赖label来组合条件的。如下配置表示根据监控的服务名称进行分组:

                route:
                  group_by: 
                   - mysql
                  group_interval: 3m
                  group_wait: 2m

                对于分组还有以下两个配置:

                • group_wait:等待2分钟来聚合分组消息。当mysql在两分钟的滑动窗口时间内产生了多条告警,会合并成一条告警发送出来。

                • group_interval:发送告警组的间隔时间。当mysql消息组发送出一条消息后,下一次消息通知将会在3分钟后再发送。

                当然报警类型还可以以其他的维度,例如资源类型或者是告警级别。将同类型的消息聚合不仅仅只是减少了消息量,当大量消息时,聚合一起的消息也便于运维获取信息能够快速定位问题。

                告警抑制

                告警抑制是当先后产生了多条告警后,使用一定的条件来使一些告警不会被发出。

                同样,这些条件的定义来源于告警配置的label。我们可以用最简单的告警优先级来做抑制规则,对于相同的服务,可以用高等级报警抑制低等级告警。

                例如有以下两条label的告警:

                  labels:
                  severity: critical
                  server: mysql
                  type: cpu

                  labels:
                  severity: warm
                  server: mysql
                  type: connections

                  在alertManager的配置文件中我们可以这样配置抑制规则:

                    inhibit_rules:
                    - source_match:
                          severity: critical
                    target_match:
                    severity: warm
                    equal: ['server']

                    inhibit_rules节点表示告警抑制的规则列表,这条配置表达的是配置有label为severity等于 critical的报警将会抑制配置有label为severity等于 warm且这两者label中的server字段相等的报警。

                    告警延迟

                    在分组中已经体现出一组消息的延迟,在group_interval时间内不会发出相同组的告警,而对于一条告警也有延迟策略其配置在AlertManager的route.repeat_interval节点:

                      route: 
                      ...
                      repeat_interval: 5m

                      例如以上配置表示在5分钟的滑动窗口时间内不会发出相同的告警。

                      告警路由

                      对于不同的告警,可能通知的对象(人群、手机号、微信群等)不一样,则需要对告警进行路由分发。而AlertManager的路由依据仍然还是告警规则的labels。在AlertManager配置的route节点配置路由规则,示例子如下:

                        route:
                        repeat_interval: 2h
                          receiver: default-group
                        routes:
                        - match:
                              server: mysql
                              receiver: group-1
                        - match:
                              server: redis
                              receiver: group-2

                        route是以树的结构递归下去的,当匹配路由时也是匹配到最深的节点。这里我们将mysql服务和redis服务分别告警至不同的群体。若没有匹配到match条件,则会走默认的路由,也就是树的根节点。

                        当然,如果有必要,也可以对mysql这个match再进行路由 , 区别出不同的资源,通知告警人群也不同。

                        而对应群体具体的对象我们需要在receivers节点中定义,例如:

                          receivers:
                          - name: default-group
                          email_configs:
                            - to: '491101403@qq.com'
                          send_resolved: true
                          headers:
                          subject: "报警邮件"
                                from: "警报中心"
                          - name: group-1
                          webhook_configs:
                            - url: http://localhost:8002/sms
                          name: group-2
                          wechat_configs:
                          - corp_id: 'xxxx'
                          api_url: 'https://qyapi.weixin.qq.com/xxxxx'
                          send_resolved: true
                              to_party: '3'
                          agent_id: 'xxxxxx'
                              api_secret: 'xxxxx'

                          在上面的示例中:

                          • default-group:使用alertmanager自带的邮件功能发送邮件通知

                          • group-1: 调用外部接口,需要实现该接口对报警信息的解析,然后可对接不同的邮件、电话、短信平台

                          • group-2:使用AlertManager自带的企业微信功能

                          以上讲解了从Prometheus产生告警到发送告警的完整过程。

                          特殊难题

                          对于生产实践中,我们大部分的场景只需要知道如何使用配置就行,但是也存在着很棘手的情况,接下来我将给大家讲解一个我遇到问题的场景,以及提供我的解决方案,如果你们有更好的解决方案可以直接留言给我。

                          在上面讲解到了告警抑制功能,我们希望利用该机制能够在以下场景产生消息抑制:当某台主机宕机了,部署在该主机上的应用也将会宕机。希望告警发出主机宕机后,不再需要发出对应的应用宕机告警

                          告警规则如下:

                            groups:
                            - name: node
                            rules:
                              - alert: 主机宕机
                            expr: up == 0
                            for: 1m
                            labels:
                            severity: critical
                            server: node
                            type: lifecycle
                            annotations:
                            summary: "主机 {{ $labels.instance }} 宕机"
                            description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 1 minutes."

                            - name: MySQLStatsAlert
                            rules:
                            - alert: MySQL 服务宕机
                            expr: mysql_up == 0
                            for: 1m
                            labels:
                            severity: critical
                            server: mysql
                            type: lifecycle
                            annotations:
                                  summary: "MySQL{{ $labels.instance }}宕机"
                                  description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 1 minutes."

                            如果我们考虑多集群(多台主机、多个mysql集群),我们仅仅通过现有的labels发现无法组装inhibit_rules的,因为根本没法将Mysql所在的主机对应上

                            因为部署node_exporter和mysql_exporter同主机的关系可能如下:

                            也可能如下:

                            所以,无法确定exporter的instance与对应的监控对象instance的ip一致。就算ip一致,由于instance是带有ip和端口的,而inhibit_rules的规则只能配置简单的字符串相等或者正则表达式,也无法对字符串进行处理。

                            该如何解决这个问题呢?

                            我发现在prometheus定义scrape_configs(收集指标的exporter或者是pushGateway时是可以指定固定label的,我们可以这样去定义对应的static_configs:

                              scrape_configs:

                              - job_name: 'node_exporter'
                              static_configs:
                                    - targets: ['172.16.212.38:9100']
                                      labels:
                                        extra: group1
                              - job_name: 'node_exporter'
                              static_configs:
                                    - targets: ['172.16.212.39:9100']
                                        extra: group2
                                - job_name: 'mysql_exporter'
                              static_configs:
                                    - targets: ['172.16.212.38:9090']
                                     extra: group1
                              - job_name: 'node_exporter'
                              static_configs:
                                    - targets: ['172.16.212.39:9090']
                                      extra: group2

                              这样的label起到了成对匹配的作用,我们可以这样定义inhibit_rules完成消息抑制:

                                inhibit_rules:
                                - source_match:
                                type: lifecycle
                                      server: node
                                target_match:
                                type: lifecycle
                                equal: ['extra']

                                解释:

                                主机(server: Node机(type: lifecycle的告警将会抑制其他与其extra值相同(extra都为group1或者group2)的宕机(type: lifecycle告警。

                                注意告警抑制中,如果先产生了mysql宕机告警,再产生主机宕机的告警,那么mysql宕机告警不会被抑制。只有较后产生的报警会被较前产生的报警抑制

                                如果你有更好的解决方案,可以留言与我交流,谢谢。

                                本系列回顾:

                                prometheus(一)快速入门

                                prometheus系列2 - Exporter专场

                                文章转载自栋总侃技术,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                评论