Prometheus系列--altermanager结合webhook定制钉钉和微信告警

举报
郁唯xiaolin 发表于 2021/05/22 08:48:33 2021/05/22
【摘要】 基于ding-webhook与wechat-webhook的消息发送

一、概述

在一些相关的exporter的文档中,已经相关的介绍了一些告警规则的内容。简单介绍一下告警的流程:prometheus-server采集到的各种exporter的指标,然后根据规则去判断是不是达到规则的阈值,如果达到就判断要告警,将告警的内容发送给altermanager,altermanager是用来处理告警的,比如接收prometheus-server发来的告警信息,然后做一些整合,然后发送给消息发送组件,整合具体做了什么呢,就是告警信息的聚合、告警信息的沉默、告警分组等,altermanager将告警消息的发出,可以通过altermanager的smtp发出,也可以通过消息发送组件webhook集成,这些组件一般是自己开发或者借鉴别人开发的。

接下来要介绍一下webhook与钉钉或企业微信机器人结合实现告警消息的推送。

二、企业微信版webhook

这里使用的企业微信版webhook,使用了flask开发的,使用docker-compose 运行,并定制消息的样式。

发送企业微信版告警消息,是调用的群机器人,所以需要将接受告警消息的人拉到一个群里,然后添加一个告警机器人,里面就能看到一个webhook地址:https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=56a69cxxxxxxxxxxxxxxxxxxxxxxxx,此地址可以分成两部分,一部分key=(包含key=)之前的部分,一部分是key=之后的部分。之后的部分赋予一个名称吧,叫ROBOT_TOKEN=“56a69cxxxxxxxxxxxxxxxxxxxxxxxx”

贴上使用flask开发的内容

]# vim app.py
​
# -*- coding: utf-8 -*-
import os
import json
import requests
import arrow
​
from flask import Flask
from flask import request
​
app = Flask(__name__)
​
​
def bytes2json(data_bytes):
    data = data_bytes.decode('utf8').replace("'", '"')
    return json.loads(data)
​
def makealertdata(data):
    for output in data['alerts'][:]:
        try:
            pod_name = output['labels']['pod']
        except KeyError:
            try:
                pod_name = output['labels']['pod_name']
            except KeyError:
                pod_name = 'null'
​
        try:
            namespace = output['labels']['namespace']
        except KeyError:
            namespace = 'null'
​
        try:
            message = output['annotations']['message']
        except KeyError:
            try:
                message = output['annotations']['description']
            except KeyError:
                message = 'null'
​
        if output['status'] == 'firing':
            status_zh = '报警'
            title = '【%s】xxxx环境 %s 有新的报警' % (status_zh, output['labels']['alertname'])
            send_data = {
                "msgtype": "markdown",
                "markdown": {
                    "content": "## %s \n\n" %title +
                            ">**告警级别**: %s \n\n" % output['labels']['severity'] +
                            ">**告警类型**: %s \n\n" % output['labels']['alertname'] +
                            ">**告警主机**: %s \n\n" % output['labels']['node_name'] +
                            ">**告警详情**: %s \n\n" % message +
                            ">**告警状态**: %s \n\n" % output['status'] +
                            ">**触发时间**: %s \n\n" % arrow.get(output['startsAt']).to('Asia/Shanghai').format(
                        'YYYY-MM-DD HH:mm:ss ZZ')
​
                }
            }
        elif output['status'] == 'resolved':
            status_zh = '恢复'
            title = '【%s】xxxx环境 %s 有报警恢复' % (status_zh, output['labels']['alertname'])
            send_data = {
                "msgtype": "markdown",
                "markdown": {
                    "content": "## %s \n\n" %title +
                            ">**告警级别**: %s \n\n" % output['labels']['severity'] +
                            ">**告警类型**: %s \n\n" % output['labels']['alertname'] +
                            ">**告警主机**: %s \n\n" % output['labels']['node_name'] +
                            ">**告警详情**: %s \n\n" % message +
                            ">**告警状态**: %s \n\n" % output['status'] +
                            ">**触发时间**: %s \n\n" % arrow.get(output['startsAt']).to('Asia/Shanghai').format(
                        'YYYY-MM-DD HH:mm:ss ZZ') +
                            ">**触发结束时间**: %s \n" % arrow.get(output['endsAt']).to('Asia/Shanghai').format(
                        'YYYY-MM-DD HH:mm:ss ZZ')
                }
            }
        return send_data
