夜鶯簡訊告警教程

SRETALK發表於2024-11-28

夜鶯監控(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/

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

相關文章