资源群组

  • Tier: 基础版,专业版,旗舰版
  • Offering: JihuLab.com, 私有化部署

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

添加资源组#

您只能为一个资源组添加一个资源。

假设您有以下流水线配置(存储库中的 .gitlab-ci.yml 文件):

yaml
1build: 2 stage: build 3 script: echo "Your build script" 4 5deploy: 6 stage: deploy 7 script: echo "Your deployment script" 8 environment: production

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

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

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

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

yaml
deploy: # ... resource_group: production

通过此配置,确保了部署的安全性,同时您仍然可以并发运行 build 作业,以最大化流水线效率。

前提条件#

处理模式#

您可以选择一种处理模式来战略性地控制作业并发性,以满足您的部署偏好。支持以下模式:

  • Unordered: 这是限制运行作业并发性的默认处理模式。当您不关心作业执行顺序时,这是最简单的选项。它会在作业准备好运行时开始处理。

  • Oldest first: 此处理模式限制作业并发性。当资源空闲时,它会从即将到来的作业列表(createdscheduledwaiting_for_resource 状态)中选择第一个作业,该列表按流水线 ID 升序排序。

    当您希望确保作业从最早的流水线执行时,此模式效率较高。与 unordered 模式相比,在流水线效率方面不如前者,但对于连续部署来说更安全。

  • Newest first: 此处理模式限制作业并发性。当资源空闲时,它会从即将到来的作业列表(createdscheduledwaiting_for_resource 状态)中选择第一个作业,该列表按流水线 ID 降序排序。

    当您希望确保作业从最新的流水线执行并通过 防止过时的部署作业 功能来防止所有旧的部署作业时,此模式效率较高。这是流水线效率方面最有效的选项,但您必须确保每个部署作业是幂等的。

更改处理模式#

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

  • unordered
  • oldest_first
  • newest_first

处理模式之间差异的示例#

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

yaml
1build: 2 stage: build 3 script: echo "Your build script" 4 5deploy: 6 stage: deploy 7 script: echo "Your deployment script" 8 environment: production 9 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 会等待这些流水线完成后再运行另一个。

yaml
1# .gitlab-ci.yml (parent pipeline) 2 3build: 4 stage: build 5 script: echo "Building..." 6 7test: 8 stage: test 9 script: echo "Testing..." 10 11deploy: 12 stage: deploy 13 trigger: 14 include: deploy.gitlab-ci.yml 15 strategy: depend 16 resource_group: AWS-production
yaml
1# deploy.gitlab-ci.yml (child pipeline) 2 3stages: 4 - provision 5 - deploy 6 7provision: 8 stage: provision 9 script: echo "Provisioning..." 10 11deployment: 12 stage: deploy 13 script: echo "Deploying..." 14 environment: production

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

故障排除#

避免流水线配置中的死锁#

由于 oldest_first 处理模式 强制按流水线顺序执行作业,因此有一种情况它与其他 CI 功能不兼容。

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

yaml
1# BAD 2test: 3 stage: test 4 trigger: 5 include: child-pipeline-requires-production-resource-group.yml 6 strategy: depend 7 8deploy: 9 stage: deploy 10 script: echo 11 resource_group: production 12 environment: production

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

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

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

yaml
1# GOOD 2test: 3 stage: test 4 trigger: 5 include: child-pipeline.yml 6 strategy: depend 7 resource_group: production # 在父流水线中指定资源组 8 9deploy: 10 stage: deploy 11 script: echo 12 resource_group: production 13 environment: production

作业卡在“等待资源”#

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

  1. 转到作业详情页面。

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

    • 如果状态是 runningpending,该功能正常工作。等待作业完成并释放资源。
    • 如果状态是 created 并且 处理模式Oldest firstNewest first,该功能正常工作。访问作业的流水线页面,并检查阻止执行的上游阶段或作业。
    • 如果不符合上述条件,该功能可能无法正常工作。向极狐GitLab 报告问题
  3. 如果 查看当前使用资源的作业 不可用,则资源未分配给作业。相反,检查资源的即将到来的作业。

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

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

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

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

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

  • 启动一个新的流水线。

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

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

通过 GraphQL 获取作业详情#

您可以从 GraphQL API 获取作业信息。如果您使用 跨项目/父子流水线的流水线级别并发控制,您应该使用 GraphQL API,因为触发作业无法从 UI 访问。

要从 GraphQL API 获取作业信息:

  1. 转到流水线详情页面。

  2. 选择 作业 标签并找到卡住作业的 ID。

  3. 转到 交互式 GraphQL 浏览器

  4. 运行以下查询:

    graphql
    1{ 2 project(fullPath: "<fullpath-to-your-project>") { 3 name 4 job(id: "gid://gitlab/Ci::Build/<job-id>") { 5 name 6 status 7 detailedStatus { 8 action { 9 path 10 buttonTitle 11 } 12 } 13 } 14 } 15}

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

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

    graphql
    1{ 2 project(fullPath: "<fullpath-to-your-project>") { 3 name 4 job(id: "gid://gitlab/Ci::Build/<job-id-currently-using-the-resource>") { 5 name 6 status 7 pipeline { 8 path 9 } 10 } 11 } 12}