夜莺短信告警教程
夜莺监控(Nightingale)已经内置支持了邮件、钉钉、飞书、企微等多种通知机制,但是没有内置支持电话、短信等方式,是因为邮件、钉钉、企微、飞书等方式是协议固定的,但是电话、短信的通知方式,各家不同,一个是短信通道供应商不同,一个是各家封装的电话、短信接口不同,所以夜莺没有内置支持。
不过好在夜莺支持通过自定义脚本的方式对接新的通知媒介,只要你会写脚本,就可以接入任何通知媒介,电话、短信自然也不在话下。本文会以短信通知为例,讲解个中原理和配置方式。
告警通知这块,不仅仅是把告警消息投递出去,还需要告警降噪、按规则分派、排班、认领、升级、和 IM 打通、统计分析等更多诉求,我们建议您使用 FlashDuty 来作为一站式告警 OnCall 响应平台,可以对接 Nightingale、Prometheus、Zabbix、各类云监控,把众多监控系统的告警集中到一个中心平台来处理,提升告警处理效率。当然,也省去了您编写脚本的麻烦。
1. 增加短信通知媒介
菜单位置:告警通知-通知设置-通知媒介
。点击下面的添加,增加一个新的通知媒介,名称就叫“短信”即可,标识就叫“sms”即可,如下图所示:
增加通知媒介之后,在告警规则的配置页面,就可以勾选“短信”这个通知媒介了,如下图所示:
2. 编写短信通知模板
每一种通知媒介,都有对应的通知模板,啥意思呢?告警事件原本的格式是一条大 JSON,显然没法直接发出去,通过邮件发的时候要把这个 JSON 中的重要字段提取出来拼成 HTML 格式的邮件,发钉钉的时候要把这个 JSON 中的重要字段提取出来拼成 markdown 格式的消息,发短信的时候也是一样,要把这个 JSON 中的重要字段提取出来拼成短信的格式。
短信一般是普通文本字符串,有长度限制,咱们尽量只放重要内容,比如告警标题、告警内容、告警级别、告警时间等等,不要放太多无关紧要的信息,因为短信费用是按字数算的。
菜单入口在:告警通知-通知模板
,创建一个短信模板,标识姑且也叫 sms,内容就是你想要发送的短信内容,比如:
**级别状态**: {{if .IsRecovered}}S{{.Severity}} Recovered{{else}}S{{.Severity}} Triggered{{end}}
**规则标题**: {{.RuleName}}
**监控指标**: {{.TagsJSON}}
{{- if not .IsRecovered}}
**触发时值**: {{.TriggerValue}}
{{- end}}
**发送时间**: {{timestamp}}
这个内容是可以自定义的,遵从 go template 语法,你可以参考其他的模板学习具体的写法。
n9e 的进程在调用通知脚本的时候,会把告警事件作为变量传给各个通知模板,进而渲染出最终的通知内容,然后把渲染结果+事件详情传给脚本,脚本中就可以直接取用了,无需再去处理模板渲染逻辑。
3. 编写短信通知脚本
夜莺告警的时候,可以自动调用通知脚本,你就在通知脚本里写你的逻辑就可以了。脚本需要先启用,菜单入口:告警通知-通知设置-通知脚本
,如下图:
我这里设置为启用,超时时间 10s,然后贴了一段脚本内容,内容如下:
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import sys
import json
class Sender(object):
@classmethod
def send_email(cls, payload):
# already done in go code
pass
@classmethod
def send_wecom(cls, payload):
# already done in go code
pass
@classmethod
def send_dingtalk(cls, payload):
# already done in go code
pass
@classmethod
def send_feishu(cls, payload):
# already done in go code
pass
@classmethod
def send_mm(cls, payload):
# already done in go code
pass
@classmethod
def send_sms(cls, payload):
users = payload.get('event').get("notify_users_obj")
phones = {}
for u in users:
if u.get("phone"):
phones[u.get("phone")] = 1
if phones:
print("send_sms not implemented, phones: {}".format(phones.keys()))
@classmethod
def send_voice(cls, payload):
users = payload.get('event').get("notify_users_obj")
phones = {}
for u in users:
if u.get("phone"):
phones[u.get("phone")] = 1
if phones:
print("send_voice not implemented, phones: {}".format(phones.keys()))
def main():
payload = json.load(sys.stdin)
with open(".payload", 'w') as f:
f.write(json.dumps(payload, indent=4))
for ch in payload.get('event').get('notify_channels'):
send_func_name = "send_{}".format(ch.strip())
if not hasattr(Sender, send_func_name):
print("function: {} not found", send_func_name)
continue
send_func = getattr(Sender, send_func_name)
send_func(payload)
def hello():
print("hello nightingale")
if __name__ == "__main__":
if len(sys.argv) == 1:
main()
elif sys.argv[1] == "hello":
hello()
else:
print("I am confused")
显然这是一段 Python 脚本,比较简单,入口是 main 函数,从 stdin 中读取内容,然后根据告警事件中的通知渠道,调用 Sender 类中对应的发送函数,比如 send_sms、send_voice 等等,这里只是打印了一下,实际上应该是调用短信供应商的接口,把短信发送出去。
我来详细解释一下:
payload = json.load(sys.stdin)
夜莺 n9e 进程会把告警事件 encode 成一个 json,通过 stdin 的方式传给脚本,脚本里自然就通过 sys.stdin 的方式接收,因为是 json 字符串,使用 json.load 解码成 payload 对象- 然后把 payload 的内容打印到
.payload
文件中,方便调试,方便我们了解夜莺进程到底发过来了个什么内容
下面我把我的环境里的 .payload
贴到下面,怎么找到 .payload
呢?去 n9e 二进制的同级目录下找,注意这是一个隐藏文件。
{
"event": {
"id": 1,
"cate": "prometheus",
"cluster": "vm01",
"datasource_id": 1,
"group_id": 1,
"group_name": "Default Busi Group",
"hash": "ac7546f009158fe945933780cd2f3ff3",
"rule_id": 1,
"rule_name": "test sms alerting",
"rule_note": "",
"rule_prod": "metric",
"rule_algo": "",
"severity": 2,
"prom_for_duration": 0,
"prom_ql": "cpu_usage_active{ident=\"mac-vm-001\"}>0",
"rule_config": {
"queries": [
{
"prom_ql": "cpu_usage_active{ident=\"mac-vm-001\"}>0",
"severity": 2,
"unit": "none"
}
]
},
"prom_eval_interval": 10,
"callbacks": [],
"runbook_url": "",
"notify_recovered": 1,
"notify_channels": [
"sms"
],
"notify_groups": [
"1"
],
"notify_groups_obj": [
{
"id": 1,
"name": "demo-root-group",
"note": "",
"create_at": 1732094402,
"create_by": "root",
"update_at": 1732094402,
"update_by": "root",
"users": null
}
],
"target_ident": "mac-vm-001",
"target_note": "",
"trigger_time": 1732756597,
"trigger_value": "5.03804",
"trigger_values": "",
"trigger_values_json": {
"values_with_unit": {
"v": {
"value": 5.038038883421,
"unit": "",
"text": "5.04",
"stat": 5.038038883421
}
}
},
"tags": [
"__name__=cpu_usage_active",
"cpu=cpu-total",
"ident=mac-vm-001",
"rulename=test sms alerting"
],
"tags_map": {
"__name__": "cpu_usage_active",
"cpu": "cpu-total",
"ident": "mac-vm-001",
"rulename": "test sms alerting"
},
"original_tags": null,
"annotations": {},
"is_recovered": false,
"notify_users_obj": [
{
"id": 1,
"username": "root",
"nickname": "\u8d85\u7ba1",
"phone": "18612185520",
"email": "",
"portrait": "/image/avatar1.png",
"roles": [
"Admin"
],
"contacts": {},
"maintainer": 0,
"create_at": 1732094402,
"create_by": "system",
"update_at": 1732756566,
"update_by": "root",
"belong": "",
"admin": true,
"user_groups": null,
"busi_groups": null,
"last_active_time": 1732756588
}
],
"last_eval_time": 1732756597,
"last_sent_time": 1732756597,
"notify_cur_number": 1,
"first_trigger_time": 1732756597,
"extra_config": null,
"status": 0,
"claimant": "",
"sub_rule_id": 0,
"extra_info": null,
"target": {
"id": 2,
"group_id": 0,
"group_objs": null,
"ident": "mac-vm-001",
"note": "",
"tags": [],
"tags_maps": {},
"update_at": 1732756595,
"host_ip": "10.211.55.3",
"agent_version": "v0.3.60",
"engine_name": "default",
"os": "linux",
"host_tags": null,
"unixtime": 1732756586936,
"offset": 18,
"target_up": 2,
"mem_util": 17.314975929184705,
"cpu_num": 4,
"cpu_util": 4.303797466603506,
"arch": "arm64",
"remote_addr": "172.27.0.1",
"group_ids": null,
"group_names": []
},
"recover_config": {
"judge_type": 0,
"recover_exp": ""
},
"rule_hash": "3abc1f400d7338cae3785ecb826eac7c",
"extra_info_map": null
},
"tpls": {
"dingtalk": "#### <font color=\"#FF0000\">\ud83d\udc94test sms alerting</font>\n\n---\n\n- **\u544a\u8b66\u7ea7\u522b**: 2\u7ea7\n- **\u5f53\u6b21\u89e6\u53d1\u65f6\u503c**: 5.03804\n- **\u5f53\u6b21\u89e6\u53d1\u65f6\u95f4**: 2024-11-28 09:16:37\n- **\u544a\u8b66\u6301\u7eed\u65f6\u957f**: 0s\n- **\u544a\u8b66\u4e8b\u4ef6\u6807\u7b7e**:\n\t- __name__: cpu_usage_active\n\t- cpu: cpu-total\n\t- ident: mac-vm-001\n \n[\u4e8b\u4ef6\u8be6\u60c5](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-his-events/1)|[\u5c4f\u853d1\u5c0f\u65f6](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-mutes/add?busiGroup=1&cate=prometheus&datasource_ids=1&prod=metric&tags=__name__%3Dcpu_usage_active&tags=cpu%3Dcpu-total&tags=ident%3Dmac-vm-001&tags=rulename%3Dtest sms alerting)|[\u67e5\u770b\u66f2\u7ebf](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/metric/explorer?data_source_id=1&data_source_name=prometheus&mode=graph&prom_ql=cpu_usage_active%7Bident=%22mac-vm-001%22%7D%3E0)",
"email": "<!DOCTYPE html>\n\t<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\">\n\t\t<meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n\t\t<title>\u591c\u83ba\u544a\u8b66\u901a\u77e5</title>\n\t\t<style type=\"text/css\">\n\t\t\t.wrapper {\n\t\t\t\tbackground-color: #f8f8f8;\n\t\t\t\tpadding: 15px;\n\t\t\t\theight: 100%;\n\t\t\t}\n\t\t\t.main {\n\t\t\t\twidth: 600px;\n\t\t\t\tpadding: 30px;\n\t\t\t\tmargin: 0 auto;\n\t\t\t\tbackground-color: #fff;\n\t\t\t\tfont-size: 12px;\n\t\t\t\tfont-family: verdana,'Microsoft YaHei',Consolas,'Deja Vu Sans Mono','Bitstream Vera Sans Mono';\n\t\t\t}\n\t\t\theader {\n\t\t\t\tborder-radius: 2px 2px 0 0;\n\t\t\t}\n\t\t\theader .title {\n\t\t\t\tfont-size: 14px;\n\t\t\t\tcolor: #333333;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\t\t\theader .sub-desc {\n\t\t\t\tcolor: #333;\n\t\t\t\tfont-size: 14px;\n\t\t\t\tmargin-top: 6px;\n\t\t\t\tmargin-bottom: 0;\n\t\t\t}\n\t\t\thr {\n\t\t\t\tmargin: 20px 0;\n\t\t\t\theight: 0;\n\t\t\t\tborder: none;\n\t\t\t\tborder-top: 1px solid #e5e5e5;\n\t\t\t}\n\t\t\tem {\n\t\t\t\tfont-weight: 600;\n\t\t\t}\n\t\t\ttable {\n\t\t\t\tmargin: 20px 0;\n\t\t\t\twidth: 100%;\n\t\t\t}\n\t\n\t\t\ttable tbody tr{\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 12px;\n\t\t\t\tcolor: #666;\n\t\t\t\theight: 32px;\n\t\t\t}\n\t\n\t\t\t.succ {\n\t\t\t\tbackground-color: green;\n\t\t\t\tcolor: #fff;\n\t\t\t}\n\t\n\t\t\t.fail {\n\t\t\t\tbackground-color: red;\n\t\t\t\tcolor: #fff;\n\t\t\t}\n\t\n\t\t\t.succ th, .succ td, .fail th, .fail td {\n\t\t\t\tcolor: #fff;\n\t\t\t}\n\t\n\t\t\ttable tbody tr th {\n\t\t\t\twidth: 80px;\n\t\t\t\ttext-align: right;\n\t\t\t}\n\t\t\t.text-right {\n\t\t\t\ttext-align: right;\n\t\t\t}\n\t\t\t.body {\n\t\t\t\tmargin-top: 24px;\n\t\t\t}\n\t\t\t.body-text {\n\t\t\t\tcolor: #666666;\n\t\t\t\t-webkit-font-smoothing: antialiased;\n\t\t\t}\n\t\t\t.body-extra {\n\t\t\t\t-webkit-font-smoothing: antialiased;\n\t\t\t}\n\t\t\t.body-extra.text-right a {\n\t\t\t\ttext-decoration: none;\n\t\t\t\tcolor: #333;\n\t\t\t}\n\t\t\t.body-extra.text-right a:hover {\n\t\t\t\tcolor: #666;\n\t\t\t}\n\t\t\t.button {\n\t\t\t\twidth: 200px;\n\t\t\t\theight: 50px;\n\t\t\t\tmargin-top: 20px;\n\t\t\t\ttext-align: center;\n\t\t\t\tborder-radius: 2px;\n\t\t\t\tbackground: #2D77EE;\n\t\t\t\tline-height: 50px;\n\t\t\t\tfont-size: 20px;\n\t\t\t\tcolor: #FFFFFF;\n\t\t\t\tcursor: pointer;\n\t\t\t}\n\t\t\t.button:hover {\n\t\t\t\tbackground: rgb(25, 115, 255);\n\t\t\t\tborder-color: rgb(25, 115, 255);\n\t\t\t\tcolor: #fff;\n\t\t\t}\n\t\t\tfooter {\n\t\t\t\tmargin-top: 10px;\n\t\t\t\ttext-align: right;\n\t\t\t}\n\t\t\t.footer-logo {\n\t\t\t\ttext-align: right;\n\t\t\t}\n\t\t\t.footer-logo-image {\n\t\t\t\twidth: 108px;\n\t\t\t\theight: 27px;\n\t\t\t\tmargin-right: 10px;\n\t\t\t}\n\t\t\t.copyright {\n\t\t\t\tmargin-top: 10px;\n\t\t\t\tfont-size: 12px;\n\t\t\t\ttext-align: right;\n\t\t\t\tcolor: #999;\n\t\t\t\t-webkit-font-smoothing: antialiased;\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t<div class=\"wrapper\">\n\t\t<div class=\"main\">\n\t\t\t<header>\n\t\t\t\t<h3 class=\"title\">test sms alerting</h3>\n\t\t\t\t<p class=\"sub-desc\"></p>\n\t\t\t</header>\n\t\n\t\t\t<hr>\n\t\n\t\t\t<div class=\"body\">\n\t\t\t\t<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\n\t\t\t\t\t<tr class=\"fail\">\n\t\t\t\t\t\t<th>\u7ea7\u522b\u72b6\u6001\uff1a</th>\n\t\t\t\t\t\t<td>S2 Triggered</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t\n\t\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>\u7b56\u7565\u5907\u6ce8\uff1a</th>\n\t\t\t\t\t\t<td></td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>\u8bbe\u5907\u5907\u6ce8\uff1a</th>\n\t\t\t\t\t\t<td></td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>\u89e6\u53d1\u65f6\u503c\uff1a</th>\n\t\t\t\t\t\t<td>5.03804</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t\n\t\n\t\t\t\t\t\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>\u76d1\u63a7\u5bf9\u8c61\uff1a</th>\n\t\t\t\t\t\t<td>mac-vm-001</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>\u76d1\u63a7\u6307\u6807\uff1a</th>\n\t\t\t\t\t\t<td>[__name__=cpu_usage_active cpu=cpu-total ident=mac-vm-001 rulename=test sms alerting]</td>\n\t\t\t\t\t</tr>\n\t\n\t\t\t\t\t\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>\u89e6\u53d1\u65f6\u95f4\uff1a</th>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t2024-11-28 09:16:37\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t\n\t\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>\u53d1\u9001\u65f6\u95f4\uff1a</th>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t2024-11-28 09:16:37\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\n\t\t\t\t<hr>\n\t\n\t\t\t\t<footer>\n\t\t\t\t\t<div class=\"copyright\" style=\"font-style: italic\">\n\t\t\t\t\t\t\u62a5\u8b66\u592a\u591a\uff1f\u4f7f\u7528 <a href=\"https://flashcat.cloud/product/flashduty/\" target=\"_blank\">FlashDuty</a> \u505a\u544a\u8b66\u805a\u5408\u964d\u566a\u3001\u6392\u73edOnCall\uff01\n\t\t\t\t\t</div>\n\t\t\t\t</footer>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\t</body>\n\t</html>",
"feishu": "\u7ea7\u522b\u72b6\u6001: S2 Triggered \n\u89c4\u5219\u540d\u79f0: test sms alerting \n\u76d1\u63a7\u6307\u6807: [__name__=cpu_usage_active cpu=cpu-total ident=mac-vm-001 rulename=test sms alerting]\n\u89e6\u53d1\u65f6\u95f4: 2024-11-28 09:16:37\n\u89e6\u53d1\u65f6\u503c: 5.03804\n\u53d1\u9001\u65f6\u95f4: 2024-11-28 09:16:37\n \n\u4e8b\u4ef6\u8be6\u60c5: http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-his-events/1\n\u5c4f\u853d1\u5c0f\u65f6: http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-mutes/add?busiGroup=1&cate=prometheus&datasource_ids=1&prod=metric&tags=__name__%3Dcpu_usage_active&tags=cpu%3Dcpu-total&tags=ident%3Dmac-vm-001&tags=rulename%3Dtest sms alerting",
"feishucard": " \n**\u544a\u8b66\u96c6\u7fa4:** vm01 \n**\u7ea7\u522b\u72b6\u6001:** S2 Triggered \n**\u544a\u8b66\u540d\u79f0:** test sms alerting \n**\u89e6\u53d1\u65f6\u95f4:** 2024-11-28 09:16:37 \n**\u53d1\u9001\u65f6\u95f4:** 2024-11-28 09:16:37 \n**\u89e6\u53d1\u65f6\u503c:** 5.03804 \n \n[\u4e8b\u4ef6\u8be6\u60c5](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-his-events/1)|[\u5c4f\u853d1\u5c0f\u65f6](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-mutes/add?busiGroup=1&cate=prometheus&datasource_ids=1&prod=metric&tags=__name__%3Dcpu_usage_active&tags=cpu%3Dcpu-total&tags=ident%3Dmac-vm-001&tags=rulename%3Dtest sms alerting)|[\u67e5\u770b\u66f2\u7ebf](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/metric/explorer?data_source_id=1&data_source_name=prometheus&mode=graph&prom_ql=cpu_usage_active%7Bident=%22mac-vm-001%22%7D%3E0)",
"lark": "\u7ea7\u522b\u72b6\u6001: S2 Triggered \n\u89c4\u5219\u540d\u79f0: test sms alerting \n\u76d1\u63a7\u6307\u6807: [__name__=cpu_usage_active cpu=cpu-total ident=mac-vm-001 rulename=test sms alerting]\n\u89e6\u53d1\u65f6\u95f4: 2024-11-28 09:16:37\n\u89e6\u53d1\u65f6\u503c: 5.03804\n\u53d1\u9001\u65f6\u95f4: 2024-11-28 09:16:37\n \n\u4e8b\u4ef6\u8be6\u60c5: http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-his-events/1\n\u5c4f\u853d1\u5c0f\u65f6: http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-mutes/add?busiGroup=1&cate=prometheus&datasource_ids=1&prod=metric&tags=__name__%3Dcpu_usage_active&tags=cpu%3Dcpu-total&tags=ident%3Dmac-vm-001&tags=rulename%3Dtest sms alerting",
"larkcard": " \n**\u544a\u8b66\u96c6\u7fa4:** vm01 \n**\u7ea7\u522b\u72b6\u6001:** S2 Triggered \n**\u544a\u8b66\u540d\u79f0:** test sms alerting \n**\u89e6\u53d1\u65f6\u95f4:** 2024-11-28 09:16:37 \n**\u53d1\u9001\u65f6\u95f4:** 2024-11-28 09:16:37 \n**\u89e6\u53d1\u65f6\u503c:** 5.03804\n**\u6301\u7eed\u65f6\u957f**: 0s \n \n[\u4e8b\u4ef6\u8be6\u60c5](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-his-events/1)|[\u5c4f\u853d1\u5c0f\u65f6](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-mutes/add?busiGroup=1&cate=prometheus&datasource_ids=1&prod=metric&tags=__name__%3Dcpu_usage_active&tags=cpu%3Dcpu-total&tags=ident%3Dmac-vm-001&tags=rulename%3Dtest sms alerting)|[\u67e5\u770b\u66f2\u7ebf](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/metric/explorer?data_source_id=1&data_source_name=prometheus&mode=graph&prom_ql=cpu_usage_active%7Bident=%22mac-vm-001%22%7D%3E0)",
"mailsubject": "Triggered: test sms alerting [__name__=cpu_usage_active cpu=cpu-total ident=mac-vm-001 rulename=test sms alerting]",
"mm": "\u7ea7\u522b\u72b6\u6001: S2 Triggered \n\u89c4\u5219\u540d\u79f0: test sms alerting \n\u76d1\u63a7\u6307\u6807: [__name__=cpu_usage_active cpu=cpu-total ident=mac-vm-001 rulename=test sms alerting] \n\u89e6\u53d1\u65f6\u95f4: 2024-11-28 09:16:37 \n\u89e6\u53d1\u65f6\u503c: 5.03804 \n\u53d1\u9001\u65f6\u95f4: 2024-11-28 09:16:37",
"sms": "**\u7ea7\u522b\u72b6\u6001**: S2 Triggered \n**\u89c4\u5219\u6807\u9898**: test sms alerting\n**\u76d1\u63a7\u6307\u6807**: [__name__=cpu_usage_active cpu=cpu-total ident=mac-vm-001 rulename=test sms alerting] \n**\u89e6\u53d1\u65f6\u503c**: 5.03804 \n**\u53d1\u9001\u65f6\u95f4**: 2024-11-28 09:16:37\n",
"telegram": "**\u7ea7\u522b\u72b6\u6001**: <font color=\"warning\">S2 Triggered</font> \n**\u89c4\u5219\u6807\u9898**: test sms alerting \n**\u76d1\u63a7\u5bf9\u8c61**: mac-vm-001 \n**\u76d1\u63a7\u6307\u6807**: [__name__=cpu_usage_active cpu=cpu-total ident=mac-vm-001 rulename=test sms alerting] \n**\u89e6\u53d1\u65f6\u503c**: 5.03804 \n**\u9996\u6b21\u89e6\u53d1\u65f6\u95f4**: 2024-11-28 09:16:37 \n**\u8ddd\u79bb\u9996\u6b21\u544a\u8b66**: 0s\n**\u53d1\u9001\u65f6\u95f4**: 2024-11-28 09:16:37",
"wecom": "**\u7ea7\u522b\u72b6\u6001**: <font color=\"warning\">S2 Triggered</font> \n**\u89c4\u5219\u6807\u9898**: test sms alerting \n**\u76d1\u63a7\u5bf9\u8c61**: mac-vm-001 \n**\u76d1\u63a7\u6307\u6807**: [__name__=cpu_usage_active cpu=cpu-total ident=mac-vm-001 rulename=test sms alerting] \n**\u89e6\u53d1\u65f6\u503c**: 5.03804 \n**\u9996\u6b21\u89e6\u53d1\u65f6\u95f4**: 2024-11-28 09:16:37 \n**\u8ddd\u79bb\u9996\u6b21\u544a\u8b66**: 0s\n**\u53d1\u9001\u65f6\u95f4**: 2024-11-28 09:16:37\n \n[\u4e8b\u4ef6\u8be6\u60c5](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-his-events/1)|[\u5c4f\u853d1\u5c0f\u65f6](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/alert-mutes/add?busiGroup=1&cate=prometheus&datasource_ids=1&prod=metric&tags=__name__%3Dcpu_usage_active&tags=cpu%3Dcpu-total&tags=ident%3Dmac-vm-001&tags=rulename%3Dtest sms alerting)|[\u67e5\u770b\u66f2\u7ebf](http://\u8bf7\u8054\u7cfb\u7ba1\u7406\u5458\u4fee\u6539\u901a\u77e5\u6a21\u677f\u5c06\u57df\u540d\u66ff\u6362\u4e3a\u5b9e\u9645\u7684\u57df\u540d/metric/explorer?data_source_id=1&data_source_name=prometheus&mode=graph&prom_ql=cpu_usage_active%7Bident=%22mac-vm-001%22%7D%3E0)"
}
}
- payload 是个大 json,里边有两个顶层字段,一个是 event,表示事件内容,一个是 tpls,表示渲染出来的多个模板结果
- 继续解释那个通知脚本,
for ch in payload.get('event').get('notify_channels')
是循环遍历 payload.event.notify_channels,这个字段表示用户在告警规则里勾选的通知媒介列表,是个数组,上例的话,用户只勾选了短信,所以这个数组里只有一个元素,是sms
- 然后下面的代码是拼接一个函数名称,最终的效果是,如果 notify_channels 里有 sms 和 voice 两个元素的话,就会以此调用 send_sms 和 send_voice 函数。当然了,上例中 notify_channels 里只有 sms,所以只会调用 send_sms 函数,调用 send_sms 函数的时候,会把整个 payload 作为参数传入
- 下面我们来具体看一下 send_sms 函数的实现
@classmethod
def send_sms(cls, payload):
users = payload.get('event').get("notify_users_obj")
phones = {}
for u in users:
if u.get("phone"):
phones[u.get("phone")] = 1
if phones:
print("send_sms not implemented, phones: {}".format(phones.keys()))
要发短信,显然需要两个东西,一个是要发给哪些手机号,即手机号列表,另一个是发送的内容。通过解析 notify_users_obj 可以拿到告警接收人的手机号列表,上例中没有演示如何拿到告警模板渲染之后的通知内容,稍后再说。上例中的代码拿到了手机号并打印到 stdout 了,我们直接做个测试,看看日志里能否看到相关的输出。
3. 测试告警
下面我们做个测试。
3.1 配置告警接收人的手机号
要发短信显然是需要手机号的,先把手机号配置好。假设我想把告警发给 root 账号,那就给 root 账号设置一下手机号:
每个人可以设置自己的手机号,入口在页面右上角,点击自己的头像,进入个人信息设置页面即可设置。
3.2 创建告警接收组
夜莺里的告警接收人是通过告警接收组来管理的,所以我们先创建一个告警接收组,然后把 root 加入进去。
之后,我们在告警规则里选择这个告警接收组作为告警接收人。上面的例子中,告警接收组(也叫团队)可以展示为列表形式,也可以展示为树形结构,是在 系统配置-站点设置
里配置的。
3.3 创建告警规则
创建一个告警规则,入口在:告警管理-告警规则-新增
:
一个公司可能有很多告警规则,为了方便管理,告警规则要归属某个业务组。选中左侧业务组,右侧点击新增即可创建告警规则。如果你看不到业务组列表,要么就是从来都没有创建过业务组,要么是这个区块隐藏了,注意那个收起的 icon,可以通过那个 icon 收起和展开。下面是我创建的告警规则:
我的版本是 7.7.1,首先设置一下告警规则的标题,然后设置一下该规则要生效的数据源,然后配置 promql、执行频率、持续时长,为了尽快触发,我把 promql 设置成了一个必然会触发的条件,把执行频率调小,把持续时长设置为 0。然后下面的通知媒介勾选了短信,并且选择了一个告警接收组。
3.4 触发告警查看日志
保存之后去查看夜莺的日志。我是 docker compose 部署的,直接使用 docker logs -f nightingale
查看夜莺容器的日志了。稍等片刻,就可以看到夜莺进程调用了脚本,并打印了相关日志:
2024-11-28 09:16:37.890954 DEBUG dispatch/dispatch.go:276 no sender for channel: sms
2024-11-28 09:16:37.958345 INFO sender/plugin.go:108 event_script_notify_ok: exec ./.notify_scriptt output: send_sms not implemented, phones: dict_keys(['18612185520'])
你也可以 grep send,就能看到类似上面的两条日志了。第一条 no sender for channel: sms
表示 n9e 的 go 进程里没有内置 sms 这个通知渠道,所以打印了这么一行 DEBUG,可以忽略无关紧要。第二条日志显示,夜莺的 go 进程调用了 notify_scriptt 脚本(脚本的内容就是页面上填写的那个通知脚本的内容),这个脚本有个输出 output,output 的内容是:send_sms not implemented, phones: dict_keys(['18612185520'])
,显然这个消息就是刚刚脚本里 print 的内容,一切都符合预期。
4. 继续完善脚本
上面的流程走到现在,其实已经跑通流程了。告警了之后调用了脚本,脚本里调用了 send_sms 函数,send_sms 函数里拿到了手机号。接下来我们要做的是,继续完善 send_sms 函数,把短信模板渲染之后的内容拿到,然后调用短信发送接口发送短信。
获取短信内容其实比较简单,模仿上面获取 notify_channels 的方式:
content = payload.get('tpls').get("sms", "sms template not found")
所以最终的代码类似:
@classmethod
def send_sms(cls, payload):
content = payload.get('tpls').get("sms", "sms template not found")
users = payload.get('event').get("notify_users_obj")
phones = {}
for u in users:
if u.get("phone"):
phones[u.get("phone")] = 1
if phones:
# 在这个位置调用短信通知接口,把短信内容 content 发给所有的手机号 phones
# 如果你们的短信通知接口每次只能发送一个手机号,那就遍历 phones,逐个发送
一般短信发送接口都是封装的 HTTP 接口,你可以使用 requests 库调用短信发送接口。这里可能会掉坑。就是你的系统环境里没有 requests 这个 module,你需要安装一下,可以使用 pip 安装:
pip install requests
然后就可以在脚本里使用 requests 了。requests 的使用方法可以参考官方文档:https://docs.python-requests.org/en/latest/
好了,整个流程讲到这里相信你已经可以搞定了。如果你搞定了,真切的恳求你写一篇博客分享一下。因为大家的背景站的角度不同,我们提供的文档和教程有些人可能看不懂,你写的博客可能会帮助到他们,感谢,咱们双赢 :)