def send_alert(data):
  #此处获取环境变量“ROBOT_TOKEN”,会在docker-compose的配置文件中配置,docker-compose启动docker时向docker容器注入环境变量
    token = os.getenv('ROBOT_TOKEN')
    if not token:
        print('you must set ROBOT_TOKEN env')
        return
    url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=%s' % token
​
    send_data = makealertdata(data)
    req = requests.post(url, json=send_data)
    result = req.json()
    if result['errcode'] != 0:
        print('notify dingtalk error: %s' % result['errcode'])
​
​
@app.route('/', methods=['POST', 'GET'])
def send():
    if request.method == 'POST':
        post_data = request.get_data()
        send_alert(bytes2json(post_data))
        return 'success'
    else:
        return 'weclome to use prometheus alertmanager dingtalk webhook server!'
​
​
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
​

使用flask启动之前还需要安装一些python的包,内容如下:

]# vim requirements.txt

certifi==2018.10.15
chardet==3.0.4
Click==7.0
Flask==1.0.2
idna==2.7
itsdangerous==1.1.0
Jinja2==2.10
MarkupSafe==1.1.0
requests==2.20.1
urllib3==1.24.1
Werkzeug==0.14.1
arrow==0.13.1

接下来是编写Dockerfile

]# vim Dockerfile
FROM python:3.6.4
​
# set working directory
WORKDIR /src
​
# add app
ADD . /src
​
# install requirements
RUN pip install -r requirements.txt
​
EXPOSE 5000
​
# run server
CMD python app.py

使用docker-compose启动,当然离不开docker-compose.yaml,看下

]# vim docker-compose.yml
version: "2"
networks:
    monitor:
        driver: bridge
services:
  prometheus-webhook-alert:
    build: .
    restart: always
    volumes:
    - /etc/localtime:/etc/localtime
    - ./app.py:/src/app.py
    ports:
    - "5000:5000"
    environment:
    # 此处设置的环境变量会被app.py运行时获取到
      ROBOT_TOKEN: "xxxxxxxxxxxxxxxxxxxxxxb00"
    networks:
    - monitor

各个程序都已经写好了,开始启动

# 将以上三个文件和requirements.txt文件放到与prometheus-server同台主机上
]#  ls /usr/local/prometheus/webhook-wechat
app.py Dockerfile docker-compose.yml requirements.txt
​
# 启动
]# docker-compose up -d 
#检查是否启动,一下三种方式都能启动
]# docker-compose status
]# docker ps -a 
]# ss -luntp |grep 5000
​
# 日志查看,一下两种都可查看,-f 支持输出日志
]# docker-compose logs 
]# docker-compose logs -f
​

因为安装alertmanager的文档中已经介绍并且配置了webhook告警,所以,此事确定启动无误,就可以测试告警是否能发出了。

如何测试:还是安装alertmanager中的,启动或者停止node_exporter 查看是否消息能发到企业微信,我这边收到的企业微信群告警消息是这样的:其实还可以增加更多的信息,只是需要在flask的Python文件中配置即可。

image-20210508161142295


二、钉钉版webhook

这里使用的钉钉版webhook,与企业微信版相似,只是在flask的文件中,稍微改一些地方,同样使用docker-compose 运行,并可定制消息的样式。

发送钉钉版告警消息,是调用的钉钉群机器人,所以需要将接受告警消息的人拉到一个群里,然后群设置的之鞥呢群助手添加一个告警机器人,里面就能看到一个webhook地址:https://oapi.dingtalk.com/robot/send?access_token=56a69xxxxxxxxxxxxxxxxxxxxxxxxxx,此地址可以分成两部分,一部分access_token=(包含access_token=)之前的部分,一部分是access_token=之后的部分。之后的部分赋予一个名称吧,叫ROBOT_TOKEN=“56a69cxxxxxxxxxxxxxxxxxxxxxxxx”

贴上使用flask开发的内容

]# vim app.py
​
# -*- coding: utf-8 -*-
import os
import json
import requests
import arrow
​
from flask import Flask
from flask import request
​
app = Flask(__name__)
​
​
def bytes2json(data_bytes):
    data = data_bytes.decode('utf8').replace("'", '"')
    return json.loads(data)
​
def makealertdata(data):
    for output in data['alerts'][:]:
        try:
            pod_name = output['labels']['pod']
        except KeyError:
            try:
                pod_name = output['labels']['pod_name']
            except KeyError:
                pod_name = 'null'
​
        try:
            namespace = output['labels']['namespace']
        except KeyError:
            namespace = 'null'
​
        try:
            message = output['annotations']['message']
        except KeyError:
            try:
                message = output['annotations']['description']
            except KeyError:
                message = 'null'
​
        if output['status'] == 'firing':
            status_zh = '报警'
            title = '【%s】 %s 有新的报警' % (status_zh, output['labels']['alertname'])
            send_data = {
                "msgtype": "markdown",
                "markdown": {
                    "title": title,
                    "text": "## %s \n\n" %title +
                            ">**告警级别**: %s \n\n" % output['labels']['severity'] +
                            ">**告警类型**: %s \n\n" % output['labels']['alertname'] +
                            ">**告警主机**: %s \n\n" % output['labels']['node_name'] +
                            ">**告警详情**: %s \n\n" % message +
                            ">**告警状态**: %s \n\n" % output['status'] +
                            ">**触发时间**: %s \n\n" % arrow.get(output['startsAt']).to('Asia/Shanghai').format(
                        'YYYY-MM-DD HH:mm:ss ZZ')
​
                }
            }
        elif output['status'] == 'resolved':
            status_zh = '恢复'
            title = '【%s】 %s 有新的报警' % (status_zh, output['labels']['alertname'])
            send_data = {
                "msgtype": "markdown",
                "markdown": {
                    "title": title,
                    "text": "## %s \n\n" %title +
                            ">**告警级别**: %s \n\n" % output['labels']['severity'] +
                            ">**告警类型**: %s \n\n" % output['labels']['alertname'] +
                            ">**告警主机**: %s \n\n" % output['labels']['node_name'] +
                            ">**告警详情**: %s \n\n" % message +
                            ">**告警状态**: %s \n\n" % output['status'] +
                            ">**触发时间**: %s \n\n" % arrow.get(output['startsAt']).to('Asia/Shanghai').format(
                        'YYYY-MM-DD HH:mm:ss ZZ') +
                            "  **触发结束时间**: %s \n" % arrow.get(output['endsAt']).to('Asia/Shanghai').format(
                        'YYYY-MM-DD HH:mm:ss ZZ')
                }
            }
        return send_data
def send_alert(data):
    token = os.getenv('ROBOT_TOKEN')
    if not token:
        print('you must set ROBOT_TOKEN env')
        return
    url = 'https://oapi.dingtalk.com/robot/send?access_token=%s' % token
​
    send_data = makealertdata(data)
    req = requests.post(url, json=send_data)
    result = req.json()
    if result['errcode'] != 0:
        print('notify dingtalk error: %s' % result['errcode'])
​
​
@app.route('/', methods=['POST', 'GET'])
def send():
    if request.method == 'POST':
        post_data = request.get_data()
        print(post_data)
        send_alert(bytes2json(post_data))
        return 'success'
    else:
        return 'weclome to use prometheus alertmanager dingtalk webhook server!'
​
​
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

接下来是编写Dockerfile(与企业微信版一样)

]# vim Dockerfile
FROM python:3.6.4
​
# set working directory
WORKDIR /src
​
# add app
ADD . /src
​
# install requirements
RUN pip install -r requirements.txt
​
EXPOSE 5000
​
# run server
CMD python app.py

使用docker-compose启动,当然离不开docker-compose.yaml(与企业微信版一样),看下

]# vim docker-compose.yml
version: "2"
networks:
    monitor:
        driver: bridge
services:
  prometheus-webhook-alert:
    build: .
    restart: always
    volumes:
    - /etc/localtime:/etc/localtime
    - ./app.py:/src/app.py
    ports:
    - "5000:5000"
    environment:
    # 此处设置的环境变量会被app.py运行时获取到
      ROBOT_TOKEN: "xxxxxxxxxxxxxxxxxxxx"
    networks:
    - monitor

各个程序都已经写好了,开始启动(与企业微信版一样)

# 将以上三个文件和requirements.txt文件放到与prometheus-server同台主机上
]#  ls /usr/local/prometheus/webhook-wechat
app.py Dockerfile docker-compose.yml requirements.txt
​
# 启动
]# docker-compose up -d 
#检查是否启动,一下三种方式都能启动
]# docker-compose status
]# docker ps -a 
]# ss -luntp |grep 5000
​
# 日志查看,一下两种都可查看,-f 支持输出日志
]# docker-compose logs 
]# docker-compose logs -f
​

因为安装alertmanager的文档中已经介绍并且配置了webhook告警,所以,此时确定启动无误,就可以测试告警是否能发出了。

如何测试:还是安装alertmanager中的,启动或者停止node_exporter 查看是否消息能发到钉钉,我这边收到的钉钉群告警消息是这样的:其实还可以增加更多的信息。

image-20210508161142295.png


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。