Apollo配置更新通知

⚡️: 应领导要求想要把 Apollo 配置变更信息更新到企业微信群中,线上出现异常可根据变更时间,快速反应是否是配置变更导致异常

启用方式

🌛: 前提有一个可正常使用的Apollo服务

配置项统一存储在ApolloPortalDB.ServerConfig表中,也可以通过管理员工具 - 系统参数页面进行配置,修改完一分钟实时生效。

1、webhook.supported.envs

开启 webhook 的环境列表,多个环境以英文逗号分隔,如

DEV,FAT,UAT,PRO

在这里插入图片描述

2、config.release.webhook.service.url

webhook 通知的 url 地址,需要接收 HTTP POST 请求。如有多个地址,以英文逗号分隔,如

http://ip:port/webhook1,http://ip:port/webhook2

在这里插入图片描述

hook编写

🏴: 这里使用的 golang 进行hook编写,拿到信息之后数据格式以 MakeDown 形式发送到企业微信小机器人上,key 根据实际替换

以下是代码展示:

package main

import (
        "bytes"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "time"
)

const (
        weChatWebhookURL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx-xxx-xxx-xxx"    #
)

type ApolloChange struct {
        AppID          string `json:"appId"`
        NamespaceName  string `json:"namespaceName"`
        Operator       string `json:"operator"`
        ReleaseComment string `json:"releaseComment"`
        Operation      int64  `json:"operation"`
        Time           string `json:"time"`
}

const (
        OperatorNormalRelease  = "0"
        OperatorConfigRollback = "1"
        OperatorGrayRelease    = "2"
        OperatorFullRelease    = "3"
)

func main() {
        fmt.Println("**** 开启apollo配置监听 ****")
        http.HandleFunc("/apollo/webhook", HandleWarning)
        if err := http.ListenAndServe(":8080", nil); err != nil {
                log.Printf("Error listening HTTP: %s", err.Error())
        }
}

func HandleWarning(w http.ResponseWriter, r *http.Request) {
        time.Sleep(time.Second)
        body, err := io.ReadAll(r.Body)
        if err != nil {
                http.Error(w, "Error reading HTTP request", http.StatusBadRequest)
                log.Printf("Error reading HTTP request: %s", err.Error())
                return
        }

        var change ApolloChange
        if err := json.Unmarshal(body, &change); err != nil {
                http.Error(w, "Error parsing JSON", http.StatusBadRequest)
                log.Printf("Error parsing JSON: %s", err.Error())
                return
        }

        apolloType := getApolloType(change.Operator)
        change.Time = time.Now().Format("2006-01-02 15:04:05.000")

        message := fmt.Sprintf("******* <font color=\"info\">【 PRO环境 】Apollo 配置变更通知</font> *******\n"+
                ">项目名: <font color=\"comment\">%s</font>\n"+
                ">配置空间: <font color=\"comment\">%s</font>\n"+
                ">操作人员: <font color=\"comment\">%s</font>\n"+
                ">更新时间: <font color=\"comment\">%s</font>\n"+
                ">更新类型: <font color=\"comment\">%s</font>\n"+
                ">更新内容: <font color=\"comment\">%s</font>",
                change.AppID, change.NamespaceName, change.Operator, change.Time, apolloType, change.ReleaseComment)

        fmt.Println(message)

        payload, err := json.Marshal(map[string]interface{}{
                "msgtype": "markdown",
                "markdown": map[string]interface{}{
                        "content": message,
                },
        })
        if err != nil {
                http.Error(w, "Error creating JSON payload", http.StatusInternalServerError)
                log.Printf("Error creating JSON payload: %s", err.Error())
                return
        }

        resp, err := http.Post(weChatWebhookURL, "application/json", bytes.NewBuffer(payload))
        if err != nil {
                http.Error(w, "Error sending HTTP request", http.StatusInternalServerError)
                log.Printf("Error sending HTTP request: %s", err.Error())
                return
        }
        defer resp.Body.Close()

        if resp.StatusCode == http.StatusOK {
                fmt.Println("Message sent successfully!")
        } else {
                fmt.Println("Error sending message. Status code:", resp.StatusCode)
        }
}

func getApolloType(operator string) string {
        switch operator {
        case OperatorNormalRelease:
                return "正常发布"
        case OperatorConfigRollback:
                return "配置回滚"
        case OperatorGrayRelease:
                return "灰度发布"
        case OperatorFullRelease:
                return "全量发布"
        default:
                return "正常发布"
        }
}

服务部署

本地部署

最简单直接跑在宿主机上,详细操作就不讲解了

容器化部署
构建镜像

Dockerfile如下

FROM golang:1.21.1-alpine3.18 AS builder

COPY . /src
WORKDIR /src

RUN GOPROXY=https://goproxy.cn go build   -o bin/apollo-webhook

FROM alpine:3.18.3

COPY --from=builder /src/bin /app

WORKDIR /app

# 同步时区
RUN apk --update add tzdata && \
	cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
	echo "Asia/Shanghai" > /etc/timezone && apk del tzdata && \
	rm -rf /var/cache/apk/*

EXPOSE 8080

CMD ["./apollo-webhook"]

构建好镜像之后直接使用就好了,可根据自己的方式部署好,把地址 正确填写到 Apollo 的config.release.webhook.service.url

使用

在Apollo更新配置,点击发布时填写对应的Comment信息,发布成功之后数据就会通知到对应的企业微信群中

在这里插入图片描述

样例展示,在企业微信群中能够看到以下内容

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力做一名技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值