Apollo配置更新通知

文章目录

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

启用方式

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

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

1、webhook.supported.envs

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

DEV,FAT,UAT,PRO

2、config.release.webhook.service.url

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

shell 复制代码
http://ip:port/webhook1,http://ip:port/webhook2

hook编写

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

以下是代码展示:

go 复制代码
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如下

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信息,发布成功之后数据就会通知到对应的企业微信群中

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

相关推荐
枫叶丹439 分钟前
【在Linux世界中追寻伟大的One Piece】进程信号
linux·运维·服务器
刻词梨木42 分钟前
ubuntu中挂载点内存不足,分配不合理后使用软链接的注意事项
linux·运维·ubuntu
灯火不休ᝰ2 小时前
[win7] win7系统的下载及在虚拟机中详细安装过程(附有下载文件)
linux·运维·服务器
HHoao5 小时前
Ubuntu启动后第一次需要很久才能启动GTK应用问题
linux·运维·ubuntu
小灰兔的小白兔5 小时前
【Ubuntu】Ubuntu常用命令
linux·运维·ubuntu
winds~5 小时前
ubuntu中软件的进程管理-结束软件运行
linux·运维·ubuntu
bush46 小时前
使用root账号ssh登录虚拟机ubuntu
运维·ubuntu·ssh
叫我龙翔7 小时前
【Linux】进程间关系与守护进程
linux·运维·服务器·计算机网络
S hh7 小时前
【Linux】进程地址空间
java·linux·运维·服务器·学习
唐大爹7 小时前
项目实战:k8s部署考试系统
云原生·容器·kubernetes