查看原文
其他

如何优雅地用Python3发送Zabbix告警推送

IT服务圈儿 2022-09-11

The following article is from 脚本之家 Author 左国才

IT服务圈儿

有温度、有态度的IT自媒体平台


作者 | 左国才

出品 | 脚本之家(ID:jb51net)

如何让Zabbix告警推送出来的消息内容具有可读性、易识别?比如故障时是红色标题,恢复时是绿色标题,监控项目之间要有换行,清晰可辨。在这里我分享一下我的思路,在这个小项目中我们统一使用Python3来发送消息,并且实现了推送到Exchange、钉钉、企业微信、自研IM多种消息平台。

首先,我们先来看一下Zabbix 消息内容加了样式后的效果

Exchange样式

告警 如图1

如图 1

恢复 如图2

如图 2


企业微信样式

告警 如图3

如图 3

恢复 如图4

如图 4

钉钉样式

告警 如图5

如图 5

恢复 如图6

如图 6

源码逻辑

以上效果基本实现了我们的预期,接下来,讲讲实现逻辑吧,比较简单,先把动作里传过来的字符串转为字典,再进行数据重组,添加html或markdown样式,发送给各个消息平台。

图 7

在这里讲讲实现过程中的3个问题吧。问题1.为什么要把消息内容转成dict而不是json对象?因为在python中对json做处理,value外层必须用双引号,但是实际测试,发现zabbix的原消息内容里带有双引号,会和json的外层双引号冲突,于是动作的消息内容字符串外层改用单引号。dict支持单引号,所以处理成dict更合适。

问题2. 样式为什么写在了脚本里,而不是动作的消息内容里?样式确实可以直接在动作的消息内容里添加,比如配置xml,html,纯文本很容易,但是markdown 很容易搞成一团,发送出来的内容没有换行,所以建议把样式写到了脚本里。

问题3. token 或key收件人还是脚本里好?如果要复用脚本,写到收件人里更好,比如群机器人比较多时这种情况下,多个token可以复用/usr/lib/zabbix/alertscripts/下的同一个脚本。当然,如果只有一个群机器人的话,写到哪都比较方便,看个人习惯而言。

在这里我们以企业微信为例,说说具体流程,当zabbix的字符串消息通过位置参数传给脚本send-wework-problems.py后,ast.literal_eval()函数接受参数并转为字典类型,以便进行数据重组,使用join()函数进行字符串拼接,加入markdown样式,加入换行,完成数据重组。企业微信字体支持3种内置颜色,markdown语法如下:

  1. <fontcolor="info">绿色</font>

  2. <fontcolor="comment">灰色</font>

  3. <fontcolor="warning">橙红色</font>

