在某些状况下除了Alertmanager已经内置的集中告警通知方式之外,对于不一样的用户和组织而言还须要一些自定义的告知方式支持。经过Alertmanager提供的webhook支持能够轻松实现这一类的扩展。除了用于支持额外的通知方式,webhook还能够与其余第三方系统集成实现运维自动化,或者弹性伸缩等。git
在Alertmanager中可使用以下配置定义基于webhook的告警接收器receiver。一个receiver能够对应一组webhook配置。github
1 2 3
|
name: <string> webhook_configs: [ - <webhook_config>, ... ]
|
每一项webhook_config的具体配置格式以下:golang
1 2 3 4 5 6 7 8
|
# Whether or not to notify about resolved alerts. [ send_resolved: <boolean> | default = true ]
# The endpoint to send HTTP POST requests to. url: <string>
# The HTTP client's configuration. [ http_config: <http_config> | default = global.http_config ]
|
send_resolved用于指定是否在告警消除时发送回执消息。url则是用于接收webhook请求的地址。http_configs则是在须要对请求进行SSL配置时使用。web
当用户定义webhook用于接收告警信息后,当告警被触发时,Alertmanager会按照如下格式向这些url地址发送HTTP Post请求,请求内容以下:json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
{ "version": "4", "groupKey": <string>, // key identifying the group of alerts (e.g. to deduplicate) "status": "<resolved|firing>", "receiver": <string>, "groupLabels": <object>, "commonLabels": <object>, "commonAnnotations": <object>, "externalURL": <string>, // backlink to the Alertmanager. "alerts": [ { "labels": <object>, "annotations": <object>, "startsAt": "<rfc3339>", "endsAt": "<rfc3339>" } ] }
|
使用Golang建立webhook服务
首先咱们尝试使用Golang建立用于接收webhook告警通知的服务。首先建立model包,用于映射ALertmanager发送的告警信息,Alertmanager的一个通知中根据配置的group_by规则可能会包含多条告警信息Alert。建立告警通知对应的结构体Notification。api
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
package model
import "time"
type Alert struct { Labels map[string]string `json:"labels"` Annotations map[string]string `json:annotations` StartsAt time.Time `json:"startsAt"` EndsAt time.Time `json:"endsAt"` }
type Notification struct { Version string `json:"version"` GroupKey string `json:"groupKey"` Status string `json:"status"` Receiver string `json:receiver` GroupLabels map[string]string `json:groupLabels` CommonLabels map[string]string `json:commonLabels` CommonAnnotations map[string]string `json:commonAnnotations` ExternalURL string `json:externalURL` Alerts []Alert `json:alerts` }
|
这里使用gin-gonic框架建立用于接收Webhook通知的Web服务。定义路由/webhook接收来自Alertmanager的POST请求。markdown
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
package main
import ( "net/http"
"github.com/gin-gonic/gin" model "github.com/yunlzheng/alertmanaer-dingtalk-webhook/model" )
func main() { router := gin.Default() router.POST("/webhook", func(c *gin.Context) { var notification model.Notification
err := c.BindJSON(¬ification)
if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return }
c.JSON(http.StatusOK, gin.H{"message": " successful receive alert notification message!"})
}) router.Run() }
|
与钉钉集成
钉钉,阿里巴巴出品,专为中国企业打造的免费智能移动办公平台,提供了即时通信以及移动办公等丰富的功能。app
钉钉群机器人是钉钉群的高级扩展功能。群机器人能够将第三方服务的信息聚合到群聊中,实现自动化的信息同步。例如:经过聚合GitHub,GitLab等源码管理服务,实现源码更新同步;经过聚合Trello,JIRA等项目协调服务,实现项目信息同步。不只如此,群机器人支持Webhook协议的自定义接入,支持更多可能性。这里咱们将演示若是将Alertmanager运维报警提醒经过自定义机器人聚合到钉钉群。框架
这里将继续扩展webhook服务,以支持将Alertmanager的告警通知转发到钉钉平台。完整的示例代码能够从github仓库https://github.com/yunlzheng/alertmanaer-dingtalk-webhook中获取。运维
自定义webhook群机器人
经过钉钉客户端(如:桌面或者手机)进入到群设置后选择“群机器人”。将显示以下界面:

选择“自定义机器人”,而且按照提示填写机器人名称,获取机器人webhook地址,以下所示:

