首页 > 其他分享 >夜莺自定义告警模板

夜莺自定义告警模板

时间:2024-01-07 22:32:09浏览次数:43  
标签:end 自定义 err get 夜莺 promql 告警 event 模板



!! 大家好,我是乔克,一个爱折腾的运维工程,一个睡觉都被自己丑醒的云原生爱好者。

作者:乔克
公众号:运维开发故事
博客:www.jokerbai.com



预期目标

夜莺自定义告警模板_github

Pasted image 20230906090309.png

希望在告警通知里有以下数据:

  • 告知当前系统还有多少未处理的告警
  • 告知当前告警恢复时候的具体值
  • 告警通知里增加查看未处理告警的页面链接

具体实现

要实现上面的需求很简单,夜莺监控的数据库表alert_cur_event保存了我们所需要的当前未处理的告警总数,而且夜莺监控也提供了查询未处理告警的面板,而对于告警恢复时候的值我们只需要根据自定义的恢复promql即可查询。

最简单的方式就是直接通过notify.py脚本进行告警发送,我们只需要做一丢丢修改即可。

整体脚本如下:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import sys
import json
import requests
import pymysql

# 处理字符问题
reload(sys)
sys.setdefaultencoding('utf-8')

# 通过api查询指标
def getPrometheus(url, promql):
    response = requests.get(url, params={'query': promql})
    data = json.loads(response.text)
    # 提取指标数据
    if response.status_code == 200
        result = data['data']['result']
        if len(result) == 1:
            return result[0]['value'][1]
        else:
            return 0
    else:
        return 0

def count_rows_and_get_rule_names():
    try:
        conn = pymysql.connect(
            host='127.0.0.1',
            port=3306,
            user='n9e',
            passwd='1234',
            db='n9e_v6',
            charset='utf8mb4'
        )
        cursor = conn.cursor()

        # Count the total number of rows in the table
        count_query = "SELECT COUNT(*) FROM alert_cur_event"
        cursor.execute(count_query)
        total_rows = cursor.fetchone()[0]
        return total_rows

    except Exception as e:
        print("Error: ", e)


class Sender(object):
    @classmethod
    def send_qywx(cls, payload):
        users = payload.get('event').get("notify_users_obj")
        is_recovered = payload.get('event').get("is_recovered")
        tokens = {}
        phones = {}
        res = {} 

        history_row = count_rows_and_get_rule_names()

        if is_recovered:
            # 获取自定义的恢复promql
            promQL = payload.get('event').get("annotations").get("recovery_promql")
            url = "http://127.0.0.1:9090/api/v1/query"
            res = getPrometheus(url, promQL)

        # 查询活跃告警的面板
        currAlert = "http://127.0.0.1:17000/alert-cur-events"
        for u in users:
            if u.get("phone"):
                phones[u.get("phone")] = 1

            contacts = u.get("contacts")
            if contacts.get("qywx_robot_token", ""):
                tokens[contacts.get("qywx_robot_token", "")] = 1

        headers = {
            "Content-Type": "application/json;charset=utf-8",
            "Host": "qyapi.weixin.qq.com"

        }

        for t in tokens:
            url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={}".format(t)
            content = payload.get('tpls').get("qywx", "qywx not found")
            content = "# **当前环境的全部告警数**: %s" % (history_row) + "\n" + content
            if is_recovered:
                content = content + "\n" + "> **恢复时值**: %s" % (res)

            if history_row > 0:
                content = content + "\n" + "[当前活跃告警](%s)" % (currAlert)
            body = {
                "msgtype": "markdown",
                "markdown": {
                    "content": content
                }
            }

            response = requests.post(url, headers=headers, data=json.dumps(body))
  

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")

需要在服务器上安装pymysql以及requests

然后将上面的脚本放到夜莺监控面板->系统设置->通知设置->通知脚本中,并将脚本设置为启用状态。

夜莺自定义告警模板_夜莺_02

Pasted image 20230906091813.png

然后新增名叫qywx的通知媒介以及名叫qywx_robot_token的联系方式,在发送告警的时候会通过通知媒介来调用通知方法,比如你的通知媒介名叫zhangsan,那么你定义的方法名就是send_zhangsan。另外还会从联系方式处获取发送的token

然后我们来创建一个通知模板,这个模板是在原生的基础上进行更改的,如下创建一个名叫qywx的模板。

