• 使用Webhook扩展Alertmanager
    • 使用Golang创建webhook服务
    • 与钉钉集成
      • 自定义webhook群机器人
      • 定义转换器将告警通知转化为Dingtalk消息对象
      • 创建Dingtalk通知发送包
      • 扩展启动函数
      • 使用Dingtalk扩展

    使用Webhook扩展Alertmanager

    在某些情况下除了Alertmanager已经内置的集中告警通知方式以外,对于不同的用户和组织而言还需要一些自定义的告知方式支持。通过Alertmanager提供的webhook支持可以轻松实现这一类的扩展。除了用于支持额外的通知方式,webhook还可以与其他第三方系统集成实现运维自动化,或者弹性伸缩等。

    在Alertmanager中可以使用如下配置定义基于webhook的告警接收器receiver。一个receiver可以对应一组webhook配置。

    1. name: <string>
    2. webhook_configs:
    3. [ - <webhook_config>, ... ]

    每一项webhook_config的具体配置格式如下:

    1. # Whether or not to notify about resolved alerts.
    2. [ send_resolved: <boolean> | default = true ]
    3. # The endpoint to send HTTP POST requests to.
    4. url: <string>
    5. # The HTTP client's configuration.
    6. [ http_config: <http_config> | default = global.http_config ]

    send_resolved用于指定是否在告警消除时发送回执消息。url则是用于接收webhook请求的地址。http_configs则是在需要对请求进行SSL配置时使用。

    当用户定义webhook用于接收告警信息后,当告警被触发时,Alertmanager会按照以下格式向这些url地址发送HTTP Post请求,请求内容如下:

    1. {
    2. "version": "4",
    3. "groupKey": <string>, // key identifying the group of alerts (e.g. to deduplicate)
    4. "status": "<resolved|firing>",
    5. "receiver": <string>,
    6. "groupLabels": <object>,
    7. "commonLabels": <object>,
    8. "commonAnnotations": <object>,
    9. "externalURL": <string>, // backlink to the Alertmanager.
    10. "alerts": [
    11. {
    12. "labels": <object>,
    13. "annotations": <object>,
    14. "startsAt": "<rfc3339>",
    15. "endsAt": "<rfc3339>"
    16. }
    17. ]
    18. }

    使用Golang创建webhook服务

    首先我们尝试使用Golang创建用于接收webhook告警通知的服务。首先创建model包,用于映射ALertmanager发送的告警信息,Alertmanager的一个通知中根据配置的group_by规则可能会包含多条告警信息Alert。创建告警通知对应的结构体Notification。

    1. package model
    2. import "time"
    3. type Alert struct {
    4. Labels map[string]string `json:"labels"`
    5. Annotations map[string]string `json:annotations`
    6. StartsAt time.Time `json:"startsAt"`
    7. EndsAt time.Time `json:"endsAt"`
    8. }
    9. type Notification struct {
    10. Version string `json:"version"`
    11. GroupKey string `json:"groupKey"`
    12. Status string `json:"status"`
    13. Receiver string `json:receiver`
    14. GroupLabels map[string]string `json:groupLabels`
    15. CommonLabels map[string]string `json:commonLabels`
    16. CommonAnnotations map[string]string `json:commonAnnotations`
    17. ExternalURL string `json:externalURL`
    18. Alerts []Alert `json:alerts`
    19. }

    这里使用gin-gonic框架创建用于接收Webhook通知的Web服务。定义路由/webhook接收来自Alertmanager的POST请求。

    1. package main
    2. import (
    3. "net/http"
    4. "github.com/gin-gonic/gin"
    5. model "github.com/yunlzheng/alertmanaer-dingtalk-webhook/model"
    6. )
    7. func main() {
    8. router := gin.Default()
    9. router.POST("/webhook", func(c *gin.Context) {
    10. var notification model.Notification
    11. err := c.BindJSON(&notification)
    12. if err != nil {
    13. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    14. return
    15. }
    16. c.JSON(http.StatusOK, gin.H{"message": " successful receive alert notification message!"})
    17. })
    18. router.Run()
    19. }

    与钉钉集成

    钉钉,阿里巴巴出品,专为中国企业打造的免费智能移动办公平台,提供了即时通讯以及移动办公等丰富的功能。

    钉钉群机器人是钉钉群的高级扩展功能。群机器人可以将第三方服务的信息聚合到群聊中,实现自动化的信息同步。例如:通过聚合GitHub,GitLab等源码管理服务,实现源码更新同步;通过聚合Trello,JIRA等项目协调服务,实现项目信息同步。不仅如此,群机器人支持Webhook协议的自定义接入,支持更多可能性。这里我们将演示如果将Alertmanager运维报警提醒通过自定义机器人聚合到钉钉群。

    这里将继续扩展webhook服务,以支持将Alertmanager的告警通知转发到钉钉平台。完整的示例代码可以从github仓库https://github.com/yunlzheng/alertmanaer-dingtalk-webhook中获取。

    自定义webhook群机器人

    通过钉钉客户端(如:桌面或者手机)进入到群设置后选择“群机器人”。将显示如下界面:

    群机器人

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

    获取webhook地址

    webhook机器人创建成功后,用户就可以使用任何方式向该地址发起HTTP POST请求,即可实现向该群主发送消息。目前自定义机器人支持文本(text),连接(link),markdown三种消息类型。

    例如,可以向webhook地址以POST形式发送以下

    1. {
    2. "msgtype": "markdown",
    3. "markdown": {
    4. "title":"Prometheus告警信息",
    5. "text": "#### 监控指标\n" +
    6. "> 监控描述信息\n\n" +
    7. "> ###### 告警时间 \n"
    8. },
    9. "at": {
    10. "atMobiles": [
    11. "156xxxx8827",
    12. "189xxxx8325"
    13. ],
    14. "isAtAll": false
    15. }
    16. }

    可以使用curl验证钉钉webhook是否能够成功调用:

    1. $ 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
    2. {"errcode":0,"errmsg":"ok"}

    调用成功后,可以在钉钉应用群消息中接收到类似于如下通知消息:

    测试消息

    定义转换器将告警通知转化为Dingtalk消息对象

    这里定义结构体DingTalkMarkdown用于映射Dingtalk的消息体。

    1. package model
    2. type At struct {
    3. AtMobiles []string `json:"atMobiles"`
    4. IsAtAll bool `json:"isAtAll"`
    5. }
    6. type DingTalkMarkdown struct {
    7. MsgType string `json:"msgtype"`
    8. At *At `json:at`
    9. Markdown *Markdown `json:"markdown"`
    10. }
    11. type Markdown struct {
    12. Title string `json:"title"`
    13. Text string `json:"text"`
    14. }

    定义转换器将Alertmanager发送的告警通知转换为Dingtalk的消息体。

    1. package transformer
    2. import (
    3. "bytes"
    4. "fmt"
    5. "github.com/yunlzheng/alertmanaer-dingtalk-webhook/model"
    6. )
    7. // TransformToMarkdown transform alertmanager notification to dingtalk markdow message
    8. func TransformToMarkdown(notification model.Notification) (markdown *model.DingTalkMarkdown, err error) {
    9. groupKey := notification.GroupKey
    10. status := notification.Status
    11. annotations := notification.CommonAnnotations
    12. var buffer bytes.Buffer
    13. buffer.WriteString(fmt.Sprintf("### 通知组%s(当前状态:%s) \n", groupKey, status))
    14. buffer.WriteString(fmt.Sprintf("#### 告警项:\n"))
    15. for _, alert := range notification.Alerts {
    16. annotations := alert.Annotations
    17. buffer.WriteString(fmt.Sprintf("##### %s\n > %s\n", annotations["summary"], annotations["description"]))
    18. buffer.WriteString(fmt.Sprintf("\n> 开始时间:%s\n", alert.StartsAt.Format("15:04:05")))
    19. }
    20. markdown = &model.DingTalkMarkdown{
    21. MsgType: "markdown",
    22. Markdown: &model.Markdown{
    23. Title: fmt.Sprintf("通知组:%s(当前状态:%s)", groupKey, status),
    24. Text: buffer.String(),
    25. },
    26. At: &model.At{
    27. IsAtAll: false,
    28. },
    29. }
    30. return
    31. }
    创建Dingtalk通知发送包

    notifier包中使用golang的net/http包实现与Dingtalk群机器人的交互。Send方法包含两个参数:接收到的告警通知结构体指针,以及Dingtalk群机器人的Webhook地址。

    通过包transformer.TransformToMarkdown将Alertmanager告警通知与Dingtalk消息进行映射。

    1. package notifier
    2. import (
    3. "bytes"
    4. "encoding/json"
    5. "fmt"
    6. "net/http"
    7. "github.com/yunlzheng/alertmanaer-dingtalk-webhook/model"
    8. "github.com/yunlzheng/alertmanaer-dingtalk-webhook/transformer"
    9. )
    10. func Send(notification model.Notification, dingtalkRobot string) (err error) {
    11. markdown, err := transformer.TransformToMarkdown(notification)
    12. if err != nil {
    13. return
    14. }
    15. data, err := json.Marshal(markdown)
    16. if err != nil {
    17. return
    18. }
    19. req, err := http.NewRequest(
    20. "POST",
    21. dingtalkRobot,
    22. bytes.NewBuffer(data))
    23. if err != nil {
    24. return
    25. }
    26. req.Header.Set("Content-Type", "application/json")
    27. client := &http.Client{}
    28. resp, err := client.Do(req)
    29. if err != nil {
    30. return
    31. }
    32. defer resp.Body.Close()
    33. fmt.Println("response Status:", resp.Status)
    34. fmt.Println("response Headers:", resp.Header)
    35. return
    36. }
    扩展启动函数

    首先为程序添加命令行参数支持,用于在启动时添加全局的Dingtalk群聊机器人地址。

    1. package main
    2. import (
    3. "flag"
    4. ...
    5. "github.com/yunlzheng/alertmanaer-dingtalk-webhook/notifier"
    6. )
    7. var (
    8. h bool
    9. defaultRobot string
    10. )
    11. func init() {
    12. flag.BoolVar(&h, "h", false, "help")
    13. flag.StringVar(&defaultRobot, "defaultRobot", "", "global dingtalk robot webhook")
    14. }
    15. func main() {
    16. flag.Parse()
    17. if h {
    18. flag.Usage()
    19. return
    20. }
    21. ...
    22. }

    同时通过notifier包的Send方法将告警通知发送给Dingtalk群聊机器人

    1. func main() {
    2. ...
    3. err = notifier.Send(notification, defaultRobot)
    4. if err != nil {
    5. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    6. }
    7. c.JSON(http.StatusOK, gin.H{"message": "send to dingtalk successful!"})
    8. }
    使用Dingtalk扩展

    运行并启动dingtalk webhook服务之后,修改Alertmanager配置文件, 为default-receiver添加webhook配置,如下所示:

    1. receivers:
    2. - name: default-receiver
    3. email_configs:
    4. - to: yunl.zheng@wise2c.com
    5. webhook_configs:
    6. - url: http://localhost:8080/webhook

    重启Alertmanager服务后,手动拉高虚拟机CPU使用率触发告警条件,此时Dingtalk即可接收到相应的告警通知信息:

    钉钉群机器人告警信息