0%

gitlab ci 优化指南

在之前的《gitlab ci cd 不完全指南》一文中,我们讲了 gitlab ci 中的一些基本用法。 本文会继续介绍一些在使用 gitlab ci 过程中的优化方法,帮助大家减少在 gitlab ci 上的等待时间。

本文会从以下几个方面来介绍 gitlab ci 的优化方法:

  1. gitlab runner 的配置优化:包括 executor 的选择、concurrent 的设置等
  2. 依赖缓存:如何使用 cache 来加速构建
  3. 依赖使用国内的源:如何使用国内的源来加速依赖的下载
  4. 多个 job 同时执行
  5. job 配置优化:cache policy、GIT_STRATEGY、dependencies
  6. 网络优化:减少同步代码的时间
  7. 升级 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
2
3
4
5
6
7
8
9
build:
stage: build
cache:
key:
files:
- package.json
- package-lock.json
paths:
- node_modules/

上面这个例子的含义是:

  • 当 package.json 或 package-lock.json 文件发生变化时,就会重新下载依赖(不使用缓存)
  • 将 node_modules 目录缓存到 gitlab runner 中

具体可参考:https://docs.gitlab.com/ee/ci/caching/

需要注意的是:通过监测多个文件的变动来决定是否使用缓存的这个配置,在较新版本的 gitlab 中才有,具体忘记什么版本开始支持

依赖使用国内的源

还是拿部署前端项目作为例子,我们在下载依赖时,可以使用国内的源来加速下载。比如使用淘宝的 npm 镜像:

1
2
3
4
5
6
7
build:
stage: build
script:
- npm config set sass_binary_site https://npmmirror.com/mirrors/node-sass
- npm config set registry https://registry.npmmirror.com
- npm install
- npm run build

实际上其实就是我们在 npm install 之前设置了一下 registry,这样就会使用国内的源来下载依赖。

类似的,其他常用语言的包管理工具一般都有国内源,比如 go mod 有七牛云、composer 有阿里云的源等。

多个 job 同时执行:一个 stage 的多个 job

这里说的是那种没有相互依赖的 job,可以同时执行。比如在我们的后端项目中,build 这个 stage 中有两个 job,一个是用来生成 api 文档的,另一个是用来安装依赖的。 因为生成文档这个操作只是依赖于源码本身,不需要等到依赖安装完成,所以可以同时执行。

这种情况实际上就是把多个 job 放到一个 stage 中,这样 gitlab ci 就会同时执行这些 job:

1
2
3
4
5
6
7
8
9
10
11
12
stages:
- build

composer:
stage: build
script:
- composer install

apidoc:
stage: build
script:
- php artisan apidoc

注意:如果 job 之间有依赖,或者可能会读写相同的文件,那么可能会有异常。

job 配置优化

cache policy

这在之前那篇文章有说过,这里再重复一下。默认是 pull-push。意思是在 job 开始时拉取缓存,push 是在 job 结束时推送缓存,这样会保留我们在 job 执行过程中对缓存目录的变更。

但是实际上有时候我们是不需要在 job 结束的时候更新缓存的,比如我们的 job 不会更新缓存目录,那么我们可以设置为 pull,这样在 job 结束的时候就不会推送缓存了。

1
2
3
4
5
6
7
8
9
10
# 只是拉取缓存,然后同步到服务器的 job,不会更新缓存
sync:
cache:
key:
files:
- composer.json
- composer.lock
paths:
- "vendor/"
policy: pull

GIT_STRATEGY: none

跟上面这一小点类似,如果我们的 job 并不需要拉取代码,那么可以设置 GIT_STRATEGYnone,这样就不会拉取代码了。

1
2
3
4
deploy:
stage: deploy
variables:
GIT_STRATEGY: none

在实际中的应用场景是:部署跟发布分离的时候,发布的 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 是没有办法并行的,因为会相互影响。