撸了一个 CI/CD 工具

1,626 阅读4分钟

徒手打造一个Gitlab Runner

Git Runner(也称为“GitLab Runner”)是一个开源的命令行工具,用于在GitLab CI/CD(持续集成/持续交付)中运行作业和管道。它允许使用者将GitLab上的代码仓库与不同的执行环境(例如Docker容器、虚拟机或物理机器)进行交互,并执行特定的任务,例如构建、测试、部署和代码分析等。

Git Runner是作为GitLab CI/CD的一部分而创建的,它可以与GitLab和GitLab CI/CD集成,并提供了广泛的自定义选项和配置。Git Runner可以在Linux、Windows和macOS等操作系统上运行,可以作为单个实例或作为群集来管理和扩展。

目前常见CI/CD工具主要分为几类:基于Gitlab的runner,Jenkins,托管式平台有Travis CI,CircleCI,还有最近几年冒出来的诸如GoCD的平台。平台很多,究其设计原理大同小异。使用方法其实也差不多,通过插件,定义yaml文件去定义整个CI,CD流程。

在运维脚本化,自动化的年代,这类产品足够我们使用,通过拼装来实现我们的需求。进入平台化时代后,如何将这类开源产品和内部系统、流程有机的集成起来成为一个难题了。开源用的爽,开箱即用,到后期都是坑,想必大家都深有感触。

我一直都不喜欢Jenkins,gitlab的runner配置又复杂,托管平台不具备条件,后期目标要把相关运维平台都集成起来,怎么办呢,自己撸一个吧,说干就干。

00. 先取个名字

取名比写代码难多了,问了下GPT,也没给出太好的建议,忽然灵光一现,就叫OpsGoCD吧,寓意就是一个ops用golang写了一个CD(CI)系统,完美。

01. 软件设计

a.png

设计流程,webHook Server收到来自git仓库的push event后插入task_details和task_states两张表,然后写入task_list队列。工作节点(agent)每个5秒发送一个心跳包给Tcp Server,更新agent_states表,服务端收到客户端的心跳包后判断agent是否合法,任务并发是否小于等于3; 满足条件后,从task_list中取构建任务返回给agent(如果task_list没有任务,不返回);agent继续每隔五秒钟发送心跳包,主要是当前agent的并发任务量,同时更新node_states表。由于每个任务由自己的协程管理,所以每个构建任务会单独发送deploaystate的包告诉服务端任务构建成功与否,同时更新agent_states表。

02. 功能

可以扩充大量工作节点,并可以高度自定义流水线环节的CD系统,目前功能主要从提交代码到上传镜像仓库为止,相关状态数据已经入库,可以通过开发界面,加入审批等功能,形成一个可视化的CI/CD系统。

03. 部署:

启用git的webhook功能,相关代码段在

cicdServer/server.go

func gitLabWebhook() {
hook, _ := gitlab.New(gitlab.Options.Secret("*****"))

http.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
	payload, err := hook.Parse(r, gitlab.PushEvents)
	if err != nil {
		if err == gitlab.ErrEventNotFound {
		}
	}

	switch payload.(type) {

	case gitlab.PushEventPayload:
		release := payload.(gitlab.PushEventPayload)
		pushDate := release.Commits[0].Timestamp.String()
		projectName := strings.Split(release.Repository.Name, "/")[0]
		branch := strings.Split(release.Ref, "/")[2]
		branchName := release.Ref
		gitSshUrl := release.Repository.GitSSHURL
		gitHttpUrl := release.Repository.GitHTTPURL
		pushUserName := release.UserUsername
		//insert task to redis and mysql
		dtd := new(todo.DeployTaskDetails)
		dtd.TaskId = tools.CreateTaskId()
		dtd.PushDate = tools.UtcDateConvert(pushDate)
		dtd.ProjectName = projectName
		dtd.BranchName = branchName
		dtd.Branch = branch
		dtd.GitSshUrl = gitSshUrl
		dtd.GitHttpUrl = gitHttpUrl
		dtd.PushUserName = pushUserName
		todo.AddDeployTask(dtd, sqldb, rdb)
	}

})
http.ListenAndServe(":6060", nil)

}

部署mysql,版本随意,主流版本即可,导入三个sql。

部署redis,版本随意,主流版本即可。

修改数据库相关信息:

mysql:配置在cicdSerevr/mypack_mysql/myMysql.go中

func InitConn() *sql.DB {
  // Set up the DSN
  dsn := "cicd_user:******@tcp(127.0.0.1:3306)/wcicd"
  // Open a connection to the database
  sqldb, err := sql.Open("mysql", dsn)
  if err != nil {
    dlogger.Error(err.Error())
    os.Exit(1)
}

redis:配置在cicdSerevr/mypack_redis/myRedis.go中

func InitRedisConn() *redis.Client {
  rdb := redis.NewClient(&redis.Options{
    Addr:     "localhost:6379",
    Password: "******", // no password set
    DB:       0,        // use default DB
    PoolSize: 8,
  })
  err := rdb.Ping(ctx).Err()
  if err != nil {
    dlogger.Error(err.Error())
    os.Exit(1)
  }
  return rdb

}

不提出配置文件的目的在于一个二进制包就能启动,不依赖于其他文件。

目前只支持java项目,maven的私服setting.xml文件需要自己准备,不同的maven镜像的工作目录和config目录可能所有区别,需要修改相关路径等。 目前支持在ci.yaml自定义镜像的工作目录,不支持定义settings所在的目录。

需要匹配setting.xml路径涉及的文件在Agent机器上,具体文件在cicdAgent/deploy_code/deployCode.go

func (di *DeployBasicInfo) DeployJar() (deployStat int) { ...

resp, err := cli.ContainerCreate(ctx, &container.Config{
  Image:      di.DeployBaseImage, //"maven:3.3-jdk-8"
  Cmd:        di.DeployCmd,       //"mvn package -Dmaven.test.skip=true"
  Tty:        false,
  WorkingDir: di.WorkDir,
}, &container.HostConfig{
  AutoRemove: false,
  //settings.xml in /tmp/cicdAgent/conf
  Binds: []string{codeDir + ":" + di.WorkDir, "/tmp/cicdAgent/conf:/usr/share/maven/ref/"}},
  nil, nil, deployImgName)

... }

自动构建依赖git仓库中有ci.yaml文件存在,目的是为了定义项目构建的命令。

build:
language: java
image: maven:3.3-jdk-8
workdir: /usr/src/mymaven
script: mvn package -Dmaven.test.skip=true

agent所在服务器需要安装docker环境,安装git,定义好镜像仓库,定义git仓库等

cicdAgent/deal_image/dealImage.go中修改镜像仓库的地址,用户名,密码等

cicdAgen/deal_code/dealCode.go中修改git仓库信息,考虑通用性使用http连接,用户名,密码的方式

分别编译运行server端和agent端。

04. 待完成工作

支持golang和python项目; 缺少可视化,不会前端,大概率不会做了;

05. 仓库地址

github.com/weichenqi/O…