微信公众号:运维开发故事,作者:刘大仙
开发背景:
单位在内网使用了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表达式,需要安装项目级监控。
-
测试结果