webhook机器人建立成功后,用户就可使用任何方式向该地址发起HTTP POST请求,便可实现向该群主发送消息。目前自定义机器人支持文本(text),链接(link),markdown三种消息类型。
例如,能够向webhook地址以POST形式发送如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
{ "msgtype": "markdown", "markdown": { "title":"Prometheus告警信息", "text": "#### 监控指标\n" + "> 监控描述信息\n\n" + "> ###### 告警时间 \n" }, "at": { "atMobiles": [ "156xxxx8827", "189xxxx8325" ], "isAtAll": false } }
|
可使用curl验证钉钉webhook是否可以成功调用:
1 2
|
$ curl -l -H "Content-type: application/json" -X POST -d '{"msgtype": "markdown","markdown": {"title":"Prometheus告警信息","text": "#### 监控指标\n> 监控描述信息\n\n> ###### 告警时间 \n"},"at": {"isAtAll": false}}' https://oapi.dingtalk.com/robot/send?access_token=xxxx {"errcode":0,"errmsg":"ok"}
|
调用成功后,能够在钉钉应用群消息中接收到相似于以下通知消息:

定义转换器将告警通知转化为Dingtalk消息对象
这里定义结构体DingTalkMarkdown用于映射Dingtalk的消息体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
package model
type At struct { AtMobiles []string `json:"atMobiles"` IsAtAll bool `json:"isAtAll"` }
type DingTalkMarkdown struct { MsgType string `json:"msgtype"` At *At `json:at` Markdown *Markdown `json:"markdown"` }
type Markdown struct { Title string `json:"title"` Text string `json:"text"` }
|
定义转换器将Alertmanager发送的告警通知转换为Dingtalk的消息体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
package transformer
import ( "bytes" "fmt"
"github.com/yunlzheng/alertmanaer-dingtalk-webhook/model" )
// TransformToMarkdown transform alertmanager notification to dingtalk markdow message func TransformToMarkdown(notification model.Notification) (markdown *model.DingTalkMarkdown, err error) {
groupKey := notification.GroupKey status := notification.Status
annotations := notification.CommonAnnotations
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("### 通知组%s(当前状态:%s) \n", groupKey, status))
buffer.WriteString(fmt.Sprintf("#### 告警项:\n"))
for _, alert := range notification.Alerts { annotations := alert.Annotations buffer.WriteString(fmt.Sprintf("##### %s\n > %s\n", annotations["summary"], annotations["description"])) buffer.WriteString(fmt.Sprintf("\n> 开始时间:%s\n", alert.StartsAt.Format("15:04:05"))) }
markdown = &model.DingTalkMarkdown{ MsgType: "markdown", Markdown: &model.Markdown{ Title: fmt.Sprintf("通知组:%s(当前状态:%s)", groupKey, status), Text: buffer.String(), }, At: &model.At{ IsAtAll: false, }, }
return }
|
建立Dingtalk通知发送包
notifier包中使用golang的net/http包实现与Dingtalk群机器人的交互。Send方法包含两个参数:接收到的告警通知结构体指针,以及Dingtalk群机器人的Webhook地址。
经过包transformer.TransformToMarkdown将Alertmanager告警通知与Dingtalk消息进行映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
package notifier
import ( "bytes" "encoding/json" "fmt" "net/http"
"github.com/yunlzheng/alertmanaer-dingtalk-webhook/model" "github.com/yunlzheng/alertmanaer-dingtalk-webhook/transformer" )
func Send(notification model.Notification, dingtalkRobot string) (err error) {
markdown, err := transformer.TransformToMarkdown(notification)
if err != nil { return }
data, err := json.Marshal(markdown) if err != nil { return }
req, err := http.NewRequest( "POST", dingtalkRobot, bytes.NewBuffer(data))
if err != nil { return }
req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req)
if err != nil { return }
defer resp.Body.Close() fmt.Println("response Status:", resp.Status) fmt.Println("response Headers:", resp.Header)
return }
|
扩展启动函数
首先为程序添加命令行参数支持,用于在启动时添加全局的Dingtalk群聊机器人地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
package main
import ( "flag" ... "github.com/yunlzheng/alertmanaer-dingtalk-webhook/notifier" )
var ( h bool defaultRobot string )
func init() { flag.BoolVar(&h, "h", false, "help") flag.StringVar(&defaultRobot, "defaultRobot", "", "global dingtalk robot webhook") }
func main() {
flag.Parse()
if h { flag.Usage() return }
...
}
|
同时经过notifier包的Send方法将告警通知发送给Dingtalk群聊机器人
1 2 3 4 5 6 7 8 9 10 11 12 13
|
func main() {
...
err = notifier.Send(notification, defaultRobot)
if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
c.JSON(http.StatusOK, gin.H{"message": "send to dingtalk successful!"}) }
|
使用Dingtalk扩展
运行并启动dingtalk webhook服务以后,修改Alertmanager配置文件, 为default-receiver添加webhook配置,以下所示:
1 2 3 4 5 6
|
receivers: - name: default-receiver email_configs: - to: yunl.zheng@wise2c.com webhook_configs: - url: http://localhost:8080/webhook
|
重启Alertmanager服务后,手动拉高虚拟机CPU使用率触发告警条件,此时Dingtalk便可接收到相应的告警通知信息: