Rust CI/CD 实践

使用 Rust 也有一段时间了,大部分应用是在公司的后端 API 上面,有两个服务一个源自 ZenGo 的 Rust 门限签名的苦,都有深度参与。门限签名的初版就是出自奶爸之手,从 gotham-city 这个库里面剥离出来的一套流程。

我司一直都是使用 GitHub,GitHub 没有将组织免费时我司使用的是奶爸做的一个 GitHub 个人账户管理工具,基本上管理团队的一些必备功能都有,人员管理、外部贡献者管理、仓库创建删除重命名等。然后 GitHub Team 免费之后我就将仓库 Archive 了,没啥价值了嘛,然后主导将账户转为了组织账户。

再后面 GitHub 推出了 Actions、Package Registry 服务,在这些服务出来之前,我司使用的是奶爸做的一个 NoCD 持续交付工具 来实现内部部署流程自动化的,同样在 GitHub 出了 Actions 之后奶爸也不怎么维护 NoCD 了,当时在团队内部奶爸做的这两个工具还是很好用的,但是至于为什么没有 Archive 掉这个仓库,是因为在现阶段,编译缓存、内网部署等特殊需求 NoCD 还是比较适用的,还有一些价值,所以就不 Archive 了。

好,说回正题吧,Rust 的 CI/CD 的最佳实践。

Rust CI/CD

首先的话就是利用上 GitHub Actions,使用 Actions 来打包发布 Rust 项目。

然后用起来之后我们发现 Rust 的编译耗时比较长,该怎么优化呢,所以就加入了 sccache 来进行构建缓存。

然后整个流程的时间就大大缩减了。

sccache 的问题

在使用过程中我们发现从某一天开始这个构建时间突然变长了,因为平时工作安排比较满没有时间去特意看一下是什么问题,所以一只在搁置。

但是这个问题一只困扰着奶爸,所以在今天奶爸决心将这个问题解决掉,因为在 Actions 里面打印了 sccache stats,在构建 Docker 镜像之前有一个前置的测试流程,测试流程跟 Docker 镜像构建流程都是用了 Actions Cache 这个东西。

但是发现在 测试流程 里面,sccache 是工作的 99% 的编译请求都是 hit,只有一个两个是 miss,而在测试通过之后的构建流程,hit 是 0 ,miss 100%。这就很奇怪了,两个 workflow 的代码是一模一样的。

经过翻来覆去的思考,终于发现了问题所在,我们测试 workflow 里面的编译是 debug 版本,然后 sccache 根据 Cargo.lock 缓存的是一个 debug 编译过程,然后我们 Docker 镜像构建又是 release,就导致 sccache 无法使用缓存的编译过程。

然后 workflow 执行成功之后,post hook 又不会更新缓存,因为缓存的 key,是一样的 Cargo.lock 内容没有变化,就导致我们在上了前置测试流程之后,每次 Docker 构建流程都全量编译了,耗费了大量时间。

解决了 sccache,Rust 交付起来就美滋滋了,在这放一个 Rust CI/CD 的示例项目 供各位参考。

影响 sccache 命中的因素

  1. 主要还是 Cargo.lock,剩下就是可能 Cargo.lock 文件没有变化,但是会导致构建过程有变化的因素
  2. 构建类型 debug/release
  3. rustc 版本

Comments