使用Golang开发Webhook服务器实现Rancher短信告警

笑脸

开发背景:

单位在内网使用了Rancher作为容器平台,没用办法使用Rancher提供的其他告警方式,但是内网有短信告警平台,所以我准备将Rancher的告警接入内网的短信告警平台,尝试使用Rancher提供的webhook方式。

需求分析:

内网提供的告警平台只需要我们把告警信息写入数据库即可,我们需要开发一个web服务器,接收Rancher发来的Json,然后拼接告警信息,再将信息写入数据库。

代码实现:

Rancher发来的Json示例内容如下

# 此处示例为pod告警json
{
"receiver": "p-rh2r8:pag-m2xs2",
"status": "resolved",
"alerts": [ # 这里面是我们用到的东西, !:平台是在不停的重启pod的,如果勾选了已解决告警,alerts内部会有多个告警信息,包括已解决信息、告警信息,这个信息会不断的更新。
{
"status": "resolved", # 告警时pod的状态
"labels": { # pod的信息
"alert_name": "go-test", # 告警名称
"alert_type": "podNotRunning", # 告警类型
"cluster_name": "local (ID: c-qvhh4)", # 集群名称
"container_name": "go-test", # 容器名称
"group_id": "p-rh2r8:pag-m2xs2",
"logs": "Back-off pulling image \"xxx/go/app:13\"", # 告警日志
"namespace": "default", # 命名空间
"pod_name": "go-test-6574b67dd6-7vwzt", # 告警pod
"project_name": "Default (ID: c-qvhh4:p-rh2r8)", # 项目名称
"rule_id": "p-rh2r8:pag-m2xs2_par-9h4jr",
"severity": "critical",
"workload_name": "go-test" # 工作负载
},
"annotations": {},
"startsAt": "2020-05-21T10:10:00.047662184Z", # 开始时间
"endsAt": "2020-05-21T10:15:30.046352922Z", # 结束时间, 如果没有恢复,结束时间全为0
"generatorURL": ""
},
{
"status": "resolved",
"labels": {
"alert_name": "gotest",
"alert_type": "podNotRunning",
"cluster_name": "local (ID: c-qvhh4)",
"container_name": "go-test",
"group_id": "p-rh2r8:pag-m2xs2",
"logs": "Back-off pulling image \"xxx/go/app:13\"",
"namespace": "default",
"pod_name": "go-test-6574b67dd6-7vwzt",
"project_name": "Default (ID: c-qvhh4:p-rh2r8)",
"rule_id": "p-rh2r8:pag-m2xs2_par-g4klr",
"severity": "critical",
"workload_name": "go-test"
},
"annotations": {},
"startsAt": "2020-05-21T10:05:00.059679579Z",
"endsAt": "2020-05-21T10:13:30.051427628Z",
"generatorURL": ""
}
],
"groupLabels": {
"group_id": "p-rh2r8:pag-m2xs2"
},
"commonLabels": { # 同样也是告警信息
"alert_type": "podNotRunning",
"cluster_name": "local (ID: c-qvhh4)",
"container_name": "go-test",
"group_id": "p-rh2r8:pag-m2xs2",
"logs": "Back-off pulling image \"xxx/go/app:13\"",
"namespace": "default",
"pod_name": "go-test-6574b67dd6-7vwzt",
"project_name": "Default (ID: c-qvhh4:p-rh2r8)",
"severity": "critical",
"workload_name": "go-test"
},
"commonAnnotations": {},
"externalURL": "http://alertmanager-cluster-alerting-0:9093",
"version": "4",
"groupKey": "{}/{group_id=\"p-rh2r8:pag-m2xs2\"}:{group_id=\"p-rh2r8:pag-m2xs2\"}"
}

