资源组

默认情况下,极狐GitLab CI/CD 中的流水线并行运行。并行化是改善合并请求中反馈循环的重要因素,但是,在某些情况下,您可能希望限制部署作业的并发性,来逐个运行它们。 使用资源组从战略上控制作业的并发性,来安全地优化您的持续部署工作流程。

添加资源组

您应具有以下流水线配置(仓库中的.gitlab-ci.yml 文件):

build:
  stage: build
  script: echo "Your build script"

deploy:
  stage: deploy
  script: echo "Your deployment script"
  environment: production

每次将新提交推送到分支时,它都会运行一个新流水线,其中包含两个作业 builddeploy。但是如果您在短时间内推送多个提交,多个流水线会同时开始运行,例如:

  • 第一个流水线运行作业 build -> deploy
  • 第二个流水线运行作业 build -> deploy

在这种情况下,跨不同流水线的 deploy 作业可以同时运行到 production 环境中。对同一基础架构运行多个部署脚本可能会损害/混淆实例,并在最坏的情况下使其处于损坏状态。

为确保 deploy 作业每次只运行一次,您可以为并发敏感作业指定 resource_group 关键字

deploy:
  ...
  resource_group: production

使用此配置,可以确保部署的安全性,同时您仍然可以同时运行 build 作业,最大限度地提高流水线效率。

要求

限制

一个资源只能附加到一个资源组。

流程模式

您可以选择一种流程模式来战略性地控制您的部署作业并发。 支持以下模式:

  • 无序:这是限制运行作业并发的默认流程模式。当您不关心作业的执行顺序时,这是最简单的选择。只要作业准备好运行,它就会开始处理作业。
  • 最早在前:这种模式限制了作业的并发性。当资源空闲时,它会在即将到来的作业(createdscheduledwaiting_for_resource 状态)列表中,按流水线 ID 升序排序选择第一个作业。

    当您要确保从最旧的流水线开始执行作业时,此模式非常有效。就流水线效率而言,与“无序”模式相比,它的效率较低,但对于持续部署来说更安全。

  • 最新在前:这种模式限制了作业的并发性。当资源空闲时,它会在即将到来的作业(createdscheduledwaiting_for_resource 状态)列表中,按流水线 ID 降序排序选择第一个作业。

    当您希望确保从最新流水线执行作业并使用跳过过时的部署作业功能。就流水线效率而言,这是最有效的选项,但您必须确保每个部署作业都是幂等的。

更改流程模式

要更改资源组的流程模式,您必须使用 API 并发送请求,通过指定 process_mode 编辑现有资源组

  • unordered
  • oldest_first
  • newest_first

流程模式之间的差异示例

考虑以下 .gitlab-ci.yml,其中我们有两个作业 builddeploy,每个作业都在各自的阶段运行,并且 deploy 作业有一个资源组设置为 production

build:
  stage: build
  script: echo "Your build script"

deploy:
  stage: deploy
  script: echo "Your deployment script"
  environment: production
  resource_group: production

如果在短时间内将三个提交推送到项目,这意味着三个流水线几乎同时运行:

  • 第一个流水线运行作业 build -> deploy。我们将此部署作业称为 deploy-1
  • 第二个流水线运行作业 build -> deploy。我们将此部署作业称为 deploy-2
  • 第三个流水线运行作业 build -> deploy。我们将此部署作业称为 deploy-3

根据资源组的流程模式:

  • 如果设置为 unordered
    • deploy-1deploy-2deploy-3 不会并行运行。
    • 作业执行顺序无法保证,例如,deploy-1 可以在 deploy-3 运行之前或之后运行。
  • 如果设置为 oldest_first
    • deploy-1deploy-2deploy-3 不会并行运行。
    • deploy-1 首先运行,deploy-2 第二个运行,deploy-3 最后运行。
  • 如果设置为 newest_first
    • deploy-1deploy-2deploy-3 不会并行运行。
    • deploy-3 首先运行,deploy-2 第二个运行,deploy-1 最后运行。

跨项目/父子流水线的流水线级并发控制

您可以为对并发执行敏感的下游流水线定义 resource_grouptrigger 关键字可以触发下游流水线,resource_group 关键字可以与之共存。resource_group 可以有效控制部署流水线的并发,而其他作业可以继续并发运行。

以下示例在一个项目中有两个流水线配置。当流水线开始运行时,非敏感作业首先执行,不受其他流水线中并发执行的影响。 但是,极狐GitLab 确保在触发部署(子)流水线之前没有其他部署流水线正在运行。如果其他部署流水线正在运行,极狐GitLab 会等到这些流水线完成后再运行另一个。

# .gitlab-ci.yml (parent pipeline)

build:
  stage: build
  script: echo "Building..."

test:
  stage: test
  script: echo "Testing..."

deploy:
  stage: deploy
  trigger:
    include: deploy.gitlab-ci.yml
    strategy: depend
  resource_group: AWS-production
# deploy.gitlab-ci.yml (child pipeline)

