go接收alertmanager告警并发送钉钉
前言
- 功能:作为 alertmanager 的 webhook receiver,提取需要的数据转发到钉钉群机器人的webhook
 - web框架:gin
 - alertmanager版本:0.24
 - 系统版本:ubuntu 20.04
 - 功能比较简单,所以就随便写了,全部属于
main包 
原始json数据示例
{
    "receiver": "web\\.hook",
    "status": "firing",
    "alerts": [{
        "status": "firing",
        "labels": {
            "alertname": "node",
            "instance": "192.168.0.10",
            "job": "rh7",
            "severity": "critical"
        },
        "annotations": {
            "description": "rh7 192.168.0.10 节点断联已超过1分钟!",
            "summary": "192.168.0.10 down "
        },
        "startsAt": "2022-04-28T08:44:23.05Z",
        "endsAt": "0001-01-01T00:00:00Z",
        "generatorURL": "http://localhost.localdomain:19090/graph?g0.expr=up+%3D%3D+0\u0026g0.tab=1",
        "fingerprint": "726681bf4674e8a5"
    }],
    "groupLabels": {
        "alertname": "node"
    },
    "commonLabels": {
        "alertname": "node",
        "instance": "192.168.0.10",
        "job": "rh7",
        "severity": "critical"
    },
    "commonAnnotations": {
        "description": "rh7 192.168.0.10 节点断联已超过1分钟!",
        "summary": "192.168.0.10 down "
    },
    "externalURL": "http://192.168.0.10:19092",
    "version": "4",
    "groupKey": "{}:{alertname=\"node\"}",
    "truncatedAlerts": 0
}
示例代码
- model.go(定义结构体)
 
package main
// 定义接收JSON数据的结构体
type ReqAlert struct {
	Status       string              `json:"status"`
	StartsAt     string              `json:"startsAt"`
	EndsAt       string              `json:"endsAt"`
	GeneratorURL string              `json:"generatorURL"`
	Fingerprint  string              `json:"fingerprint"`
	Labels       ReqAlertLabel       `json:"labels"`
	Annotations  ReqAlertAnnotations `json:"annotations"`
}
type ReqGroupLabels struct {
	Alertname string `json:"alertname"`
}
type ReqCommonLabels struct {
	Alertname string `json:"alertname"`
	Instance  string `json:"instance"`
	Job       string `json:"job"`
	Severity  string `json:"severity"`
}
type ReqCommonAnnotations struct {
	Description string `json:"description"`
	Summary     string `json:"summary"`
}
type ReqAlertLabel struct {
	Alertname string `json:"alertname"`
	Instance  string `json:"instance"`
	Job       string `json:"job"`
	Severity  string `json:"severity"`
}
type ReqAlertAnnotations struct {
	Description string `json:"description"`
	Summary     string `json:"summary"`
}
type RequestBody struct {
	// alertmanager传来的请求体
	Receiver          string               `json:"receiver"`
	Status            string               `json:"status"`
	ExternalURL       string               `json:"externalURL"`
	Version           string               `json:"version"`
	GroupKey          string               `json:"groupkey"`
	TruncatedAlerts   float64              `json:"truncatedAlerts"`
	Alert             []ReqAlert           `json:"alerts"`
	GroupLabels       ReqGroupLabels       `json:"groupLabels"`
	CommonLabels      ReqCommonLabels      `json:"commonLabels"`
	CommonAnnotations ReqCommonAnnotations `json:"commonAnnotations"`
}
- dingmsg.go(对接钉钉webhook,注意替换webhook的url)
 
package main
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)
type Md struct {
	Title string `json:"title"`
	Text  string `json:"text"`
}
type Ding struct {
	Msgtype  string `json:"msgtype"`
	Markdown Md     `json:"markdown"`
}
func DingMarshal(text string) string {
	// struct序列化为json
	var myjson Ding = Ding{
		Msgtype: "markdown",
		Markdown: Md{
			Title: "锄田测试钉钉机器人",
			Text:  text,
		},
	}
	va, err := json.Marshal(myjson)
	if err != nil {
		panic(err)
	}
	return string(va)
}
func PostUrl(jsondata string) {
	// 发起post请求
	// 替换钉钉群机器人的webhook
	url := "https://oapi.dingtalk.com/robot/send?access_token=123456"
	var jsonstr = []byte(jsondata)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonstr))
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Content-Type", "application/json;charset=utf-8")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("钉钉webhook响应: %v\n", string(body))
}
func SpliceString(status, description, summary string) string {
	// 拼接字符串
	var rst strings.Builder
	rst.WriteString("# 锄田测试钉钉机器人 \n\n")
	rst.WriteString("- status: " + status + " \n\n")
	rst.WriteString("- description: " + description + " \n\n")
	rst.WriteString("- summary: " + summary + " \n\n")
	return rst.String()
}
- main.go
 
