使用 Webhook 扩展 Alertmanager
在某些情况下除了 Alertmanager 已经内置的集中告警通知方式以外,对于不同的用户和组织而言还需要一些自定义的告知方式支持。通过 Alertmanager 提供的 webhook 支持可以轻松实现这一类的扩展。除了用于支持额外的通知方式,webhook 还可以与其他第三方系统集成实现运维自动化,或者弹性伸缩等。
在 Alertmanager 中可以使用如下配置定义基于 webhook 的告警接收器 receiver。一个 receiver 可以对应一组 webhook 配置。
name: <string>webhook_configs:[ - <webhook_config>, ... ]
每一项 webhook_config 的具体配置格式如下:
# 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_config s则是在需要对请求进行 SSL 配置时使用。
当用户定义 webhook 用于接收告警信息后,当告警被触发时,Alertmanager 会按照以下格式向这些url地址发送HTTP Post 请求,请求内容如下:
{"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。
package modelimport "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 请求。
package mainimport ("net/http""github.com/gin-gonic/gin"model "github.com/k8stech/alertmanaer-dingtalk-webhook/model")func main() {router := gin.Default()router.POST("/webhook", func(c *gin.Context) {var notification model.Notificationerr := 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()}
与钉钉集成
钉钉,阿里巴巴出品,专为中国企业打造的免费智能移动办公平台,提供了即时通讯以及移动办公等丰富的功能。
钉钉群机器人是钉钉群的高级扩展功能。群机器人可以将第三方服务的信息聚合到群聊中,实现自动化的信息同步。例如:通过聚合GitHub,GitLab等源码管理服务,实现源码更新同步;通过聚合Trello,JIRA等项目协调服务,实现项目信息同步。不仅如此,群机器人支持Webhook协议的自定义接入,支持更多可能性。这里我们将演示如果将Alertmanager运维报警提醒通过自定义机器人聚合到钉钉群。
这里将继续扩展webhook服务,以支持将Alertmanager的告警通知转发到钉钉平台。完整的示例代码可以从github仓库https://github.com/k8stech/alertmanaer-dingtalk-webhook中获取。
自定义 webhook 群机器人
通过钉钉客户端(如:桌面或者手机)进入到群设置后选择“群机器人”。将显示如下界面:

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

获取webhook地址
webhook机器人创建成功后,用户就可以使用任何方式向该地址发起HTTP POST请求,即可实现向该群主发送消息。目前自定义机器人支持文本(text),连接(link),markdown三种消息类型。
例如,可以向webhook地址以POST形式发送以下
{"msgtype": "markdown","markdown": {"title":"Prometheus告警信息","text": "#### 监控指标\n" +"> 监控描述信息\n\n" +"> ###### 告警时间 \n"},"at": {"atMobiles": ["156xxxx8827","189xxxx8325"],"isAtAll": false}}
可以使用 curl 验证钉钉 webhook 是否能够成功调用:
$ 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的消息体。
package modeltype 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的消息体。
package transformerimport ("bytes""fmt""github.com/k8stech/alertmanaer-dingtalk-webhook/model")// TransformToMarkdown transform alertmanager notification to dingtalk markdow messagefunc TransformToMarkdown(notification model.Notification) (markdown *model.DingTalkMarkdown, err error) {groupKey := notification.GroupKeystatus := notification.Statusannotations := notification.CommonAnnotationsvar buffer bytes.Bufferbuffer.WriteString(fmt.Sprintf("### 通知组%s(当前状态:%s) \n", groupKey, status))buffer.WriteString(fmt.Sprintf("#### 告警项:\n"))for _, alert := range notification.Alerts {annotations := alert.Annotationsbuffer.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消息进行映射。
package notifierimport ("bytes""encoding/json""fmt""net/http""github.com/k8stech/alertmanaer-dingtalk-webhook/model""github.com/k8stech/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群聊机器人地址。
package mainimport ("flag"..."github.com/k8stech/alertmanaer-dingtalk-webhook/notifier")var (h booldefaultRobot 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群聊机器人
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配置,如下所示:
receivers:- name: default-receiveremail_configs:- to: yunl.zheng@wise2c.comwebhook_configs:- url: http://localhost:8080/webhook
重启Alertmanager服务后,手动拉高虚拟机CPU使用率触发告警条件,此时Dingtalk即可接收到相应的告警通知信息:

