为大型仓库优化极狐GitLab

由于克隆和检出需要时间,工作树中包含超过 50k 个文件的大型仓库可能需要超出流水线效率的更多优化。

极狐GitLab 和极狐GitLab Runner 可以很好地处理这种情况,但需要优化配置才能有效地执行其一组操作。

处理大型仓库的一般准则很简单。 以下各节更详细地描述了每条准则:

  • 始终以增量方式获取。不要以导致重新创建所有工作树的方式进行克隆。
  • 始终使用浅克隆来减少数据传输。请注意,由于更高的 CPU 影响,这会给极狐GitLab 实例带来更多负担。
  • 如果您大量使用基于派生的工作流程,请控制克隆目录。
  • 优化 git clean 标志,确保您删除或保留可能影响或加速构建的数据。

浅克隆

极狐GitLab 和极狐GitLab Runner 默认执行浅克隆

理想情况下,您应该为 GIT_DEPTH 始终使用 10 这样的小数字,指示极狐GitLab Runner 执行浅克隆。 浅克隆使 Git 仅请求给定分支的最新更改集,最多可达到 GIT_DEPTH 变量定义的期望提交次数。

这样做加快了从 Git 仓库中获取更改的速度,因为我们有效地减少了数据传输量,特别是当仓库有很长的积压工作,其中包含大量大文件时。

以下示例使 runner 浅克隆仅获取给定分支;它不获取任何其他分支或标签。

variables:
  GIT_DEPTH: 10

test:
  script:
    - ls -al

Git 策略

默认情况下,极狐GitLab 配置为使用 fetch Git 策略,推荐用于大型仓库。 此策略减少了要传输的数据量,并且不会真正影响您可能从 CI 对仓库执行的操作。

Git 克隆路径

引入于极狐GitLab Runner 11.10。

GIT_CLONE_PATH 允许您控制克隆源的位置。如果您大量使用带有派生工作流的大型仓库,这可能会产生影响。

从极狐GitLab Runner 的角度来看,派生工作流存储为具有单独工作树的单独仓库。这意味着极狐GitLab Runner 无法优化工作树的使用。

在这种情况下,您希望使极狐GitLab Runner 执行器仅用于给定项目,而不是在不同项目之间共享,来提高此过程的效率。

GIT_CLONE_PATH 必须在$CI_BUILDS_DIR 中。目前,不能从磁盘中选择任何路径。

Git 清理标志

引入于极狐GitLab Runner 11.10。

GIT_CLEAN_FLAGS 允许您控制是否需要为每个 CI 作业执行 git clean 命令。默认情况下,极狐GitLab 确保您的工作树位于给定的 SHA 上,并且您的仓库是干净的。

GIT_CLEAN_FLAGS 在设置为 none 时被禁用。 在非常大的仓库上,这可能是需要的,因为 git clean 是磁盘 I/O 密集型的。使用 GIT_CLEAN_FLAGS: -ffdx -e .build/ (例如)控制它,允许您控制和禁用在后续运行之间删除工作树中的某些目录,可以加速增量构建。如果您重用现有机器,并拥有可重用于构建的现有工作树,将产生最大的影响。

有关 GIT_CLEAN_FLAGS 接受的确切参数,请参阅 git clean 的文档。可用参数取决于 Git 版本。

Git 抓取额外的标志

引入于极狐GitLab Runner 13.1。

GIT_FETCH_EXTRA_FLAGS 允许您通过传递额外标志来修改 git fetch 行为。

例如,如果您的项目包含大量 CI 作业不依赖的标签,您可以将 --no-tags 添加为额外的标志,使您的抓取更快,更紧凑。

同样,如果您的仓库包含很多标签,--no-tags 在某些情况下会产生很大的不同。 如果您的 CI 构建不依赖于 Git 标签,那么值得一试。

基于派生的工作流程

引入于极狐GitLab Runner 11.10。

按照上面的指导,假设我们想要:

  • 针对大型项目(目录中超过 50k 文件)进行优化。
  • 使用基于派生的工作流程进行贡献。
  • 重用现有的工作树。具有预配置的 runner,这些 runner 预克隆了仓库。
  • Runner 仅分配给项目和所有派生项目。

让我们考虑以下两个示例,一个使用 shell 执行器,另一个使用 docker 执行器。

shell 执行器示例

假设您有以下 config.toml

concurrent = 4

[[runners]]
  url = "GITLAB_URL"
  token = "TOKEN"
  executor = "shell"
  builds_dir = "/builds"
  cache_dir = "/cache"

  [runners.custom_build_dir]
    enabled = true

这个config.toml

  • 使用 shell 执行器。
  • 指定存储所有克隆的自定义 /builds 目录。
  • 启用指定 GIT_CLONE_PATH 的能力。
  • 一次最多运行 4 个作业。

docker 执行器示例

假设您有以下 config.toml

concurrent = 4

[[runners]]
  url = "GITLAB_URL"
  token = "TOKEN"
  executor = "docker"
  builds_dir = "/builds"
  cache_dir = "/cache"

  [runners.docker]
    volumes = ["/builds:/builds", "/cache:/cache"]

这个 config.toml

  • 使用 docker 执行器,
  • 指定磁盘上存储所有克隆的自定义 /builds 目录。我们托管挂载 /builds 目录,使其在后续运行之间可重用,并允许覆盖克隆策略。
  • 不启用指定 GIT_CLONE_PATH 的功能,因为它默认启用。
  • 一次最多运行 4 个作业。

我们的 .gitlab-ci.yml

一旦我们配置了执行器,我们需要微调 .gitlab-ci.yml

如果我们使用以下 .gitlab-ci.yml,我们的流水线性能最高:

variables:
  GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_NAME

build:
  script: ls -al

此 YAML 设置配置自定义克隆路径。此路径使得在父项目和分支之间重用工作树成为可能,因为我们对所有分支使用相同的克隆路径。

为什么使用 $CI_CONCURRENT_ID?主要原因是确保使用的工作树不会在项目之间发生冲突。$CI_CONCURRENT_ID 表示给定执行器中的唯一标识符。 当我们使用它来构造路径时,这个目录不会与其他正在运行的并发作业发生冲突。

将自定义克隆选项存储在 config.toml

理想情况下,所有与作业相关的配置都应该存储在 .gitlab-ci.yml 中。 但是,有时希望将这些方案作为 runner 配置的一部分。

在上面的派生示例中,此配置可能是首选,但这会带来管理开销,因为需要为每个分支更新 .gitlab-ci.yml。 在这种情况下,可能需要保持 .gitlab-ci.yml 克隆路径不可知,但使其成为 runner 的配置。

如果 .gitlab-ci.yml 没有覆盖它,我们可以使用 runner 的以下规范扩展我们的 config.toml

concurrent = 4

[[runners]]
  url = "GITLAB_URL"
  token = "TOKEN"
  executor = "docker"
  builds_dir = "/builds"
  cache_dir = "/cache"

  environment = [
    "GIT_DEPTH=10",
    "GIT_CLONE_PATH=$CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_NAME"
  ]

  [runners.docker]
    volumes = ["/builds:/builds", "/cache:/cache"]

这使得克隆配置成为给定 runner 的一部分,并且不需要我们更新每个 .gitlab-ci.yml