package main
import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
	"github.com/gin-gonic/gin"
)
func ParseJson(c *gin.Context) {
	// 解析AlertManager 传递的 json 数据
	var json RequestBody
	// 将数据解析到结构体中
	if err := c.ShouldBindJSON(&json); err != nil {
		// 返回错误信息
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}
	// 遍历json中alerts数据
	for k, v := range json.Alert {
		fmt.Printf("k: %d, status: %s, description: %s, summary: %s \n", k, v.Status, v.Annotations.Description, v.Annotations.Summary)
		text := SpliceString(v.Status, v.Annotations.Description, v.Annotations.Summary)
		va := DingMarshal(text)
		PostUrl(va)
	}
	// 返回给客户端的消息
	c.JSON(http.StatusOK, gin.H{
		"msg": "success",
	})
}
func main() {
	// 设置gin运行模式,可选:DebugMode、ReleaseMode、TestMode
	gin.SetMode(gin.ReleaseMode)
	// 关闭控制台日志颜色
	gin.DisableConsoleColor()
	// 记录到文件
	f, _ := os.OpenFile("gin.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	gin.DefaultWriter = io.MultiWriter(f)
	r := gin.Default()
	// 只接受来自127.0.0.1的请求
	r.SetTrustedProxies([]string{"127.0.0.1"})
	v1 := r.Group("/v1")
	{
		v1.POST("/alert", ParseJson)
	}
	// 设置程序优雅退出
	srv := &http.Server{
		Addr:    "127.0.0.1:19100",
		Handler: r,
	}
	go func() {
		if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
			log.Printf("listen: %s\n", err)
		}
	}()
	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	log.Println("Shutting down server...")
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server forced to shutdown:", err)
	}
	log.Println("Server exiting")
}
使用示例
- 导入依赖
 
go mod tidy
- 编译
 
go build -o goalert.bin ./main.go ./model.go ./dingmsg.go
- 运行
 
./goalert.bin
- 测试。使用python + requests 测试。python代码如下
 
import requests
# 注意json字符串的代码转为utf-8,否则会报Unicode的错误
# 使用json原字符串,而不是字典
json_firing = r"""
{"receiver":"web\\.hook","status":"firing","alerts":[{"status":"firing","labels":{"alertname":"node","instance":"192.168.0.10","job":"rh7","severity":"critical"},"annotations":{"description":"rh7 192.168.0.10 节点断联已超过1分钟!","summary":"192.168.0.10 down "},"startsAt":"2022-04-28T08:44:23.05Z","endsAt":"0001-01-01T00:00:00Z","generatorURL":"http://localhost.localdomain:19090/graph?g0.expr=up+%3D%3D+0\u0026g0.tab=1","fingerprint":"726681bf4674e8a5"}],"groupLabels":{"alertname":"node"},"commonLabels":{"alertname":"node","instance":"192.168.0.10","job":"rh7","severity":"critical"},"commonAnnotations":{"description":"rh7 192.168.0.10 节点断联已超过1分钟!","summary":"192.168.0.10 down "},"externalURL":"http://192.168.0.10:19092","version":"4","groupKey":"{}:{alertname=\"node\"}","truncatedAlerts":0}
""".encode("utf-8").decode("latin1")
url = "http://127.0.0.1:19100/v1/alert"
resp = requests.post(url=url, data = json_firing, headers={"Content-Type": "application/json"})
print(resp.text)
- alertmanager配置略过
 
补充
- 钉钉可以通过面对面建群的方式建一个单人群,单人群也能添加机器人。
 - 如果不知道alertmanager发送的json数据是什么样,可以写个服务端,直接接收数据不解析,在控制台原样输出字符串。示例代码如下:
 
package main
import (
	"fmt"
	"io/ioutil"
	"net/http"
)
func f1(w http.ResponseWriter, r *http.Request) {
	// 向客户端响应ok
	defer fmt.Fprintf(w, "ok\n")
	// 获取客户端的请求方式
	fmt.Println("method:", r.Method)
	// 获取客户端请求的body
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Printf("read body err, %v\n", err)
		return
	}
	fmt.Println("json: ", string(body))
}
func main() {
	http.HandleFunc("/", f1)
	http.ListenAndServe("127.0.0.1:8888", nil)
}
												
											go接收alertmanager告警并发送钉钉的更多相关文章
- pinpoint实现告警推送至钉钉和微信群
		
前言 在前面的文章中,我们学习了如何通过java实现将消息发送到钉钉.和将消息发送到微信群聊. 基于上述基础,我们今天来接入pinpoint的告警,发送到钉钉群. 实操前准备 开始之前,推荐阅读一下, ...
 - 使用 Prometheus Alertmanager 模块发送 Doris 异常信息至钉钉报警群
		
基础环境 1.Prometheus 版本:2.22.2 下载地址: https://github.com/prometheus/prometheus/releases/download/v2.22.2 ...
 - 一个人的安全部之ELK接收Paloalto日志并用钉钉告警
		
