ZABBIX 对接飞书实现机器人报警通知
今年年中,我开始把我自用的一个企业从企业微信迁移到飞书,历时两个半月,终于彻底迁移过来了,老企业微信也就注销掉了。ZABBIX 对接飞书也是迁移的最后一步。
其实关于 ZABBIX 对接国内各种企业 IM 已经是老生常谈的问题了。不过我在对接之前还是习惯性的搜了一下,发现使用 ZABBIX Javascript Runtime 实现的少之又少,所以我还是打算记录一下。
效果放前面
你可以通过飞书的群机器人,接收 ZABBIX 的告警信息,例如下面的是故障消息:
下面这个是故障恢复消息:
实现思路
使用 ZABBIX 6 以上版本提供的 Javascript Runtime,免去了在 ZABBIX 服务器底层放 Python 脚本的步骤,后续维护报警媒介,都可以通过 ZABBIX WEB 来实现。
扩展阅读:其它 Javascript 对象
技术实现
创建飞书机器人
我们需要先在飞书群里创建一个机器人。获取它的 Webhook 地址。创建飞书群机器人,请参考飞书帮助中心 - 如何在群组中使用机器人?,请按照文档操作,创建一个“自定义机器人”。
安全配置,按需启用就好了,或者干脆不启用。其中上图框选的部分,就是后文需要用到的 BOTKEY。
创建飞书消息卡片模板
创建一个飞书消息卡片模板,消息卡片模板可以很方便的帮助我们调整推送消息的样式,且消息卡片模板可以导出导入,方便复用。下面我提供了两个告警和告警恢复的消息卡片模板。
以下内容托管在 GitHub,如果看不到,请确认您是否有访问权限。
{"templateId":"ctp_AAVPnuaz6F8I","content":{"elements":[{"tag":"column_set","flex_mode":"stretch","background_style":"default","columns":[{"tag":"column","width":"weighted","weight":1,"vertical_align":"top","elements":[{"tag":"markdown","content":"**🟢 所属设备:**\n[${host_ip}] ${host_name} "}]},{"tag":"column","width":"weighted","weight":1,"vertical_align":"top","elements":[{"tag":"markdown","content":"**⚠️ 严重性等级:**\n${event_severity}"}]}]},{"tag":"column_set","flex_mode":"none","background_style":"default","columns":[{"tag":"column","width":"weighted","weight":1,"vertical_align":"top","elements":[{"tag":"markdown","content":"**🕐 发生时间:**\n${event_date} ${event_time}"}]}]},{"tag":"markdown","content":"**🔦 持续时间:**\n${event_duration}"},{"tag":"hr"},{"tag":"div","text":{"content":"** 👀 问题详情 **","tag":"lark_md"}},{"tag":"column_set","flex_mode":"none","background_style":"default","columns":[{"tag":"column","width":"weighted","weight":1,"vertical_align":"top","elements":[{"tag":"markdown","content":"${problem_item_name} : ${problem_item_value}"}]},{"tag":"column","width":"weighted","weight":1,"vertical_align":"top","elements":[]}]},{"tag":"column_set","flex_mode":"trisect","background_style":"default","columns":[]},{"tag":"column_set","flex_mode":"bisect","background_style":"default","columns":[]},{"tag":"hr"},{"tag":"div","text":{"content":"告警来源:${from}","tag":"plain_text"}},{"actions":[{"tag":"button","text":{"content":"转运维中心","tag":"plain_text"},"type":"danger","multi_url":{"url":"${message_url}","pc_url":"","android_url":"","ios_url":""}}],"tag":"action"}],"header":{"template":"green","title":{"content":"【${event_id}】故障恢复:${event_name}","tag":"plain_text"}}},"mock_data":"{\"event_name\":\"存储设备失效\",\"event_id\":\"114514\",\"event_date\":\"2023-12-03\",\"event_time\":\"10:36:00\",\"host_name\":\"Huawei\",\"event_severity\":\"严重\",\"message_url\":\"\",\"trigger_name\":\"\",\"problem_item_name\":\"Disk0\",\"problem_item_value\":\"处于临界状态0/0\",\"host_ip\":\"10.10.10.10\",\"from\":\"\",\"event_duration\":\"30分钟\"}","variables":[{"variable_id":"7307557781883142145","type":"Text","name":"event_date","description":"事件日期","config":"{}","create_time":1701583781,"update_time":1701583781},{"type":"Text","name":"event_id","description":"事件ID","config":"{}","create_time":1701583781,"update_time":1701583781,"variable_id":"7307557781883125761"},{"config":"{}","create_time":1701583781,"update_time":1701583781,"variable_id":"7307557781883109377","type":"Text","name":"event_name","description":"事件名称"},{"update_time":1701583781,"variable_id":"7307557781883191297","type":"Text","name":"event_severity","description":"严重性等级","config":"{}","create_time":1701583781},{"update_time":1701583781,"variable_id":"7307557781883158529","type":"Text","name":"event_time","description":"事件时间","config":"{}","create_time":1701583781},{"variable_id":"7307557781883289601","type":"Text","name":"from","description":"告警来源","config":"{}","create_time":1701583781,"update_time":1701583781},{"description":"设备IP","config":"{}","create_time":1701583781,"update_time":1701583781,"variable_id":"7307557781883273217","type":"Text","name":"host_ip"},{"config":"{}","create_time":1701583781,"update_time":1701583781,"variable_id":"7307557781883174913","type":"Text","name":"host_name","description":"设备名称"},{"name":"message_url","description":"消息链接","config":"{\"is_multi_url\":false}","create_time":1701583781,"update_time":1701583781,"variable_id":"7307557781883207681","type":"Link"},{"variable_id":"7307557781883240449","type":"Text","name":"problem_item_name","description":"问题详情名称","config":"{}","create_time":1701583781,"update_time":1701583781},{"variable_id":"7307557781883256833","type":"Text","name":"problem_item_value","description":"问题详情值","config":"{}","create_time":1701583781,"update_time":1701583781},{"variable_id":"7307557781883224065","type":"Text","name":"trigger_name","description":"触发器","config":"{}","create_time":1701583781,"update_time":1701583781},{"type":"Text","name":"event_duration","description":"事件持续时间","variable_id":0,"config":"{}"}]} |
{"templateId":"ctp_AAVTQ2mY3Fsz","content":{"elements":[{"tag":"column_set","flex_mode":"stretch","background_style":"default","columns":[{"tag":"column","width":"weighted","weight":1,"vertical_align":"top","elements":[{"tag":"markdown","content":"**🔴 所属设备:**\n[${host_ip}] ${host_name} "}]},{"tag":"column","width":"weighted","weight":1,"vertical_align":"top","elements":[{"tag":"markdown","content":"**⚠️ 严重性等级:**\n${event_severity}"}]}]},{"tag":"hr"},{"tag":"markdown","content":"**🕐 发生时间:**\n${event_date} ${event_time}"},{"tag":"div","text":{"content":"** 👀 诊断信息 **","tag":"lark_md"}},{"tag":"column_set","flex_mode":"none","background_style":"default","columns":[{"tag":"column","width":"weighted","weight":1,"vertical_align":"top","elements":[{"tag":"markdown","content":"${problem_item_name} : ${problem_item_value}"}]}]},{"tag":"hr"},{"tag":"div","text":{"content":"告警来源:${from}","tag":"plain_text"}},{"tag":"action","actions":[{"tag":"button","text":{"tag":"plain_text","content":"跳转到运维中心"},"type":"danger","multi_url":{"url":"${message_url}","pc_url":"","android_url":"","ios_url":""}}]},{"tag":"column_set","flex_mode":"trisect","background_style":"default","columns":[]},{"tag":"column_set","flex_mode":"bisect","background_style":"default","columns":[]}],"header":{"template":"red","title":{"content":"【${event_id}】故障:${event_name}","tag":"plain_text"}}},"mock_data":"{\"event_name\":\"存储设备失效\",\"event_id\":\"114514\",\"event_date\":\"2023-12-03\",\"event_time\":\"10:36:00\",\"host_name\":\"Huawei\",\"event_severity\":\"严重\",\"message_url\":\"\",\"trigger_name\":\"\",\"problem_item_name\":\"Disk0\",\"problem_item_value\":\"处于临界状态0/0\",\"host_ip\":\"10.10.10.10\",\"from\":\"\"}","variables":[{"type":"Text","name":"event_name","description":"事件名称","variable_id":0,"config":"{}"},{"type":"Text","name":"event_id","description":"事件ID","variable_id":0,"config":"{}"},{"type":"Text","name":"event_date","description":"事件日期","variable_id":0,"config":"{}"},{"type":"Text","name":"event_time","description":"事件时间","variable_id":0,"config":"{}"},{"type":"Text","name":"host_name","description":"设备名称","variable_id":0,"config":"{}"},{"type":"Text","name":"event_severity","description":"严重性等级","variable_id":0,"config":"{}"},{"type":"Link","name":"message_url","description":"消息链接","variable_id":0,"config":"{\"is_multi_url\":false}"},{"type":"Text","name":"trigger_name","description":"触发器","variable_id":0,"config":"{}"},{"type":"Text","name":"problem_item_name","description":"问题详情名称","variable_id":0,"config":"{}"},{"type":"Text","name":"problem_item_value","description":"问题详情值","variable_id":0,"config":"{}"},{"type":"Text","name":"host_ip","description":"设备IP","variable_id":0,"config":"{}"},{"type":"Text","name":"from","description":"告警来源","variable_id":0,"config":"{}"}]} |
你可以直接下载另存为 json 文件,登录 飞书开放平台 - 消息卡片搭建工具,选择导入卡片,导入刚才下载的卡片配置文件。
导入之后,你可以对卡片结构或者内容进行微调,调整完毕后,点击“保存并发布”即可。
这个卡片 ID 很重要,记得存一下,一会要用的。
创建一个飞书告警媒介
在 ZABBIX 中创建一个新的报警媒介,类型选择 Webhook,参数有三个,参考下表。
1 | Botkey: 飞书Webhook地址后面那段 |
脚本从下面这个 Gist 中获取。以下内容托管在 GitHub,如果看不到,请确认您是否有访问权限。
// Botkey : FeishuBot Key | |
// Message : {ALERT.MESSAGE} | |
// HTTPProxy | |
try { | |
var params = JSON.parse(value), | |
req = new HttpRequest(), | |
response; | |
if (params.HTTPProxy) { | |
req.setProxy(params.HTTPProxy); | |
} | |
if (params.Message === 'test') { | |
var data = { | |
"msg_type": "interactive", | |
"card": { | |
"type": "template", | |
"data": { | |
"template_id": "", | |
"template_variable": { | |
"event_name": "{EVENT.NAME}", | |
"event_id": "{EVENT.ID}", | |
"event_date": "{EVENT.DATE}", | |
"event_time": "{EVENT.TIME}", | |
"host_name": "{HOST.NAME}", | |
"event_severity": "{TRIGGER.SEVERITY}", | |
"message_url": "", | |
"trigger_name": "{TRIGGER.NAME}", | |
"problem_item_name": "{ITEM.NAME}", | |
"problem_item_value": "{ITEM.VALUE}", | |
"host_ip": "{HOST.IP}", | |
"from": "ZABBIX" | |
} | |
} | |
} | |
}; | |
} else { | |
var data = JSON.parse(params.Message); | |
} | |
req.addHeader('Content-Type: application/json'); | |
Zabbix.log(4, '[ FeishuBot WebHook ] Webhook request with value=' + value); | |
response = req.post('https://open.feishu.cn/open-apis/bot/v2/hook/' + params.Botkey, JSON.stringify(data)); | |
Zabbix.log(4, '[ FeishuBot WebHook ] Responded with code: ' + req.getStatus() + '. Response: ' + response); | |
try { | |
response = JSON.parse(response); | |
} | |
catch (error) { | |
if (req.getStatus() < 200 || req.getStatus() >= 300) { | |
throw 'Request failed with status code ' + req.getStatus(); | |
} | |
else { | |
throw 'Request success, but response parsing failed.'; | |
} | |
} | |
if (req.getStatus() !== 200 || response.msg !== 'success') { | |
throw response.msg; | |
} | |
return 'OK'; | |
} | |
catch (error) { | |
Zabbix.log(3, '[ FeishuBot WebHook ] Sending failed. Error: ' + error); | |
throw 'Failed with error: ' + error; | |
} |
配置消息模板,下面这个是告警模板,你需要替换【】中的内容,替换后,不需要加【】。
1 | { |
这个是告警恢复模板。
1 | { |
添加完是这个样子的。保存即可。
用户关联报警媒介
对用户关联一下刚才创建的报警媒介。
结语
好了,现在就可以愉快的接收 ZABBIX 飞书告警了。