其他-01 zabbix语音告警实现

  • 说明
    • 目前公司监控告警体系已初步实现,但在告警通知渠道上尚需优化
    • 为了更好保障平台稳定性,理论上需要24小时留意平台告警,故在非值班时间段使用语音告警

1 语音服务资质申请

  • 需要材料如下(阿里云)
    • 营业执照,加盖公司公章
    • 法人身份证正反面复印件,加盖公司公章
    • 管理员身份证正反面复印件,加盖公司公章
    • 管理员手持身份证照片,不需要盖公章
    • 入网承诺书,加盖公司公章
    • 号码申请公函,加盖公司公章

:语音服务不对个人用户开放

2 添加告警模板

  • 操作如下
    • 路径:语音服务控制台左侧边栏-语音消息-语音通知

    • 模板添加完成后,会得到模板ID

    image-20240122154927125

    • 模板示例如下,其中变量放在${}中
    您好,监控系统监测到新告警,信息如下:${alarm_info} , 请您务必第一时间确认并处理
    

3 调整SDK

  • 操作如下
    • 此次操作目标是将模板文本转成语音,所以用到如下接口
    # 接口名称:SingleCallByTts
    # 官方接口文档
    https://help.aliyun.com/zh/vms/developer-reference/api-dyvmsapi-2017-05-25-singlecallbytts?spm=a2c4g.11186623.0.0.47171308CFGsZB
    
    • 可以从官方接口文档下载对应接口SDK工程本地调试

    image-20240122170542679

    image-20240122170925395

    • 后续可根据实际需求,对工程中的代码进行调整

    • 本次选择go语言的工程,调整后的main.go代码如下:

    // This file is auto-generated, don't edit it. Thanks.
    package main
    
    import (
    "encoding/json"
    "fmt"
    openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
    dyvmsapi20170525 "github.com/alibabacloud-go/dyvmsapi-20170525/v3/client"
    console "github.com/alibabacloud-go/tea-console/client"
    util "github.com/alibabacloud-go/tea-utils/v2/service"
    "github.com/alibabacloud-go/tea/tea"
    "os"
    "strings"
    )
    
    /**
    * 使用AK&SK初始化账号Client
    * @param accessKeyId
    * @param accessKeySecret
    * @return Client
    * @throws Exception
    */
    func CreateClient(accessKeyId *string, accessKeySecret *string) (_result *dyvmsapi20170525.Client, _err error) {
    config := &openapi.Config{
        // 必填,您的 AccessKey ID
        AccessKeyId: accessKeyId,
        // 必填,您的 AccessKey Secret
        AccessKeySecret: accessKeySecret,
    }
    // Endpoint 请参考 https://api.aliyun.com/product/Dyvmsapi
    config.Endpoint = tea.String("dyvmsapi.aliyuncs.com")
    _result = &dyvmsapi20170525.Client{}
    _result, _err = dyvmsapi20170525.NewClient(config)
    return _result, _err
    }
    
    func _main(args []*string) (_err error) {
    // 原本读取的是本地环境变量,这里直接写死
    ALIBABA_CLOUD_ACCESS_KEY_ID := "LTAI***************BqHsR"
    ALIBABA_CLOUD_ACCESS_KEY_SECRET := "wMv**************6tU5v"
    // 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
    // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378661.html
    //client, _err := CreateClient(tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")), tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")))
    client, _err := CreateClient(&ALIBABA_CLOUD_ACCESS_KEY_ID, &ALIBABA_CLOUD_ACCESS_KEY_SECRET)
    if _err != nil {
        return _err
    }
    // 把告警信息组装成json格式
    tempStr := "{\"alarm_info\":" + "\"" + *args[2] + "\"}"
    // 组合请求
    singleCallByTtsRequest := &dyvmsapi20170525.SingleCallByTtsRequest{
        CalledNumber: args[0],
        TtsCode:      args[1],
        PlayTimes:    tea.Int32(2),
        Volume:       tea.Int32(70),
        Speed:        tea.Int32(-15),
        // 这里有调整
        TtsParam:     &tempStr,
    }
    
    runtime := &util.RuntimeOptions{}
    tryErr := func() (_e error) {
        defer func() {
            if r := tea.Recover(recover()); r != nil {
                _e = r
            }
        }()
        resp, _err := client.SingleCallByTtsWithOptions(singleCallByTtsRequest, runtime)
        if _err != nil {
            return _err
        }
    
        console.Log(util.ToJSONString(resp))
    
        return nil
    }()
    
    if tryErr != nil {
        var error = &tea.SDKError{}
        if _t, ok := tryErr.(*tea.SDKError); ok {
            error = _t
        } else {
            error.Message = tea.String(tryErr.Error())
        }
        // 错误 message
        fmt.Println(tea.StringValue(error.Message))
        // 诊断地址
        var data interface{}
        d := json.NewDecoder(strings.NewReader(tea.StringValue(error.Data)))
        d.Decode(&data)
        if m, ok := data.(map[string]interface{}); ok {
            recommend, _ := m["Recommend"]
            fmt.Println(recommend)
        }
        _, _err = util.AssertAsString(error.Message)
        if _err != nil {
            return _err
        }
    }
    return _err
    }
    
    func main() {
    err := _main(tea.StringSlice(os.Args[1:]))
    if err != nil {
        panic(err)
    }
    }
    
    • window本地调试好后,在linux环境进行编译
    # 执行下面命令会生成  call_pro_one  的命令(产物)
    go build -o call_pro_one main/main.go
    # 执行这个命令时需传入三个参数:  手机号  模板ID  告警信息
    ./call_pro_one  12345678958 TTS_2****305   "主机192.168.26.1 /data磁盘使用率达到90%"
    # 之后会收到语音电话
    

