资源组
- 层级: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每次你向分支推送新提交时,都会运行一个包含 build 和 deploy 两个作业的新流水线。但如果你在短时间内推送多个提交,多个流水线会同时运行,例如:
- 第一个流水线运行作业
build->deploy - 第二个流水线运行作业
build->deploy
在这种情况下,不同流水线中的 deploy 作业可能会并发运行到 production 环境。向同一基础设施运行多个部署脚本可能会损害/混淆实例,在最坏的情况下使其处于损坏状态。
为确保 deploy 作业一次只运行一个,你可以为并发敏感的作业指定 resource_group 关键字:
deploy:
# ...
resource_group: production通过此配置,部署的安全性得到保证,同时你仍然可以并发运行 build 作业,以最大化流水线效率。
前置条件
- 熟悉 GitLab CI/CD 流水线
- 熟悉 GitLab 环境和部署
- 至少需要项目的 Developer 角色来配置 CI/CD 流水线。
处理模式
你可以选择处理模式来控制作业的并发性,以适应你的部署偏好。支持以下模式:
| 处理模式 | 描述 | 何时使用 |
|---|---|---|
unordered |
默认处理模式。每当作业准备好运行时就处理。 | 作业执行顺序不重要。最容易使用的选项。 |
oldest_first |
当资源空闲时,从按流水线 ID 升序排列的待处理作业列表中选择第一个作业。 | 你希望先执行最旧流水线中的作业。比 unordered 模式效率低,但对持续部署更安全。 |
newest_first |
当资源空闲时,从按流水线 ID 降序排列的待处理作业列表中选择第一个作业。 | 你希望执行最新流水线中的作业并防止过时的部署作业。每个作业必须是幂等的。 |
newest_ready_first |
当资源空闲时,从等待此资源的待处理作业列表中选择第一个作业。作业按流水线 ID 降序排列。 | 你希望防止 newest_first 在部署当前流水线之前优先处理新流水线。比 newest_first 更快。每个作业必须是幂等的。 |
更改处理模式
要更改资源组的处理模式,你必须使用 API 并通过指定 process_mode 发送请求到 编辑现有资源组:
unorderedoldest_firstnewest_firstnewest_ready_first
处理模式差异示例
考虑以下 .gitlab-ci.yml,其中我们有两个作业 build 和 deploy,每个作业都在自己的阶段中运行,并且 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-1、deploy-2和deploy-3不会并发运行。- 作业执行顺序没有保证,例如
deploy-1可能在deploy-3运行之前或之后运行。
- 如果处理模式是
oldest_first:deploy-1、deploy-2和deploy-3不会并发运行。deploy-1先运行,deploy-2第二个运行,deploy-3最后运行。
- 如果处理模式是
newest_first:deploy-1、deploy-2和deploy-3不会并发运行。deploy-3先运行,deploy-2第二个运行,deploy-1最后运行。
使用跨项目/父子流水线的流水线级并发控制
你可以为对并发执行敏感的下游流水线定义 resource_group。trigger 关键字 可以触发下游流水线,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>。要解决此问题,首先检查资源组是否正常工作:
-
转到作业详情页面。
-
如果资源已分配给作业,选择查看当前使用资源的作业并检查作业状态。
- 如果状态是
running或pending,则功能正常工作。等待作业完成并释放资源。 - 如果状态是
created且处理模式是最早优先或最新优先,则功能正常工作。 访问作业的流水线页面,检查哪个上游阶段或作业正在阻塞执行。 - 如果不满足上述任何条件,功能可能无法正常工作。向 GitLab 报告问题。
- 如果状态是
-
如果查看当前使用资源的作业不可用,则资源未分配给作业。相反,检查资源的待处理作业。
- 使用 REST API 获取资源的待处理作业。
- 验证资源组的处理模式是最早优先。
- 在待处理作业列表中找到第一个作业,并通过 GraphQL 获取作业详情。
- 如果第一个作业的流水线是较旧的流水线,尝试取消流水线或作业本身。
- 可选。如果下一个待处理作业仍然在不应再运行的较旧流水线中,重复此过程。
- 如果问题持续存在,向 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 获取作业信息:
-
转到流水线详情页面。
-
选择作业选项卡并找到卡住作业的 ID。
-
运行以下查询:
{ 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。 -
运行以下查询并根据上述标准检查
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。
-
作业状态。
-
问题发生的频率。
-
复现问题的步骤。
你也可以联系支持团队获取进一步帮助,或与开发团队取得联系。