> **级别状态**: {{if .IsRecovered}}<font color="info">告警恢复</font>{{else}}<font color="warning">发生告警</font>{{end}}   
> **级别级别**: S{{.Severity}}
> **告警类型**: {{.RuleName}}{{if .RuleNote}}   
> **告警详情**: {{.RuleNote}}{{end}}{{if .TargetIdent}}   
> **监控对象**: {{.TargetIdent}}{{end}}   
> **监控指标**: {{.TagsJSON}}{{if not .IsRecovered}}   
> **触发时值**: {{.TriggerValue}}{{end}}   
{{if .IsRecovered}}> **恢复时间**: {{timeformat .LastEvalTime}}{{else}}> **首次触发时间**: {{timeformat .FirstTriggerTime}}{{end}}   
{{$time_duration := sub now.Unix .FirstTriggerTime }}{{if .IsRecovered}}{{$time_duration = sub .LastEvalTime .FirstTriggerTime }}{{end}}> **距离首次告警**: {{humanizeDurationInterface $time_duration}}
> **发送时间**: {{timestamp}}

在实际发送过程中会对模板进行相应的增加。

最后,再来配置告警,比如我们现在要配置一个K8s中Pod的状态异常的告警规则,如下:

夜莺自定义告警模板_github_03

Pasted image 20230906092542.png

填写具体的规则名以及备注,并且填写具体的promql。

往下继续填写通知媒介以及附加信息。

夜莺自定义告警模板_github_04

Pasted image 20230906092656.png

其中附加信息中就有告警恢复时候的promql,在python脚本中会获取当前的promql,然后调用prometheus的接口进行查询当前值,最后填充到告警模板中去。

以上就是具体的实现思路,希望对你有所启发。

加餐

除了这种python脚本的方式,还可以通过自定义webhook的方式实现,夜莺是支持回调地址的,只需要把回调地址填写进去即可。

那这个webhook应该怎么开发呢?

其实不需要我们做啥大的开发,直接把夜莺的源码里告警相关的CV出来,改吧改吧就能直接用了。

首先,把alert_cur_event的数据结构弄过来,查表就查它。

其次,增加一个查询prometheus的接口,如下:

package prometheus  
  
import (  
   "context"  
   "devops-webhook-service/src/server/config"       "github.com/prometheus/client_golang/api"   "github.com/prometheus/client_golang/api/prometheus/v1"   "github.com/prometheus/common/model"   "github.com/toolkits/pkg/logger"   "time")  
  
func GetMetricsValue(promql string) string {  
   client, err := api.NewClient(api.Config{  
      Address: config.C.Prometheus.Address,  
   })  
   if err != nil {  
      logger.Error("init prometheus client failed. err: ", err)  
   }  
   queryAPI := v1.NewAPI(client)  
   result, warnings, err := queryAPI.Query(context.TODO(), promql, time.Now())  
   if err != nil {  
      // handle error  
      logger.Error("query prometheus metrics failed. err: ", err)  
   }  
   if len(warnings) > 0 {  
      // handle warnings  
   }  
   vector := result.(model.Vector)  
   //for _, sample := range vector {  
   // fmt.Printf("Time: %v, Value: %v\n", sample.Timestamp, sample.Value)   //}  
   return vector[0].Value.String()  
  
}

再则,我们就可以把需要的告警字段都动议到告警模板中,通过template自动填充数据了。

## 当前环境的告警总数: {{ .TotalAlert }}  
---  
**级别状态**: {{if .IsRecovered}}<font color="info">S{{.Severity}} Recovered</font>{{else}}<font color="warning">S{{.Severity}} Triggered</font>{{end}}  
**规则标题**: {{.RuleName}}{{if .TargetIdent}}  
**监控对象**: {{.TargetIdent}}{{end}}{{ if .IsRecovered }}  
**当前值**: {{ .RecoveryValue }}{{end}}  
**监控指标**: {{.TagsJSON}}{{if not .IsRecovered}}  
**触发时值**: {{.TriggerValue}}{{end}}  
{{if .IsRecovered}}**恢复时间**: {{timeformat .LastEvalTime}}{{else}}**首次触发时间**: {{timeformat .TriggerTime}}{{end}}  
**发送时间**: {{timestamp}}  
---

最后,就是在notify.go中做一丢丢的小修改。

比如event事件增加两个字段。

type NoticeEvent struct {  
   *models.AlertCurEvent  
   RecoveryValue string // 恢复时候的值  
   TotalAlert    int    // 告警总数  
}

比如在notify.go中的GenNotice方法里,增加查询prometheus和数据库的代码。

var recoveryValue string  
if event.IsRecovered {  
   text := event.RuleNote  
   promql := strings.Split(text, "=")[1]  
   recoveryValue = prometheus.GetMetricsValue(promql)  
}  
  
