在之前的《gitlab ci cd 不完全指南》一文中,我们讲了 gitlab ci 中的一些基本用法。 本文会继续介绍一些在使用 gitlab ci 过程中的优化方法,帮助大家减少在 gitlab ci 上的等待时间。
本文会从以下几个方面来介绍 gitlab ci 的优化方法:
- gitlab runner 的配置优化:包括 executor 的选择、concurrent 的设置等
- 依赖缓存:如何使用 cache 来加速构建
- 依赖使用国内的源:如何使用国内的源来加速依赖的下载
- 多个 job 同时执行
- job 配置优化:cache policy、GIT_STRATEGY、dependencies
- 网络优化:减少同步代码的时间
- 升级 gitlab 及 runner 版本
gitlab runner 的配置优化
concurrent 配置
gitlab runner 中有一个很重要的配置是 concurrent
,它表示
gitlab runner 同时运行的 job 数量。默认情况下,concurrent
的值是 1,也就是说 gitlab runner 同时只能运行一个 job。如果你的 gitlab
runner 有多个 executor,那么可以将 concurrent
设置为大于 1
的值,这样可以让 gitlab runner 同时运行多个 job,从而减少等待时间。
具体可参考:https://docs.gitlab.com/runner/configuration/advanced-configuration.html
executor 的选择
比较常见的是 docker 和 shell 类型的 executor,docker executor 的优势是可以在不同的环境中运行 job,比如在不同的镜像中运行 job,这样可以避免环境的不一致性。 而 shell executor 的优势是可以直接在 gitlab runner 的机器上运行 job,不需要额外的环境,在较老的 gitlab 版本中,shell executor 是很快的,但 shell executor 的可靠性较低,依赖于 gitlab runner 的机器,如果机器出现问题,那么 job 就会失败。
在 16.10 版本的实际使用中,docker executor 的速度有了明显提升,所以不必为了速度而选择 shell executor。
具体可参考:https://docs.gitlab.com/runner/executors/
docker executor 的 pull policy
docker executor 在运行 job 时,会拉取 docker
镜像,这个过程会耗费一些时间。我们可以通过设置 pull_policy
来控制是否每次都拉取镜像。默认情况下,pull_policy
的值是
always
,也就是每次都会拉取镜像。
如果我们的镜像不经常更新(比如那种用来 build 项目的 job
所依赖的镜像),那么可以将 pull_policy
设置为
if-not-present
,这样只有在本地没有镜像的时候才会拉取镜像。
可选值:
always
: 每次都拉取镜像if-not-present
: 本地没有镜像的时候才会拉取镜像never
: 从不拉取镜像
具体可参考:https://docs.gitlab.com/runner/executors/docker.html#configure-how-runners-pull-images
依赖缓存
如果我们的项目需要下载一些第三方依赖,比如 npm、composer、go mod 等,那么我们可以使用 cache 来加速构建。cache 会将我们下载的依赖缓存到 gitlab runner 中,下次构建时就不需要重新下载依赖了。
下面是一个前端项目的例子:
1 | build: |
上面这个例子的含义是:
- 当 package.json 或 package-lock.json 文件发生变化时,就会重新下载依赖(不使用缓存)
- 将 node_modules 目录缓存到 gitlab runner 中
具体可参考:https://docs.gitlab.com/ee/ci/caching/
需要注意的是:通过监测多个文件的变动来决定是否使用缓存的这个配置,在较新版本的 gitlab 中才有,具体忘记什么版本开始支持
依赖使用国内的源
还是拿部署前端项目作为例子,我们在下载依赖时,可以使用国内的源来加速下载。比如使用淘宝的 npm 镜像:
1 | build: |
实际上其实就是我们在 npm install
之前设置了一下
registry,这样就会使用国内的源来下载依赖。
类似的,其他常用语言的包管理工具一般都有国内源,比如 go mod 有七牛云、composer 有阿里云的源等。
多个 job 同时执行:一个 stage 的多个 job
这里说的是那种没有相互依赖的
job,可以同时执行。比如在我们的后端项目中,build
这个
stage
中有两个 job,一个是用来生成 api
文档的,另一个是用来安装依赖的。
因为生成文档这个操作只是依赖于源码本身,不需要等到依赖安装完成,所以可以同时执行。
这种情况实际上就是把多个 job 放到一个 stage 中,这样 gitlab ci 就会同时执行这些 job:
1 | stages: |
注意:如果 job 之间有依赖,或者可能会读写相同的文件,那么可能会有异常。
job 配置优化
cache policy
这在之前那篇文章有说过,这里再重复一下。默认是 pull-push。意思是在 job 开始时拉取缓存,push 是在 job 结束时推送缓存,这样会保留我们在 job 执行过程中对缓存目录的变更。
但是实际上有时候我们是不需要在 job 结束的时候更新缓存的,比如我们的 job 不会更新缓存目录,那么我们可以设置为 pull,这样在 job 结束的时候就不会推送缓存了。
1 | # 只是拉取缓存,然后同步到服务器的 job,不会更新缓存 |
GIT_STRATEGY: none
跟上面这一小点类似,如果我们的 job 并不需要拉取代码,那么可以设置
GIT_STRATEGY
为
none
,这样就不会拉取代码了。
1 | deploy: |
在实际中的应用场景是:部署跟发布分离的时候,发布的 job 并不需要拉取代码,只需要通过远程 ssh 命令执行发布的脚本即可。如果我们的 git 仓库比较大,那么这样可以减少一些时间。
dependencies: []
我们知道,gitlab ci 中的 job 可以产出一些构建的产物,比如前端项目
build 出来的静态文件、go 项目编译出来的二进制文件等,这些产物可以被其他
job 使用,只要我们通过 artifacts
配置即可。
但并不是所有的 job 都需要这些 artifacts
的,这个时候我们可以通过 dependencies: []
来告诉 gitlab ci
这个 job 不需要依赖其他 job 的产物。
这样就可以节省下下载 artifacts
的时间。
网络优化
网络状况的好坏直接影响了同步代码的时间,这也是最容易做到的优化方式了。如果我们需要同步的机器比较多,而且同步的文件比较大的时候,网络优化带来的效果就更加明显了。
升级 gitlab 及 runner 版本
最近将 gitlab 从 15.9 升级到 16.10 后,发现使用 docker 的 executor 的时候,初始化容器的速度相比旧版本有明显了提升。这也说明了 gitlab 在不断的优化中,所以及时升级 gitlab 及 runner 版本也是一个不错的选择。
原来可能要 5~10s,如果 job 的数量多,这点提升就会比较明显了。
当然我们可以选择一个 stage 多个 job,但是有很多时候一些 job 是没有办法并行的,因为会相互影响。