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

优化 GitLab CI/CD 配置文件

  • 层级:Free、Premium、Ultimate
  • 提供:GitLab.com、GitLab 自托管、GitLab Dedicated

你可以通过使用以下方法减少 GitLab CI/CD 配置文件的复杂性和重复配置:

  • YAML 特定功能,如 锚点 (&)、别名 (*) 和映射合并 (<<)。阅读更多关于各种 YAML 功能
  • extends 关键字,它更灵活且可读性更强。你应该尽可能使用 extends

锚点

YAML 有一个名为“锚点”的功能,你可以用它来在文档中复制内容。

使用锚点来复制或继承属性。将锚点与 隐藏作业 结合使用,为你的作业提供模板。当存在重复键时,最新包含的键会覆盖其他键。

在某些情况下(参见 脚本用的 YAML 锚点),你可以使用 YAML 锚点来构建由其他地方定义的多个组件组成的数组。例如:

.default_scripts: &default_scripts
  - ./default-script1.sh
  - ./default-script2.sh

job1:
  script:
    - *default_scripts
    - ./job-script.sh

当你使用 include 关键字时,不能跨多个文件使用 YAML 锚点。锚点仅在定义它们的文件中有效。要从不同的 YAML 文件重用配置,请使用 !reference 标签extends 关键字

下面的示例使用了锚点和映射合并。它创建了两个作业 test1test2,它们继承了 .job_template 配置,每个作业都有自己自定义的 script 定义:

.job_template: &job_configuration  # 隐藏的 YAML 配置,定义了一个名为 'job_configuration' 的锚点
  image: ruby:2.6
  services:
    - postgres
    - redis

test1:
  <<: *job_configuration           # 添加 'job_configuration' 别名的内容
  script:
    - test1 project

test2:
  <<: *job_configuration           # 添加 'job_configuration' 别名的内容
  script:
    - test2 project

& 设置了锚点的名称(job_configuration),<< 表示“将给定的哈希合并到当前哈希中”,而 * 包含命名的锚点(再次是 job_configuration)。此示例的 展开版 如下:

.job_template:
  image: ruby:2.6
  services:
    - postgres
    - redis

test1:
  image: ruby:2.6
  services:
    - postgres
    - redis
  script:
    - test1 project

test2:
  image: ruby:2.6
  services:
    - postgres
    - redis
  script:
    - test2 project

你可以使用锚点来定义两组服务。例如,test:postgrestest:mysql 共享 .job_template 中定义的 script,但使用不同的 services,分别定义在 .postgres_services.mysql_services 中:

.job_template: &job_configuration
  script:
    - test project
  tags:
    - dev

.postgres_services:
  services: &postgres_configuration
    - postgres
    - ruby

.mysql_services:
  services: &mysql_configuration
    - mysql
    - ruby

test:postgres:
  <<: *job_configuration
  services: *postgres_configuration
  tags:
    - postgres

test:mysql:
  <<: *job_configuration
  services: *mysql_configuration

展开版 如下:

.job_template:
  script:
    - test project
  tags:
    - dev

.postgres_services:
  services:
    - postgres
    - ruby

.mysql_services:
  services:
    - mysql
    - ruby

test:postgres:
  script:
    - test project
  services:
    - postgres
    - ruby
  tags:
    - postgres

test:mysql:
  script:
    - test project
  services:
    - mysql
    - ruby
  tags:
    - dev

你可以看到隐藏作业被方便地用作模板,并且 tags: [postgres] 覆盖了 tags: [dev]

脚本用的 YAML 锚点

你可以将 YAML 锚点scriptbefore_scriptafter_script 结合使用,以在多个作业中使用预定义命令:

.some-script-before: &some-script-before
  - echo "先执行这个脚本"

.some-script: &some-script
  - echo "接着执行这个脚本"
  - echo "也执行这个脚本"

.some-script-after: &some-script-after
  - echo "最后执行这个脚本"

job1:
  before_script:
    - *some-script-before
  script:
    - *some-script
    - echo "仅为此作业执行某些操作"
  after_script:
    - *some-script-after

job2:
  script:
    - *some-script-before
    - *some-script
    - echo "为另一个作业执行其他操作"
    - *some-script-after

使用 extends 关键字重用配置部分

你可以使用 extends 关键字 在多个作业中重用配置。它与 YAML 锚点 类似,但更简单,并且你可以 结合 extendsinclude 使用

extends 支持多级继承。由于额外的复杂性,你应该避免使用超过三级,但最多可以使用十一级。以下示例有两级继承:

.tests:
  rules:
    - if: $CI_PIPELINE_SOURCE == "push"

.rspec:
  extends: .tests
  script: rake rspec

rspec 1:
  variables:
    RSPEC_SUITE: '1'
  extends: .rspec

rspec 2:
  variables:
    RSPEC_SUITE: '2'
  extends: .rspec

spinach:
  extends: .tests
  script: rake spinach

extends 中排除某个键

要从扩展内容中排除某个键,你必须将其赋值为 null,例如:

.base:
  script: test
  variables:
    VAR1: 基础变量 1

test1:
  extends: .base
  variables:
    VAR1: 测试1变量 1
    VAR2: 测试2变量 2

test2:
  extends: .base
  variables:
    VAR2: 测试2变量 2

test3:
  extends: .base
  variables: {}

test4:
  extends: .base
  variables: null

合并后的配置:

test1:
  script: test
  variables:
    VAR1: 测试1变量 1
    VAR2: 测试2变量 2

test2:
  script: test
  variables:
    VAR1: 基础变量 1
    VAR2: 测试2变量 2

test3:
  script: test
  variables:
    VAR1: 基础变量 1

test4:
  script: test
  variables: null

结合使用 extendsinclude

要从不同的配置文件重用配置,请结合使用 extendsinclude

在以下示例中,scriptincluded.yml 文件中定义。然后在 .gitlab-ci.yml 文件中,extends 引用了 script 的内容:

  • included.yml:

    .template:
      script:
        - echo Hello!
  • .gitlab-ci.yml:

    include: included.yml
    
    useTemplate:
      image: alpine
      extends: .template

合并细节

你可以使用 extends 来合并哈希但不能合并数组。当存在重复键时,GitLab 会基于键进行反向深度合并。来自最后一个成员的键总是覆盖其他级别定义的内容。例如:

.only-important:
  variables:
    URL: "http://my-url.internal"
    IMPORTANT_VAR: "详细信息"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_BRANCH == "stable"
  tags:
    - production
  script:
    - echo "Hello world!"

.in-docker:
  variables:
    URL: "http://docker-url.internal"
  tags:
    - docker
  image: alpine

rspec:
  variables:
    GITLAB: "很棒"
  extends:
    - .only-important
    - .in-docker
  script:
    - rake rspec

结果是这个 rspec 作业:

rspec:
  variables:
    URL: "http://docker-url.internal"
    IMPORTANT_VAR: "详细信息"
    GITLAB: "很棒"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_BRANCH == "stable"
  tags:
    - docker
  image: alpine
  script:
    - rake rspec

在此示例中:

  • variables 部分合并,但 URL: "http://docker-url.internal" 覆盖了 URL: "http://my-url.internal"
  • tags: ['docker'] 覆盖了 tags: ['production']
  • script 不合并,但 script: ['rake rspec'] 覆盖了 script: ['echo "Hello world!"']。你可以使用 YAML 锚点 来合并数组。

!reference 标签

使用自定义 YAML 标签 !reference 来选择其他作业部分的配置并将其重用在当前部分。与 YAML 锚点 不同,你可以使用 !reference 标签来重用 included 配置文件中的配置。

在以下示例中,test 作业重用了来自两个不同位置的 scriptafter_script

  • configs.yml:

    .setup:
      script:
        - echo 创建环境
  • .gitlab-ci.yml:

    include:
      - local: configs.yml
    
    .teardown:
      after_script:
        - echo 删除环境
    
    test:
      script:
        - !reference [.setup, script]
        - echo 执行我自己的命令
      after_script:
        - !reference [.teardown, after_script]

在以下示例中,test-vars-1 重用了 .vars 中的所有变量,而 test-vars-2 选择了一个特定的变量并将其作为新的 MY_VAR 变量重用。

.vars:
  variables:
    URL: "http://my-url.internal"
    IMPORTANT_VAR: "详细信息"

test-vars-1:
  variables: !reference [.vars, variables]
  script:
    - printenv

test-vars-2:
  variables:
    MY_VAR: !reference [.vars, variables, IMPORTANT_VAR]
  script:
    - printenv

在使用 !reference 标签和 parallel:matrix 关键字 时,有一个 已知问题

scriptbefore_scriptafter_script 中嵌套 !reference 标签

你可以在 scriptbefore_scriptafter_script 部分中将 !reference 标签嵌套多达 10 级。使用嵌套标签在构建更复杂的脚本时定义可重用部分。例如:

.snippets:
  one:
    - echo "一!"
  two:
    - !reference [.snippets, one]
    - echo "二!"
  three:
    - !reference [.snippets, two]
    - echo "三!"

nested-references:
  script:
    - !reference [.snippets, three]

在此示例中,nested-references 作业运行所有三个 echo 命令。

配置你的 IDE 以支持 !reference 标签

pipeline editor 支持 !reference 标签。但是,自定义 YAML 标签(如 !reference)的架构规则默认可能会被你的编辑器视为无效。

你可以配置一些编辑器以接受 !reference 标签。例如:

  • 在 VS Code 中,你可以在 settings.json 文件中将 vscode-yaml 设置为解析 customTags

    "yaml.customTags": [
       "!reference sequence"
    ]
  • 在 Sublime Text 中,如果你使用的是 LSP-yaml 包,你可以在 LSP-yaml 用户设置中设置 customTags

    {
      "settings": {
        "yaml.customTags": ["!reference sequence"]
      }
    }