起因 通报漏洞后,开发未能及时修复漏洞,导致被攻击,领导说我发现被攻击的时间晚了,由于一个人安全部精力有限未能及时看IPS告警,于是做了个钉钉告警. 本人环境介绍 ubuntu 14.04 pytho ...
 - Docker+Prometheus+Alertmanager+Webhook钉钉告警
		
Docker+Prometheus+Alertmanager+Webhook钉钉告警 1.环境部署 1.1 二进制部署 1.2 docker部署 1.2.1 webhook 1.2.2 alertma ...
 - kube-promethues监控告警详解(邮件、钉钉、微信、自研平台)
		
Alertmanager已经在前面Prometheus初体验(三)已经介绍过了.现在介绍一下在kube-promethues里面怎么修改alertmanager配置文件,以及怎么通过各种媒介发送信息. ...
 - prometheus grafana graylog 钉钉告警 短信告警 电话告警系统 PrometheusAlert
		
PrometheusAlert 简介 PrometheusAlert是开源的运维告警中心消息转发系统,支持主流的监控系统Prometheus,日志系统Graylog和数据可视化系统Grafana发出的 ...
 - prometheus-alertmanager告警推送到钉钉
		
1. Prometheus告警简介 告警能力在Prometheus的架构中被划分成两个独立的部分.如下所示,通过在Prometheus中定义AlertRule(告警规则),Prometheus会周期性 ...
 - Istio on ACK集成生态(2): 扩展AlertManager集成钉钉助力可观测性监控能力
		
阿里云容器服务Kubernetes(简称ACK)支持一键部署Istio,可以参考文档在ACK上部署使用Isito.Istio on ACK提供了丰富的监控能力,为网格中的服务收集遥测数据,其中Mixe ...
 - 基础架构之日志管理平台及钉钉&邮件告警通知
		
接上一篇,我们继续解释如何把ELK跟钉钉及发送邮件功能结合起来,让我们及时的了解重要日志并快速反馈. Sentinel 安装,项目介绍在https://github.com/sirensolution ...
 - Zabbix通过与微信、钉钉整合实现实时告警
		
abbix可以通过多种方式把告警信息发送到指定人,常用的有邮件,短信报警方式,但是越来越多的企业开始使用zabbix结合微信.钉钉作为主要的告警方式,这样可以及时有效的把告警信息推送到接收人,方便告警 ...
 
随机推荐
- 2023-04-19:给定一个非负数组arr 任何两个数差值的绝对值,如果arr中没有,都要加入到arr里 然后新的arr继续,任何两个数差值的绝对值,如果arr中没有,都要加入到arr里 一直到ar
			
2023-04-19:给定一个非负数组arr 任何两个数差值的绝对值,如果arr中没有,都要加入到arr里 然后新的arr继续,任何两个数差值的绝对值,如果arr中没有,都要加入到arr里 一直到ar ...
 - 2022-10-18:以下go语言代码输出什么?A:panic;B:编译错误;C:moonfdd1。 package main import ( “fmt“ “net/url“ ) // 其中
			
2022-10-18:以下go语言代码输出什么?A:panic:B:编译错误:C:moonfdd1. package main import ( "fmt" "net/u ...
 - 2021-09-20:给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O
			
2021-09-20:给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度.不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O ...
 - Django4全栈进阶之路5 Model模型
			
在 Django 中,模型(Model)是用于定义数据结构的组件,其作用如下: 定义数据结构:模型用于定义数据库中的表格和表格中的字段(列),其中每个模型类对应一个表格,模型中的每个字段对应表格中的一 ...
 - 2023-05-24:为什么要使用Redis做缓存?
			
2023-05-24:为什么要使用Redis做缓存? 答案2023-05-24: 缓存的好处 买啤酒和喝啤酒的例子可以帮助我们理解缓存的好处. 假设你在超市里买了一箱啤酒,如果你需要每次想喝啤酒就去超 ...
 - Mac常用文件解压命令
			
tar 解压:tar xvf fileName.tar 压缩:tar cvf fileName.tar directoryName rar 1.安装rar 下载RAR https://www.rarl ...
 - go for range的坑
			
package main import "fmt" func main() { ParseStudent() } type student struct { Name string ...
 - Linux 上的 .NET 崩溃了怎么抓 Dump
			
一:背景 1. 讲故事 训练营中有朋友问在 Linux 上如何抓 crash dump,在我的系列文章中演示的大多是在 Windows 平台上,这也没办法要跟着市场走,谁让 .NET 的主战场在工控 ...
 - odoo开发教程四:onchange、唯一性约束
			
一:onchange机制[onchange=前端js函数!可以实现前端实时更新以及修改验证] onchange机制:不需要保存数据到数据库就可以实时更新用户界面上的显示. @api.onchange( ...
 - defcon-quals 2023 crackme.tscript.dso wp
			
队友找到的引擎TorqueGameEngines/Torque3D (github.com) 将dso文件放到data/ExampleModule目录下,编辑ExampleModule.tscript ...