夜鶯監控(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/
好了,整個流程講到這裡相信你已經可以搞定了。如果你搞定了,真切的懇求你寫一篇部落格分享一下。因為大家的背景站的角度不同,我們提供的文件和教程有些人可能看不懂,你寫的部落格可能會幫助到他們,感謝,咱們雙贏 :)