Go代码

  • main.go

    package mainimport (    "RancherMSG/MsgStruct"
    "RancherMSG/sql"
    "encoding/json"
    "fmt"
    "github.com/gin-gonic/gin"
    "io/ioutil"
    "log")func msg(c *gin.Context) {
    # 这里的json信息我们使用结构体反序列化
    # 声明一个 RancherMsg 结构体 var m MsgStruct.RancherMsg
    # 从Body里面读取json
    jsonData, _ := ioutil.ReadAll(c.Request.Body)
    # log.Println(string(jsonData)) # 调试使用,打印源信息
    # json反序列化 if err := json.Unmarshal(jsonData, &m); err != nil {
    log.Println(err) return
    }
    # 获取 被通知人联系电话
    phoneNum := c.Query("phone")
    # 遍历Alerts切片,获取所有的告警信息,在不监控pod的状态下,里面信息一般只有一个。for i := 0; i < len(m.Alerts); i++ {
    alerts := m.Alerts[i]
    # 拼接告警信息
    alertMsg := fmt.Sprintf(`
    告警类型:%s
    告警信息:%s
    当前状态:%s
    告警集群: %s
    告警容器: %s
    告警Pod:%s
    告警负载:%s
    容器命名空间:%s
    开始时间:%s
    结束时间:%s
    `
    , alerts.Labels.AlertType, alerts.Labels.Logs, alerts.Status, alerts.Labels.ClusterName, alerts.Labels.ContainerName,
    alerts.Labels.PodName, alerts.Labels.WorkloadName, alerts.Labels.Namespace, alerts.StartsAt, alerts.EndsAt)
    # 打印拼接好的告警信息
    log.Println(alertMsg, phoneNum)
    # 将告警信息插入短信数据库
    err := sql.ConnSql(alertMsg, phoneNum) if err != nil {
    log.Println(err)
    }
    }
    }func main() {
    # 创建gin引擎
    r := gin.Default()
    # 设置url
    r.POST("/msg", msg)
    # 启动gin if err := r.Run("192.168.111.2:8080"); err != nil { return
    }
    }
  • msgStruct.go

    package MsgStruct

    #
    type RancherMsg struct {
    # 这里我们只保留alerts里面的信息,其他的丢弃,如果有需要可以再加上
    Alerts []Alerts `json:"alerts"` }type Alerts struct {
    Status string `json:"status"`
    Labels Labels `json:"labels"`
    Annotations Annotations `json:"annotations"`
    StartsAt string `json:"startsAt"`
    EndsAt string `json:"endsAt"`
    GeneratorURL string `json:"generatorURL"`}type Labels struct {
    AlertName string `json:"alert_name"`
    AlertType string `json:"alert_type"`
    ClusterName string `json:"cluster_name"`
    ContainerName string `json:"container_name"`
    ComponentName string `json:"component_name"`
    GroupId string `json:"group_id"`
    Logs string `json:"logs"`
    RuleId string `json:"rule_id"`
    Severity string `json:"severity"`
    Namespace string `json:"namespace"`
    PodName string `json:"pod_name"`
    ProjectName string `json:"project_name"`
    WorkloadName string `json:"workload_name"`}type Annotations struct{}
  • sqlOper.go

    package sqlimport (    "database/sql"
    "log"
    "strings")import (
    _ "github.com/mattn/go-adodb")type Mssql struct {
    *sql.DB
    dataSource string
    database string
    windows bool
    sa SA
    }type SA struct {
    user string
    passwd string}func (m *Mssql) Open() (err error) { var conf []string
    conf = append(conf, "Provider=SQLOLEDB")
    conf = append(conf, "Data Source="+m.dataSource)
    conf = append(conf, "Initial Catalog="+m.database)
    conf = append(conf, "user id="+m.sa.user)
    conf = append(conf, "password="+m.sa.passwd)
    log.Println(strings.Join(conf, ";"))
    m.DB, err = sql.Open("adodb", strings.Join(conf, ";")) if err != nil { return err
    } return nil}func ConnSql(msg, mob string) (err error) {
    db := Mssql{
    dataSource: "数据库连接地址",
    database: "数据库",
    sa: SA{
    user: "xxx",
    passwd: "xxx",
    },
    } // 连接数据库
    err = db.Open() if err != nil {
    log.Println("sql open:", err) return err
    } defer db.Close() // 执行SQL语句
    stmt, err := db.Prepare("insert into USERWakeMessage (Msg, MobileNo) values (?,?)") if err != nil {
    log.Println("Prepare: ", err) return err
    }
    rs, err := stmt.Exec(msg, mob) if err != nil {
    log.Println("Exec :", err) return err
    } // id, _ := rs.LastInsertId()
    affect, _ := rs.RowsAffected()
    log.Printf("向%s发送了%d消息", mob, affect) return nil}

配置Rancher的通知:

  • 进入集群通知配置界面
    图片

  • 点击添加通知
    图片

  • 选择webhook通知方式,配置完成
    图片

  • 制作一个坏镜像,使用Rancher启动它,然后配置告警

    图片

图片

  • 配置告警,选择需要监控的pod,选择刚刚配置的通知。

    注意:

    如果要使用Prometheus表达式,需要安装项目级监控。

    图片

  • 测试结果

    图片

------本页内容已结束,喜欢请分享------

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享