stages:
  - provision
  - deploy

provision:
  stage: provision
  script: echo "Provisioning..."

deployment:
  stage: deploy
  script: echo "Deploying..."
  environment: production

您必须使用 trigger 关键字定义 strategy:depend。这确保了在下游流水线完成之前不会释放锁。

相关主题

故障排除

避免流水线配置中的死锁

因为 oldest_first 模式强制作业按流水线顺序执行,所以有时它不能很好地与其他 CI 功能配合使用。

例如,当您运行需要与父流水线相同的资源组的子流水线 时,可能会发生死锁。以下是一个错误设置的示例:

# BAD
test:
  stage: test
  trigger:
    include: child-pipeline-requires-production-resource-group.yml
    strategy: depend

deploy:
  stage: deploy
  script: echo
  resource_group: production
  environment: production

在父流水线中,它运行 test 作业,test 作业运行子流水线,并且 strategy:depend 选项使 test 作业等到子流水线已完成。 父流水线在下一阶段运行 deploy 作业,这需要 production 资源组中的资源。 如果进程模式是 oldest_first,它会从最旧的流水线执行作业,这意味着接下来会执行 deploy 作业。

但是,子流水线还需要来自 production 资源组的资源。 因为子流水线比父流水线更新,所以子流水线一直要等到 deploy 作业完成,这是永远不会发生的事情。

在这种情况下,您应该在父流水线配置中指定 resource_group 关键字:

# GOOD
test:
  stage: test
  trigger:
    include: child-pipeline.yml
    strategy: depend
  resource_group: production # Specify the resource group in the parent pipeline

deploy:
  stage: deploy
  script: echo
  resource_group: production
  environment: production

作业阻塞:”Waiting for resource”

有时候,作业挂起,显示消息 Waiting for resource: <resource_group>。要解决此问题,请首先检查资源组是否正常工作:

  1. 前往作业详细信息页面。
  2. 如果资源分配给作业,请选择 查看当前使用资源的作业 并检查作业状态。
    • 如果状态为 runningpending,则功能正常工作。等待作业完成并释放资源。
    • 如果状态为 created,并且流程模式Oldest firstNewest first,则功能正常工作。查看作业的流水线页面,检查哪个上游阶段或作业阻止了执行。
    • 如果以上条件均不满足,则功能可能无法正常工作。可以在论坛上发帖寻求专业人员的支持
  3. 如果 当前使用资源的作业 不可用,则资源未被分配给作业。相反地,检查资源的即将执行的作业。

    1. 使用 REST API 获取资源的即将执行的作业。
    2. 验证资源组的流程模式是否为 Oldest first
    3. 在即将执行的作业列表中找到第一个作业,并使用 GraphQL 获取作业详细信息。
    4. 如果第一个作业的流水线是较旧的流水线,请尝试取消流水线或作业本身。
    5. 可选。 如果下一个即将执行的作业仍在较旧的流水线中,而该流水线不应再运行,请重复此过程。
    6. 如果问题仍然存在,可以在论坛上发帖寻求专业人员的支持

复杂或繁忙流水线中的竞争条件

如果使用上述的方案还没能解决您的问题,您可能遇到了一个已知的竞争条件问题。该竞争条件发生在复杂或繁忙的流水线中。比如,您可能遇到该问题,如果:

  • 一个流水线中有多个子流水线。
  • 一个项目中有多个流水线同时运行。

临时解决方案为:

  • 启动一个新的流水线。
  • 重新运行已完成的作业,该作业与卡住的作业具有相同的资源组。

    比如,如果您有一个 setup_job 和一个 deploy_job,它们具有相同的资源组,setup_job 可能会在 deploy_job 卡在 “waiting for resource” 时完成。重新运行 setup_job 以重新启动整个过程并允许 deploy_job 完成。

通过 GraphQL 获取作业详情

您可以从 GraphQL API 获取作业详情。如果您在使用 具有跨项目/父子流水线的流水线级别并发控制 ,则应使用 GraphQL API,因为触发作业在 UI 中不可用。

要想从 GraphQL API 获取作业信息:

  1. 前往流水线详情页面。
  2. 选择 作业 选项卡并找到 ID 为 job-id 的作业。
  3. 前往 交互式 GraphQL 浏览器
  4. 运行如下查询:

    {
      project(fullPath: "<fullpath-to-your-project>") {
        name
        job(id: "gid://gitlab/Ci::Build/<job-id>") {
          name
          status
          detailedStatus {
            action {
              path
              buttonTitle
            }
          }
        }
      }
    }
    

    The job.detailedStatus.action.path field contains the job ID using the resource.

  5. Run the following query and check job.status field according to the criteria above. You can also visit the pipeline page from pipeline.path field.

    {
      project(fullPath: "<fullpath-to-your-project>") {
        name
        job(id: "gid://gitlab/Ci::Build/<job-id-currently-using-the-resource>") {
          name
          status
          pipeline {
            path
          }
        }
      }
    }