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

资源组

  • 层级:Free, Premium, Ultimate
  • 提供方式:GitLab.com, GitLab Self-Managed, GitLab Dedicated

默认情况下,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 作业,以最大化流水线效率。

前置条件

处理模式

你可以选择处理模式来控制作业的并发性,以适应你的部署偏好。支持以下模式:

处理模式 描述 何时使用
unordered 默认处理模式。每当作业准备好运行时就处理。 作业执行顺序不重要。最容易使用的选项。
oldest_first 当资源空闲时,从按流水线 ID 升序排列的待处理作业列表中选择第一个作业。 你希望先执行最旧流水线中的作业。比 unordered 模式效率低,但对持续部署更安全。
newest_first 当资源空闲时,从按流水线 ID 降序排列的待处理作业列表中选择第一个作业。 你希望执行最新流水线中的作业并防止过时的部署作业。每个作业必须是幂等的。
newest_ready_first 当资源空闲时,从等待此资源的待处理作业列表中选择第一个作业。作业按流水线 ID 降序排列。 你希望防止 newest_first 在部署当前流水线之前优先处理新流水线。比 newest_first 更快。每个作业必须是幂等的。

更改处理模式

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

  • unordered
  • oldest_first
  • newest_first
  • newest_ready_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(父流水线)

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

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

deploy:
  stage: deploy
  trigger:
    include: deploy.gitlab-ci.yml
    strategy: mirror
  resource_group: AWS-production
# deploy.gitlab-ci.yml(子流水线)

stages:
  - provision
  - deploy

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

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

你必须定义 trigger:strategy 以确保锁在下游流水线完成之前不会被释放。

相关主题

故障排除

避免流水线配置中的死锁

因为 oldest_first 处理模式 强制作按流水线顺序执行,在某些情况下它与其他 CI 功能配合不佳。

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

# 错误配置
test:
  stage: test
  trigger:
    include: child-pipeline-requires-production-resource-group.yml
    strategy: mirror

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

在父流水线中,它运行 test 作业,该作业随后运行一个子流水线,strategy: mirror 选项 使 test 作业等待直到子流水线完成。父流水线在下一阶段运行 deploy 作业,该作业需要来自 production 资源组的资源。如果处理模式是 oldest_first,它会从最旧的流水线中执行作业,这意味着 deploy 作业接下来执行。

但是,子流水线也需要来自 production 资源组的资源。因为子流水线比父流水线更新,子流水线会等待 deploy 作业完成,而这永远不会发生。

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

# 正确配置
test:
  stage: test
  trigger:
    include: child-pipeline.yml
    strategy: mirror
  resource_group: production # 在父流水线中指定资源组

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

作业卡在"等待资源"

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

  1. 转到作业详情页面。

  2. 如果资源已分配给作业,选择查看当前使用资源的作业并检查作业状态。

    • 如果状态是 runningpending,则功能正常工作。等待作业完成并释放资源。
    • 如果状态是 created处理模式最早优先最新优先,则功能正常工作。 访问作业的流水线页面,检查哪个上游阶段或作业正在阻塞执行。
    • 如果不满足上述任何条件,功能可能无法正常工作。向 GitLab 报告问题
  3. 如果查看当前使用资源的作业不可用,则资源未分配给作业。相反,检查资源的待处理作业。

    1. 使用 REST API 获取资源的待处理作业。
    2. 验证资源组的处理模式最早优先
    3. 在待处理作业列表中找到第一个作业,并通过 GraphQL 获取作业详情。
    4. 如果第一个作业的流水线是较旧的流水线,尝试取消流水线或作业本身。
    5. 可选。如果下一个待处理作业仍然在不应再运行的较旧流水线中,重复此过程。
    6. 如果问题持续存在,向 GitLab 报告问题

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

如果你无法通过上述解决方案解决问题,你可能遇到了已知的竞态条件问题。竞态条件发生在复杂或繁忙的流水线中。 例如,如果你有以下情况,可能会遇到竞态条件:

  • 包含多个子流水线的流水线。
  • 单个项目中有多个流水线同时运行。

如果你认为遇到了这个问题,向 GitLab 报告问题并在 issue 436988 上留下你的新问题链接。 为了确认问题,GitLab 可能会要求提供其他详细信息,例如你的完整流水线配置。

作为临时解决方案,你可以:

  • 启动一个新流水线。

  • 重新运行一个已完成且与卡住作业具有相同资源组的作业。

    例如,如果你有一个 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。

  3. 转到交互式 GraphQL 探索器

  4. 运行以下查询:

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

    job.detailedStatus.action.path 字段包含使用资源的作业 ID。

  5. 运行以下查询并根据上述标准检查 job.status 字段。你还可以从 pipeline.path 字段访问流水线页面。

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

报告问题

使用以下信息打开新问题

  • 受影响作业的 ID。

  • 作业状态。

  • 问题发生的频率。

  • 复现问题的步骤。

    你也可以联系支持团队获取进一步帮助,或与开发团队取得联系。