4 添加媒介和动作

  • 操作步骤如下
    • 添加电话媒介
    # zabbix路径: Administration -->  Media types , 选择右上角 "Create media type"
    

    image-20240122183149980

    • 添加告警动作
    # zabbix路径: Configuration --> Actions , 选择右上角 "Create action"
    # 这里条件设置了只有Disaster级别的告警才会语音告警,恢复告警就不用打电话了,这个逻辑在call_phone.sh脚本中控制
    

    image-20240122183337348

    image-20240122183455761

    image-20240122183516487

5 call_phone.sh

  • 操作如下
    • 脚本存放路径
    # 一般放在zabbix安装目录下的scripts目录中
    如: /usr/local/zabbix/scripts
    
    • 脚本内容如下
    #!/bin/bash
    # 创建日期:2024年1月18号
    # 创建人:羞涩梦
    # 用途:接收告警信息,调用阿里云语音服务接口将告警信息通过语音方式通知给相应负责人响应处理
    
    ### 变量定义区
    # 定义告警呼叫电话号码,数组形式
    phoneNumbers=(17******682 17******912)
    #phoneNumbers=(17******682)
    # 接收zabbix界面传过来的变量
    sendTo=1
    alarmTopic=2
    # 过滤掉所有空格
    alarmInfo=`echo 3 | sed -rn 's# ##gp'`
    # 测试用
    #alarmInfo="vpc-huidu-02 /data磁盘使用率超过99%"
    # 语音通知模板ID
    voiceTemplateId="TTS_2******5"
    # 日志路径
    logFile=/usr/local/zabbix/logs/call.log
    # 当前时间
    currentTime=`date +"%Y-%m-%d %H:%M:%S"`
    
    ### 函数区
    
    # 执行语音呼叫函数
    function callPhone(){
    # 遍历数组,逐个呼叫
    for i in{phoneNumbers[@]};do
      # 执行告警呼叫
      callResult=`call ivoiceTemplateId alarmInfo`
      # 获取告警呼叫状态
      callStatu=`echocallResult | awk -F ":" '{print 19 }' | grep OK | wc -l`
      # 写入日志
      if [callStatu -eq 1 ];then
        echo "currentTime 告警语音呼叫i 成功" >> logFile
      else
        echo "currentTime 告警语音呼叫 i 失败" >>logFile
      fi
    done
    }
    
    ### 功能区
    # 日志分割条
    echo "---------------------------------------------------------------------" > logFile
    # 只有告警信息才呼叫
    keyWrod=`echoalarmTopic | grep "发生" | wc -l`
    # 如果有值则告警
    if [ $keyWrod -ge 1 ];then
    callPhone
    fi
    
    • 后续自己设定一个监控项去演示告警