当前位置: 首页 > 科技观察

Zadig和ChatOps能否擦出火花

时间:2023-03-13 02:05:57 科技观察

背景介绍Zadig是目前非常流行的云原生持续交付平台,具有灵活易用的高并发工作流,面向开发者的云原生环境,高效协作的测试管理,强大的免运维模板库、客观准确的性能洞察、云原生IDE插件等重要特性,为工程师提供了可满足大多数企业交付场景的统一协作面。然而,你有没有遇到过以下情况:当你被“paidtoshit”时,被要求派流水线;当你在“听会精神”时,你被要求派流水线;叫你送流水线……总之,随时随地都可能叫你送流水线。对于这种枯燥且频繁的操作,有没有更好的解决方案呢?Zadig在1.15.0版本中已经非常友好的支持了移动端,按理说应该能够满足平时的工作需求。不过,作为一个折腾的运维,我并不满足于此。我希望能够通过机器人来完成一些运维工作,比如合并分支、发送流水线、执行脚本等,这样做主要有两个好处。:移动性:您可以随时随地通过手机APP与机器人进行交流,让机器人完成以往只能通过命令行或网页完成的任务。分享:机器人群内所有成员都可以看到群聊信息,接收任务处理结果,大大提高了信息沟通的效率。这实际上是ChatOps的实现,但这只是初级阶段——即以字符串匹配的形式进行操作。但随着人工智能、机器学习等技术的不断成熟,ChatOps的交付体验会越来越好。当然,我还处于起步阶段。本文还带你通过钉钉机器人发布Zadig流水线。架构分析ChatOps的核心是通过聊天工具和机器人将web端或命令行的人工操作转换,所以整体架构不是很复杂,如下:整体流程如下:技术员在聊天群@直播,发送需要执行的命令。机器人接收命令,判断命令,根据命令执行相关操作,并将结果反馈到聊天群。要访问ChatOps,该服务需要有相应的开放API。幸运的是,Zadig提供了一些API[1],可以在文档中查看和学习。为了不在开发阶段重新发明轮子,我在Github上使用了一个ChatOpsbot框架[2],已经实现了命令行、微信网页版、企业微信、钉钉等聊天机器人,我们只需要构建在此实现具体的业务。Zadig请求的封装要实现对Zadig的API操作,我们需要对HTTP请求进行封装。为了操作方便,我把一些Zadig的API封装成一个SDK[3],简单的实现了Zadig的开发API的功能(没有仔细调试,可能有bug),如下:现在我们只需要实现我们需要的功能在项目中。首先需要创建Zadig请求,创建zadig/zadig.go文件,实现Zadig初始化。代码如下:"http://xxx/")funcSetup()*myZadig{client,err:=zadig.NewClient(token,zadig.WithBaseURL(baseURL))iferr!=nil{log.Fatalf("创建客户端失败:%v",err)}return&myZadig{client:client}}!!PS:token,url这些配置其实可以放在配置文件里,这里为了演示,放在代码文件里。其中:token用于用户认证,在WEB->AccountSettings右上角获取baseURL为zadig的地址,然后在该文件中实现CreateWorkflowTask方法,该方法用于执行工作流,如如下:packagezadigvar(callbackURL="xxx")......func(m*myZadig)CreateWorkflowTask(workfolwName,envName,serviceName,serviceType,repoName,branchstring)error{_,_,err:=m.client.Workflow.CreateWorkflowTask(&zadig.CreateWorkflowTaskOptions{WorkflowName:workfolwName,EnvName:envName,Targets:[]zadig.TargetArgs{{Name:serviceName,ServiceType:serviceType,Build:zadig.BuildArgs{Repos:[]zadig.Repository{{RepoName:repoName,Branch:branch,},},},},},Callback:zadig.Callback{CallbackUrl:callbackURL,},})iferr!=nil{returnerrors.New("执行工作流失败")}returnnil}该方法接收执行工作流需要的参数,然后调用SDK完成执行。由于我在这里构建和部署,所以我只编写了Targets实现。注册Zadig插件只是简单封装了Zadig执行工作流请求,然后注册Zadig插件。rboot项目[2]使用插件注册新的指令,系统会自动加载这些指令到应用程序中,可以通过help命令查看运行规则。在robot/plugins中创建一个zadig/zadig.go文件来注册zadig来执行流水线指令。内容如下:packagezadigimport("fmt""regexp""strings""github.com/sirupsen/logrus""devops-chatops/rboot""devops-chatops/zadig")varzadigInfo=map[string]map[string]string{“dev”:{“branch”:“dev”,“workflow”:“devops-dev”,“serviceType”:“helm”,“env”:“dev”,},“test”:{"branch":"test","workflow":"devops-qa","serviceType":"helm","env":"qa",},"uat":{"branch":"uat",“workflow”:“devops-uat”,“serviceType”:“helm”,“env”:“uat”,},“prod”:{“branch”:“master”,“workflow”:“devops-prod”,"serviceType":"helm","env":"prod",},"yamldev":{"branch":"master","workflow":"chatops-dev","serviceType":"k8s","env":"dev",},}funcinit(){//注册脚本rboot.RegisterPlugin(`pipeline`,rboot.Plugin{//脚本处理函数Action:func(bot*rboot.Robot,incoming*rboot.Message)[]*rboot.Message{//删除空格content:=strings.Replace(incoming.Content,"","",-1)res:=createWorkflowTask(content)returnrboot.NewMessages(res)},Ruleset:map[string]string{`pipeline`:`Execute[\w]+Environment[\w-]+Pipeline`},//脚本规则集Usage:map[string]string{"pipeline":"Executedevenvironmentdevops-chatopspipeline",},Description:`示例'执行开发环境devops-chatops管道'`,})}funccreateWorkflowTask(contentstring)string{res:=regexp.MustCompile(`[\w-/]+pipeline`)s:=res.FindStringSubmatch(content)sp:=strings.Split(s[0],"Pipeline")pipelineName:=sp[0]br:=regexp.MustCompile(`[\w]+environment`)tb:=br.FindStringSubmatch(content)env:=strings.Split(tb[0],"environment")[0]workflow:=zadigInfo[env]["workflow"]repoName:=pipelineNamebranch:=zadigInfo[env]["branch"]envName:=zadigInfo[env]["env"]serviceName:=pipelineNameserviceType:=zadigInfo[env]["serviceType"]logrus.Debugf("Workflow:%vPipeline:%vEnvironment:%vService:%v服务类型:%v分支:%v\n",workflow,pipelineName,envName,serviceName,serviceType,branch,)zd:=zadig.Setup()err:=zd.CreateWorkflowTask(workflow,envName,serviceName,serviceType,repoName,branch)iferr!=nil{returnfmt.Sprintf("执行%s环境的pipeline%sfailed",env,pipelineName)}returnfmt.Sprintf("pipeline%s在%s环境下执行成功",env,pipelineName)}其中:init方法是对注册的Action脚本的处理函数插入。Ruleset即指令规则UsageUsageDescription描述信息createWorkflowTask执行工作流方法,主要用于获取指令的关键字,然后调用zadig.CreateWorkflowTask执行工作流zadigInfo用于定义zadig工作流的环境信息即工作流namebranch是分支名serviceType是服务类型,里面有k8s和helmserviceenv部署环境信息。上面的匹配规则和环境信息比较简单粗暴。最好将这些数据存储在数据库中。为了不引入额外的组件,我将它们直接放在代码中。业务代码开发完成,我们需要导入zadig插件,在robot/plugins/plugins.go中导入即可,如下:/rboot/robot/plugins/ping"_"github.com/ghaoo/rboot/robot/plugins/vote"_"devops-chatops/robot/plugins/zadig")至此执行流水线业务开发完成。开发完成后需要部署ChatOps。部署需要分为几个阶段:创建聊天机器人,部署应用程序,创建聊天机器人。这个聊天机器人不是钉钉普通的自定义机器人,而是需要在钉钉开发者后台创建[4]。具体操作见文献[5],此处不再赘述。内部机器人创建完成后,会在钉钉上生成一个测试群,创建一个机器人,如下:这个机器人和普通机器人的区别在于多了一个POST地址,这个地址是我们在创建机器人的时候配置的,也是应用程序的访问地址。随着机器人的不断发展,关键词会越来越多,所以我这里选择签名验证。部署应用(1)修改配置文件。为了简单起见,我直接将配置文件放到代码仓库中,推送到镜像中。在代码根目录下创建.env文件,内容如下:#机器人名称ROBOT_NAME=DEVOPS-CHATOPS#聊天适配器名称ROBOT_ADAPTER="dingtalk"#缓冲区名称ROBOT_BRAIN=bolt#消息秘钥ROBOT_SECRET=#是否开启DEBUGDEBUG=false#缓存位置DATA_PATH=.data#bolt数据存放地址BOLT_DB_FILE=db/rboot.db#web服务监听端口WEB_SERVER_PORT=9000#是否开启TSLWEB_SERVER_TLS=false#CA证书位置WEB_SERVER_CERT=#CA秘钥位置WEB_SERVER_CERT_KEY=#钉钉机器人秘钥DING_ROBOT_SECRET="xxxx"#??钉钉webhook机器人access_tokenDING_ROBOT_HOOK_ACCESS_TOKEN="xxxx"#??钉钉webhook机器人秘钥DING_ROBOT_HOOK_SECRET="xxxx"配置适配器名称和钉钉机器人相关信息。(2)添加制作应用镜像的Dockerfile,如下:FROMgolang:1.19.1ASbuild-envENVGOPROXYhttps://goproxy.cnADD。/go/src/appWORKDIR/go/src/appRUNgomodtidyRUNGOOS=linuxGOARCH=386gobuild-v-o/go/src/app/app-serverFROMalpineRUNsed-i's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g'/etc/apk/repositories&&\apkadd-UtzdataRUNln-sf/usr/share/zoneinfo/Asia/Shanghai/etc/localtimeCOPY--from=build-env/go/src/app/app-server/app/app-serverCOPY--from=build-env/go/src/app/.env/app/.envWORKDIR/appEXPOSE9000CMD["./app-server"](3)添加应用K8SYAML配置列表,主要包括deployment、service、ingressresources,如下:apiVersion:apps/v1kind:Deploymentmetadata:labels:app:devops-chatopsname:devops-chatopsspec:replicas:1selector:matchLabels:app.kubernetes.io/实例:devops-chatopsapp.kubernetes.io/名称:devops-chatops模板:元数据:标签:app.kubernetes.io/实例:devops-chatopsapp.kubernetes.io/名称:devops-chatopsspec:containers:-image:registry.cn-hangzhou.aliyuncs.com/coolops/devops-chatops:1c5e4c9274959c8efcecfb286103b052abb44d27imagePullPolicy:IfNotPresentname:devops-chatopsports:-containerPort:9000name:httpprotocol:TCP---apiVersion:v1kind:Servicemetadata:标签:app.kubernetes.io/instance:devops-chatopsapp.kubernetes.io/name:devops-chatops名称:devops-chatopsspec:端口:-名称:http端口:80协议:TCPtargetPort:http选择器:app.kubernetes.io/instance:devops-chatopsapp.kubernetes.io/name:devops-chatopssessionAffinity:无类型:ClusterIP---apiVersion:extensions/v1beta1kind:Ingressmetadata:名称:devops-chatopsspec:规则:-host:chatops.jokerbai.comhttp:paths:-backend:serviceName:devops-chatopsservicePort:80path:/(4)在Zadig上部署应用由于我们这里使用的是YAML应用,首先在Zadig上创建一个YAML项目,如下:然后在项目中创建并添加服务,我们选择从代码仓库同步,??如下:接下来,我们需要给应用添加一个构建操作,配置如下:然后我们把服务添加到环境中,现在就可以执行工作流发布任务了,如下:测试机器人现在可以在群里测试了,先测试简单的帮助,看能否输出我们想要的帮助信息,如下:发现可以得到我们想要的信息。接下来测试并发布Zadig管道,如下:可以看到给我们的反馈是管道创建成功。成功了吗?我们到ZadigWEB端查看如下:可以看到有一个openAPI触发的pipeline在运行,说明pipeline已经触发成功。为了得到工作流执行的最终结果,我们可以在Zadig上的工作流中加入IM通知,也可以使用机器人,这样就形成了一个闭环。最后,我们完成了Zadig和ChatOps(聊天机器人)的结合。当然,这种机器人需要我们按规矩来玩。如果您丢失的指令不符合规则,您将无法进行下一步。整个过程中,还是发现了一些问题:目前使用openAPI触发Helm项目出现问题,无法正常获取服务,导致流水线无法进行。使用openAPI触发的workflow不会被IM通知给chatbot,可以接入很多能力。某些操作频繁且枯燥,可以考虑各种自动化,chatops是其中一种选择。文档[1]ZadigOpenAPIhttps://docs.koderover.com/zadig/v1.15.0/api/usage/[2]ChatOps框架https://github.com/ghaoo/rboot.git[3]ZadigSDKhttps://github.com/joker-bai/go-zadig.git【4】钉钉开发者后台https://open-dev.dingtalk.com【5】钉钉内部机器人文档https://open.dingtalk.com/文档/机器人/企业创建的聊天机器人