// 获取当前剩余的总告警数  
events, err := models.AlertCurEventGetAll(event.Cluster)  
if err != nil {  
   logger.Error("get alert event failed. err: ", err)  
}

整体代码也就只需要一丢丢东西。

夜莺自定义告警模板_夜莺_05

Pasted image 20230906094009.png

最后

以上就是整体的实现了,这只是领导根据领导的需要做的,每个团队的需求不一样,实现方式肯定也不通,这里只是抛砖引玉。

个人建议使用webhook比较好一点,因为可以比较灵活的增加其他的功能,比如告警认领,比如告警抑制,比如告警转发等。

另外,最近刚换工作没多久,写的文章少了,但是对技术的热爱并没有减少。


最后,求关注。如果你还想看更多优质原创文章,欢迎关注我们的公众号「运维开发故事」。


标签:end,自定义,err,get,夜莺,promql,告警,event,模板
From: https://blog.51cto.com/u_12970189/9135321

相关文章

  • 前端学习笔记202311学习笔记第一百贰拾伍天-nodejs-koa-ejs模板之2
     ......
  • 粉色系小白猫登录表单模板html代码
    粉色系登录模板<linkrel="stylesheet"href="css/font-awesome.min.css"type="text/css"media="all"><!--formsectionstart--><sectionclass="w3l-hotair-form"><h1>粉色系登录表单</h......
  • ant design vue 图片预览组件自定义样式
    这篇文章主要为大家介绍了ant design vue 图片预览组件自定义样式方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪−目录版本:场景:需求:版本:antdesignvue3.2.4场景:使用Image图片组件预览功能需求:自定义预览遮罩层及预览图片的样式;不得影响到其......
  • Three.js——十三、自定义大小画布、UI交互按钮以及3D场景交互、渲染画布为文件(图片)
    画布全屏以及自定义大小画布<!--canvas元素默认是行内块元素--><divclass="model"style="background-color:#ff0000;"width="300"height="180"></div>画布随窗口变化//画布跟随窗口变化window.onresize=function(){constwidth......
  • 通过模板类实现一个简单的vector容器
    什么是模板模板分为类模板和函数模板,关键字为template,基本的声明形式如下:template<classT>;//也可以写成这样template<typenameT>class和typename在声明模板参数时的用法是相似的,一般情况下可以互换但在成员模板内部访问嵌套类型时,需要使用typename。下面举一个例子加以理......
  • 如何在Spring Boot中创建和自定义Starter
    引言SpringBoot提供了一种简化Spring应用开发的方式,并通过提供大量的starter依赖来进一步减少配置。然而,有时候我们需要根据特定需求来创建自定义的starter。本文将详细介绍如何创建和自定义SpringBootstarter。创建自定义Starter的步骤步骤1:创建项目结构创建一个Maven项目,并......
  • Vue3 自定义Hooks大全:一站式解决你的疑惑!
    前言不知道喜欢vue3的小伙伴和我是不是一样,刚上手vue3的时候对自定义hooks一脸懵逼,在一些视频网站学习的时候老师讲解到自定义hooks最喜欢用加减乘除来描述自定义hooks是咋用的,可能是我理解能力比较差吧,我看了这个加减乘除的自定义hooks之后感觉跟没看一样,还是一脸懵逼,......
  • SpringCloud微服务实战——搭建企业级开发框架(三十一):自定义MybatisPlus代码生成器实现
      理想的情况下,代码生成可以节省很多重复且没有技术含量的工作量,并且代码生成可以按照统一的代码规范和格式来生成代码,给日常的代码开发提供很大的帮助。但是,代码生成也有其局限性,当牵涉到复杂的业务逻辑时,简单的代码生成功能无法解决。  目前市面上的代码生成器层出不穷,大多......
  • C++函数模板详解,轻松实现通用函数
    C++函数模板详解,轻松实现通用函数函数模板编写通用函数您也可以为独立的函数编写模板。其语法与类模板类似。例如,您可以编写以下通用函数来在数组中查找一个值并返回其索引:staticconstsize_tNOT_FOUND{static_cast<size_t>(-1)};template<typenameT>size_tFind(const......
  • Logstash自定义正则表达式ETL实战
    0、题记本文建立在干货|LogstashGrok数据结构化ETL实战上,并专注于在Grok中使用自定义正则表达式。有时Logstash没有我们需要的模式。幸运的是,我们有正则表达式库:Oniguruma。Oniguruma是一个灵活的正则表达式库。 它包含多种语言的不同正则表达式实现的特性。Github地址:https://......