Help us learn about your current experience with the documentation. Take the survey.

CI 配置性能

可中断的流水线

默认情况下,所有作业都是 可中断的,除了 dont-interrupt-me 作业,它会在 main 分支上自动运行,其他情况下为 manual

如果你希望正在运行的流水线在你向合并请求推送新的提交时能够完成,请确保在推送之前启动 dont-interrupt-me 作业。

Git fetch 缓存

由于 GitLab.com 使用了 pack-objects 缓存,同一流水线引用的并发 Git fetch 会在 Gitaly 服务器上被去重(总是),并从缓存中提供服务(当可用时)。

这之所以有效,原因如下:

  • pack-objects 缓存在 GitLab.com 的所有 Gitaly 服务器上都已启用。
  • CI/CD Git 策略设置 对于 gitlab-org/gitlabGit clone,这导致所有作业都获取相同的数据,从而最大化了缓存命中率。
  • 我们使用 浅克隆 来避免为每个作业下载完整的 Git 历史。

通过制品获取仓库,而不是从 Gitaly 克隆/获取

最近我们看到了来自 Gitaly 的错误,看起来像这样:(参见 issue

fatal: remote error: GitLab is currently unable to handle this request due to load.

虽然 GitLab.com 使用了 pack-objects 缓存,但有时负载仍然对 Gitaly 来说太重,而且 thundering herds 也是一个问题,因为我们在同一时间有很多作业在克隆仓库。

为了缓解和减少 Gitaly 的负载,我们将一些作业更改为从作业中的制品(artifacts)获取仓库,而不是一次性从 Gitaly 克隆。

目前这适用于大多数 RSpec 作业,这些作业在大多数流水线中有最多的并发作业。这也略微提高了速度,因为从制品获取也比克隆稍微快一点,代价是为每个流水线保存更多的制品。

根据 Fetch repo from artifacts for RSpec jobs 在 2023-12-20 的数据,额外的存储成本大约是每个流水线 280M,我们为每个 RSpec 作业节省了 15 秒。

我们不将其应用于没有其他作业依赖的作业,因为我们不想延迟任何作业的启动。

此行为可以通过变量 CI_FETCH_REPO_GIT_STRATEGY 控制:

  • 设置为 none 意味着使用 .repo-from-artifacts 的作业从 clone-gitlab-repo 作业中从制品获取仓库,而不是克隆。
  • 设置为 clone 意味着使用 .repo-from-artifacts 的作业照常克隆仓库。在这种情况下,clone-gitlab-repo 作业不会运行。

要禁用它,将 CI_FETCH_REPO_GIT_STRATEGY 设置为 clone。要启用它,将 CI_FETCH_REPO_GIT_STRATEGY 设置为 none

缓存策略

  1. 默认情况下,所有作业只能拉取缓存。
  2. 所有作业都必须能够在缓存为空的情况下通过。换句话说,缓存只是为了加快作业速度。
  3. 我们目前在 .gitlab/ci/global.gitlab-ci.yml 中定义了几个不同的缓存定义,使用固定的键:
    • .setup-test-env-cache
    • .ruby-cache
    • .static-analysis-cache
    • .rubocop-cache
    • .ruby-node-cache
    • .qa-cache
    • .yarn-cache
    • .assets-compile-cache(键包含 ${NODE_ENV},所以实际上是两个不同的缓存)。
  4. 这些缓存定义由 多个原子缓存 组成。
  5. 只有以下在每 2 小时的 maintenance 定时流水线中运行的作业才会推送(即更新)到缓存:
  6. 这些作业也可以通过 pipeline:update-cache 标签强制在合并请求中运行(这对于更新缓存键的 MR 来预热缓存很有用)。

制品策略

我们将作业保存和检索的制品限制在最小,以减少上传/下载时间和成本,以及制品存储。

组件缓存

GitLab 的一些外部组件(GitLab Workhorse 和前端资源)需要从源代码构建,作为运行测试的初步步骤。

cache-workhorse

这个 MR 中,然后是 这个 MR,我们引入了一个新的 cache-workhorse 作业,它:

  • 为所有 GitLab.com gitlab-org/gitlab 定时流水线自动运行
  • 为任何触及 workhorse/ 文件夹的 master 提交自动运行
  • 对于 GitLab.com gitlab-org 的触及缓存相关文件的 MR,设置为手动

此作业尝试下载一个通用包,其中包含 GitLab 测试套件中需要的 GitLab Workhorse 二进制文件(在 tmp/tests/gitlab-workhorse 下)。

  • 如果包 URL 返回 404:
    1. 它运行 scripts/setup-test-env,以便构建 GitLab Workhorse 二进制文件。
    2. 然后创建一个包含二进制文件的归档文件,并将其 作为通用包上传
  • 否则,如果包已存在,它成功退出作业。

我们还更改了 setup-test-env 作业:

  1. 首先下载由 cache-workhorse 构建并上传的 GitLab Workhorse 通用包。
  2. 如果成功检索到包,其内容会被放置在正确的文件夹中(例如,tmp/tests/gitlab-workhorse),从而防止在稍后运行 scripts/setup-test-env 时构建二进制文件。
  3. 如果包 URL 返回 404,行为与当前相比没有变化:GitLab Workhorse 二进制文件作为 scripts/setup-test-env 的一部分构建。

包的版本是 workhorse 树的 SHA(例如,git rev-parse HEAD:workhorse)。

cache-assets

这个 MR 中,我们引入了三个新的 cache-assets:testcache-assets:test as-if-fosscache-assets:production 作业,它们:

  • 除非 $CACHE_ASSETS_AS_PACKAGE == "true",否则从不运行
  • 为所有 GitLab.com gitlab-org/gitlab 定时流水线自动运行
  • 为任何触及资源相关文件夹的 master 提交自动运行
  • 对于 GitLab.com gitlab-org 的触及缓存相关文件的 MR,设置为手动

此作业尝试下载一个通用包,其中包含 GitLab 测试套件中需要的已编译 GitLab 资源(在 app/assets/javascripts/locale/**/app.jspublic/assets 下)。

  • 如果包 URL 返回 404:
    1. 它运行 bin/rake gitlab:assets:compile,以便编译 GitLab 资源。
    2. 然后创建一个包含资源的归档文件,并将其 作为通用包上传。 包版本设置为资源文件夹的哈希和。
  • 否则,如果包已存在,它成功退出作业。

compile-*-assets

我们还更改了 compile-test-assetscompile-production-assets 作业:

  1. 首先下载"原生"缓存资源,其中包含:

    • 已编译的资源

    • 一个 cached-assets-hash.txt 文件,包含资源所依赖的所有源文件的 SHA256 十六进制摘要。 此文件列表是一个悲观列表,资源可能不依赖于其中一些文件。最坏的情况下我们会更频繁地编译资源,这比使用过时的资源要好。

      该文件在 资源编译后创建

  2. 然后我们计算资源所依赖的所有源文件的 SHA256 十六进制摘要,对于当前检出的分支。我们将 十六进制摘要存储在 GITLAB_ASSETS_HASH 变量中

  3. 如果 $CACHE_ASSETS_AS_PACKAGE == "true",我们下载由 cache-assets:* 构建并上传的通用包。

    • 如果缓存对于检出的分支是最新的,我们下载原生缓存 缓存包。我们可以通过不下载通用包来优化这一点,但原生缓存实际上经常过时,因为它每 2 小时才重建一次。
  4. 我们 运行 assets_compile_script 函数,该函数 本身运行 assets:compile Rake 任务

    此任务负责决定是否需要编译资源。 它 $GITLAB_ASSETS_HASH 中的 HEAD SHA256 十六进制摘要与 cached-assets-hash.txt 中的 master 十六进制摘要进行比较

  5. 如果哈希值相同,我们不编译任何内容。如果不同,我们编译资源。

剥离的二进制文件

默认情况下,setup-test-env 创建一个包含已剥离二进制文件的制品,以 节省存储空间并加快后续 CI 作业的制品下载速度

为了更容易调试来自剥离二进制文件的崩溃,请在 setup-test-job 作业中注释掉包含 strip_executable_binaries 的行,然后启动一个新的流水线。