Post

PGO-CI/CD

或者是造轮子,或者是盲从?

PGO-CI/CD

我给自己的 Go 后端工程 pgo 编写了一个 CI/CD 工具,也是用 Go 编写的。

CI 部分包括常用的构建命令、ORM/Proto 代码生成等(本质上是调用 Makefile),

而 CD 部分则是通过 SSH 将文件同步到远端并运行或重启服务。

在构建这个工具的过程中,我一直在思考:这算真正的 CI/CD 吗?

PS: 整个部署方案记录于另一篇文章PGO-Deploy,本文包含CD工具只是包含基本的文件拷贝和部署命令的执行。明确区分“哪些内容由CD工具提供,哪些属于部署架构本身”很重要,要避免把所有工作都写到CD工具中。

为什么不直接用 GitHub Actions?

通常我们提到的 CI/CD(如 GitHub Actions, Jenkins),大都是基于Git 提交触发的。 这种模式在代码合并进主分支准备发布时非常完美。但在开发过程中,它甚至有点“迟钝”:

  1. 反馈慢:我必须 commit -> push -> 等待任务排队 -> 等待容器启动 -> 运行。
  2. 垃圾提交:为了在测试服验证一个改动,可能产生大量 “fix typo”, “try again” 的 commit。
  3. 技术栈割裂:为了写部署逻辑,我需要写大量的 YAML 和 Bash 脚本,而不是我也许更擅长的 Go。

对于私人项目或小团队,开发者往往兼任 DevOps。 我们需要的不仅仅是一个自动化的流水线,更需要一个在开发阶段能随时、快速同步代码到测试环境的“遥控器”。

设计思路:演进式落地

在这个项目中,我采取了分层策略:

  • Level 1 手动挡(开发期):命令行工具。本地编译,交互式选择,一键热更测试服。
  • Level 2 自动挡(发布期):平台级 CI/CD。由 GitHub Actions 触发,但它只是一个入口,底层依然调用 Level 1 中编写好的 Go 工具。

目前我正专注于 Level 1 的打磨。

代码实现与迭代回顾

最近经历了几次重要的迭代。

1. 构建系统的“可视化” (Make CLI)

项目根目录有一个 Makefile,包含各种 tag 注入、环境设置等变量。 为了避免死记硬背make命令,比如:

1
2
3
有些命令需要输入额外参数,如orm生成就需要mysql连接,需要密码

有些命令不复杂,但是会忘记,经常需要help或者查看makefile

我在 make_cli.go 中实现了一个解析器,它做了几件事:

  • 解析 Makefile:通过正则提取 Target 和注释,生成可视化的选择菜单。
  • 参数缓存:利用 cachePath 记住你上次输入的变量值。
  • 交互式配置:识别 ?= 赋值的变量,运行时询问是否需要修改。

这样,构建过程变成了:运行cicd工具 -> 选择 Build -> 回车确认参数 -> 等待完成。

2. 部署的双轨制 (Deploy CLI)

我将部署拆分为两种截然不同的场景。

场景 A:首次/全量部署 (Infrastructure)

搭建环境用的,读取一个json配置,记录了需要拷贝的文件列表,然后上传到目标服务器。

  • 安全检查:在覆盖前计算 MD5。如果发现远程文件与本地不同(可能是服务器上临时改过),会发出警告或中断,防止意外覆盖。
  • 容器编排:部署完成后,将询问是否一键启动,本质上是工具会自动探测目录下的脚本并赋权,然后执行 docker-compose up -d 来拉起服务。

场景 B:服务热更新 (Hot Update)

这是我在开发中最常用的功能。当我在本地修改了某个微服务的 bug,我不需要全量部署,也不想重启整个 Docker 容器。我只需要把新的可执行程序拷贝到目标服务器。

配合 PM2 的 watch 机制,一旦二进制文件更新,服务会自动重启,实现秒级生效(5s轮询)。

遇到的问题1:

为什么是轮询而不是常规的watch?

因为通过容器内部PM2部署的服务,而更新SSH上传是以宿主机为目标的,容器不适合提供SSH服务。导致了PM2常规的watch无法检测到宿主机的文件变化,即使文件映射到了容器内,也不行。

遇到的问题2

经典的 Linux 问题:Text file busy,如果是正在运行的二进制文件,直接 scp 覆盖会失败。

简单处理:scp前用 rm -f删除文件,后续优化可以加日期作为后缀备份一下。

总结

当前为什么用 Go 写 CI/CD? 除了语言亲切感,最大的好处是复用。 这个 DevOps 工具的开发我可以直接使用我自己最熟悉的Go,尤其是我自己pkg封装好的功能。 我不需要在 Bash 脚本里重新发明轮子,也(暂时)不需要去学习其他CI/CD平台的语法。 这是我当前实现CI/CD最高效的方案。

目前的形态是一个运行在本地的 CLI, 未来它完全可以被打包进 Docker 镜像,或重构成bash脚本, 在 GitHub Actions 中被调用,成为连接开发与生产的统一桥梁。

正如文章副标题: 也许当前的CI/CD是造轮子,它是我当前能最快打造,又足够好用的轮子。 当前我选择了不“盲从”,而这个说法,也仅限于当下,未来需求出现,依然会拥抱“潮流”。

This post is licensed under CC BY 4.0 by the author.