我们使用橙红色作为 告警消息标题颜色样式,使用绿色作为恢复消息标题颜色样式,发送请求代码如下:

  1. #!/usr/bin/env python3

  2. import requests

  3. import os

  4. import sys

  5. import logging

  6. import json

  7. import urllib3

  8. import ast

  9. urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

  10. logging.basicConfig(filename = os.path.join(os.getcwd(), 'push.log'), level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')


  11. subject = sys.argv[1]

  12. data = sys.argv[2]

  13. # 把字符串转为字典

  14. message = ast.literal_eval(data)


  15. # 测试用

  16. #filename = '/usr/lib/zabbix/alertscripts/1.txt'

  17. #with open(filename, 'w') as file_object:

  18. # file_object.write(data)


  19. HOSTNAME = ': '.join(('> 告警主机',message["HOSTNAME"]))

  20. HOSTIP = ': '.join(('告警地址',message["HOSTIP"]))

  21. ITEMNAME = ': '.join(('监控项目',message["ITEMNAME"]))

  22. ITEMLASTVALUE = ': '.join(('监控取值',message["ITEMLASTVALUE"]))

  23. TRIGGERSEVERITY = ': '.join(('告警等级',message["TRIGGERSEVERITY"]))

  24. TRIGGERSTATUS = ': '.join(('当前状态',message["TRIGGERSTATUS"]))

  25. TRIGGERNAME = ': '.join(('告警信息',message["TRIGGERNAME"]))

  26. EVENTTIME = ': '.join(('告警时间',message["EVENTTIME"]))

  27. EVENTAGE = ': '.join(('持续时间',message["EVENTAGE"]))

  28. EVENTID = ': '.join(('事件ID',message["EVENTID"]))



  29. sendtext = '\n >'.join((HOSTNAME,HOSTIP,ITEMNAME,ITEMLASTVALUE,TRIGGERSEVERITY,TRIGGERSTATUS,TRIGGERNAME,EVENTTIME,EVENTAGE,EVENTID))



  30. # 请修改key, zabbix server url

  31. def send_wework_message(sendtext):

  32. logger = logging.getLogger(__name__)

  33. WEWORK_API_URL="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=x"

  34. data = {

  35. 'msgtype': 'markdown',

  36. "markdown": {

  37. "content": ''.join(('## <font color="warning">',subject,'</font> \n',sendtext,'\n','#### [点击转到ZABBIX](https://www.zabbix.com/) \n'))

  38. }

  39. }

  40. resp = requests.post(

  41. WEWORK_API_URL, data=json.dumps(data), headers={'Content-Type': 'application/json'}

  42. )

  43. print(resp.text)

  44. if resp.status_code != 200:

  45. logger.info('[REQUESTS WEWORK API ERROR]%s'% resp.text)


  46. send_wework_message(sendtext)

企业微信消息发送可以参考https://work.weixin.qq.com/help?doc_id=13376#如何使用群机器人

其他类型的我都放到了github上,见https://github.com/ZuoGuocai/zabbix_push

开始配置

需要先把send-problems-wework.py,send-recovery- wework.py两个脚本放在zabbix server 的/usr/lib/zabbix/alertscripts 目录下,这两个脚本功能分别对应告警推送,恢复推送。

管理--报警媒介类型--创建媒体类型



如图 8

在这里我把key放到了脚本里,收件人里配置了一个无用的字符串,所以没有添加{ALERT.SENDTO},只有如下两项

  1. {ALERT.SUBJECT}

  2. {ALERT.MESSAGE}

如果要复用脚本,建议配置以下三项

  1. {ALERT.SENDTO}

  2. {ALERT.SUBJECT}

  3. {ALERT.MESSAGE}


管理--用户--Admin--报警媒介--添加--收件人

如图9

配置--主机--监控项和触发器,再配置动作

为了方便演示,我配置了一台主机,添加了Zabbix 原生的ICMP Ping模板,自带了监控项和触发器 。

开始配置动作

如图10

消息内容如下

  1. {

  2. "HOSTNAME":"{HOST.NAME}",

  3. "HOSTIP":"{HOST.IP}",

  4. "ITEMNAME":'{ITEM.NAME}',

  5. "ITEMLASTVALUE":"{ITEM.LASTVALUE}",

  6. "TRIGGERSEVERITY":"{TRIGGER.SEVERITY}",

  7. "TRIGGERSTATUS":"{TRIGGER.STATUS}",

  8. "TRIGGERNAME":"{TRIGGER.NAME}",

  9. "EVENTTIME":"{EVENT.DATE} {EVENT.TIME}",

  10. "EVENTAGE":"{EVENT.AGE}",

  11. "EVENTID":"{EVENT.ID}"

  12. }

有没有发现第三项的value外层是单引号,value里经常有双引号出现,所有外层改用了单引号。这儿很好解释了 问题1.为什么要把消息内容转成dict而不是json对象

开始测试

在监测主机上操作

禁止ping

  1. echo 1>/proc/sys/net/ipv4/icmp_echo_ignore_all

查看告警消息

允许ping

  1. echo 0>/proc/sys/net/ipv4/icmp_echo_ignore_all

查看恢复消息

排错

告警消息为什么没有发出,脚本执行报错,为了更好的排错,建议添加动作日志,在web控制台的路径如下

监测--仪表盘--编辑仪表盘--添加构件--类型--动作日志


如图11

爱美之心,人皆有之,用Python3改写样式后,Zabbix发送的告警消息简洁优雅,并且能很好地跟多种IM集成,不禁感慨,这才是我要的告警效果呀。

本文作者:左国才,VIPKID运维工程师,笔名icai,主要研究开源Linux操作系统,数据库,云计算领域相关技术,平时喜欢阅读脚本之家公众号。

声明:本文为 脚本之家专栏作者 投稿,未经允许请勿转载。





*版权声明:转载文章和图片均来自公开网络,版权归作者本人所有,推送文章除非无法确认,我们都会注明作者和来源。如果出处有误或侵犯到原作者权益,请与我们联系删除或授权事宜。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存