使用 GitHub Actions Cache 加快 Workflow
简单讲讲 GitHub Actions 的 Cache 功能,使用方式,以及探讨浅层的设计思想。
虽然本文是我博客中为数不多的「教程」类文章,但我还是会侧重逻辑与思考的角度来讲述。详细教程请查看,GitHub Actions Cache 官方文档。
什么是 GitHub Actions Cache
缓存被广泛运用于计算机的各个领域,核心都是 用某种方式,替换了某个高耗时的原行为。
而 GitHub Actions 也推出了 Cache 功能,用了「缓存特定文件」的方式,解决了「重复下载依赖」的高耗时问题。同时这个「依赖」也是抽象的,除了狭义上的依赖,还可以是任何需要缓存的文件。(毕竟 Linux 一切皆文件,笑,那是不是能缓存一切哈哈哈)。
GitHub Actions 的 Cache 的核心用法(抽象层面)
前面提到,本质上就是「缓存特定文件」。所以 GitHub Actions 本质上就是:
- 使用
key
来标识缓存的唯一性 - 使用
path
来标识要被缓存/已经被缓存的文件
大道至简。也正是因为足够简单,所以才能适应各种场景。
我们可以再稍微具体一点,GitHub Actions Cache 的 actions
名字叫 actions/cache@v3
,当我们在 workflow
中 use
了 actions/cache@v3
时,我们大概需要提供两个参数:
- key
- path
当 GitHub Actions 执行到这一步时,一共有两种情况:
- (缓存命中)发现 Actions 系统内已经有了
key
对应的缓存,就直接将缓存文件下载到path
对应的目录下 - (缓存未命中)当工作流结束时,将
path
对应的目录下的文件打包上传到 Actions 系统内,以key
为名字进行缓存
GitHub Actions 的 Cache 的核心用法(具体层面)
这是一个官方的例子:
- name: Cache node modules
id: cache-npm
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
上面的代码实现了对 Node
依赖的缓存。
相信读者基本都能猜个八九不离十,我只讲一些细节:
key
本身是一个变量,基于系统的架构,以及 package-lock.json 文件的 hash 值,来保证缓存的唯一性restore-keys
是一个数组,当缓存未命中时,会依次尝试这些key
,直到找到一个命中的缓存,或者全部尝试完毕。示例代码中这么写,能够尽可能地复用相似的缓存。
Setup-* 系列
读者可能迫不及待地想修改自己的 GitHub Actions
了,毕竟几乎所有的 CI
第一步都是装编译环境与依赖。
不要急哈哈哈, 官方最新的 setup-*
系列,已经默认开启了 cache
,比如 setup-go@v4
,使用 go.sum
hash 作为 key
的一部分。
我们只要确保 setup-*
的大版本号是最新的即可,不需要手动去实现。已支持的包管理器的 Actions 列表。
依赖存在与否/是否需要执行相关代码的判断
我们可以发现,GitHub Actions Cache
做的只是,将相关的依赖下载到对应目录(比直接从网络下载快很多),即使缓存命中了,它也并不会使你的 npm install
命令直接跳过。而是由 npm install
命令本身,在执行时发现相关依赖已经存在,不需要再下载。
(当然,我们也可以做得更极端。actions/cache
有一个 output
会标识缓存是否命中,我们可以结合 actions
的 if
语法,来判断是否需要执行相关代码。官方示例)
缓存其他依赖项
有的时候,我们运行 CI
会需要一些额外的二进制文件。一种方法是打成一个 Docker
镜像,另外一种方法就是使用 GitHub Actions Cache
,效率会更高。
我们可以建一个 cache
目录,将需要的二进制文件放在里面,然后使用 actions/cache
缓存这个目录即可。对于 key
的设置,可以把安装命令放在同一个文件中,然后使用 hashFiles
来计算 key
。
对 GitHub Actions Cache 设计的浅层思考
其实前面已经说了:
- GitHub Actions Cache 的核心就是「缓存特定文件」,来取代原先的复杂的分析、外部网络下载、编译带来的高耗时。
- GitHub Actions Cache 并没有通过「缓存命中」来直接跳过相关代码,而是简单地通过
key
来标识缓存的唯一性。后续代码是否执行,交由特定的命令自行判断。同时也提供了output
来判断缓存是否命中。 - 提供了基于
restore-keys
的缓存回退机制,在发生了微小变更时,也能尽可能地去使用旧的缓存。
GitHub Actions Cache 的一些限制与细节
关于分支的限制,UI 操作(查看、删除已有缓存等),回收策略,其他细节等,请看官方文档。
(本文贴的官方文档都是英文,官方文档右上角能切换成中文。但是中文版写得太烂了,所以我才写了个相对来说更「说人话」的小教程。)