CI/CD 组件实例

测试组件

取决于组件的功能性,测试组件可能需要在仓库中添加额外的文件。比如,一个用于特定编程语言的代码检查、构建和测试的组件需要实际的源代码示例。您可以在同一个仓库中拥有源代码示例、配置文件等。

示例 1:测试一个 Rust 语言的 CI/CD 组件

取决于组件的功能性,测试组件可能需要在仓库中添加额外的文件。

以下是 Rust 编程语言的“hello world”示例,使用 cargo 工具链以简化操作:

  1. 前往 CI/CD 组件根目录。
  2. 使用 cargo init 命令初始化一个新的 Rust 项目。

    cargo init
    

    此命令会创建所有必须的项目文件,包括 src/main.rs 的 “hello world” 示例。这一步足以在组件作业中使用 cargo build 命令来构建 Rust 源代码。

    tree
    .
    ├── Cargo.toml
    ├── LICENSE.md
    ├── README.md
    ├── src
    │   └── main.rs
    └── templates
        └── build.yml
    
  3. 确保组件有一个构建 Rust 源代码的作业,例如,在 templates/build.yml 中:

    spec:
      inputs:
        stage:
          default: build
          description: 'Defines the build stage'
        rust_version:
          default: latest
          description: 'Specify the Rust version, use values from https://hub.docker.com/_/rust/tags Defaults to latest'
    ---
    
    "build-$[[ inputs.rust_version ]]":
      stage: $[[ inputs.stage ]]
      image: rust:$[[ inputs.rust_version ]]
      script:
        - cargo build --verbose
    

    在此示例中: - 可以修改 stagerust_version 输入的默认值。CI/CD 作业以 build- 前缀开头,并根据 rust_version 输入动态创建名称。命令 cargo build --verbose 编译 Rust 源代码。

  4. 在项目的 .gitlab-ci.yml 配置文件中测试组件的 build 模板:

    include:
      # include the component located in the current project from the current SHA
      - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA
        inputs:
          stage: build
    
    stages: [build, test, release]
    
  5. 要运行更多测试,将更多功能和测试添加到 Rust 代码中,并在 templates/test.yml 中添加组件模板和运行 cargo test 的作业:

    spec:
      inputs:
        stage:
          default: test
          description: 'Defines the test stage'
        rust_version:
          default: latest
          description: 'Specify the Rust version, use values from https://hub.docker.com/_/rust/tags Defaults to latest'
    ---
    
    "test-$[[ inputs.rust_version ]]":
      stage: $[[ inputs.stage ]]
      image: rust:$[[ inputs.rust_version ]]
      script:
        - cargo test --verbose
    
  6. 通过在流水线中引用 test 组件模板来测试附加作业:

    include:
      # include the component located in the current project from the current SHA
      - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA
        inputs:
          stage: build
      - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/test@$CI_COMMIT_SHA
        inputs:
          stage: test
    
    stages: [build, test, release]
    

CI/CD 组件模式

此部分提供了一些在 CI/CD 组件中实现常见模式的实用示例。

使用布尔输入有条件地配置作业

你可以通过将 boolean 类型输入与 extends 功能组合来组合具有两个条件的作业。

比如,要配置一个具有 boolean 输入的复杂缓存行为:

spec:
  inputs:
    enable_special_caching:
      description: 'If set to `true` configures a complex caching behavior'
      type: boolean
---

.my-component:enable_special_caching:false:
  extends: null

.my-component:enable_special_caching:true:
  cache:
    policy: pull-push
    key: $CI_COMMIT_SHA
    paths: [...]

my-job:
  extends: '.my-component:enable_special_caching:$[[ inputs.enable_special_caching ]]'
  script: ... # run some fancy tooling

通过将 enable_special_caching 输入传递给作业的 extends 关键字,可以实现此模式。取决于 enable_special_cachingtrue 还是 false,从预定义的隐藏作业中选择适当的配置(.my-component:enable_special_caching:true.my-component:enable_special_caching:false)。

使用 options 来有条件地配置作业

您可以使用多个选项来组合作业,以实现类似于 ifelseif 条件的类似行为。使用 extendsstring 类型以及多个 options 来实现任何数量的条件。

比如,要配置具有 3 个不同选项的复杂缓存行为:

spec:
  inputs:
    cache_mode:
      description: Defines the caching mode to use for this component
      type: string
      options:
        - default
        - aggressive
        - relaxed
---

.my-component:enable_special_caching:false:
  extends: null

.my-component:cache_mode:aggressive:
  cache:
    policy: push
    key: $CI_COMMIT_SHA
    paths: ['*/**']

.my-component:cache_mode:relaxed:
  cache:
    policy: pull-push
    key: $CI_COMMIT_BRANCH
    paths: ['bin/*']

my-job:
  extends: '.my-component:cache_mode:$[[ inputs.cache_mode ]]'
  script: ... # run some fancy tooling

在此示例中,cache_mode 输入提供了 defaultaggressiverelaxed 选项,每个选项对应一个不同的隐藏作业。通过使用 extends: '.my-component:cache_mode:$[[ inputs.cache_mode ]]' 来扩展组件作业,作业会动态地继承基于所选选项的正确缓存配置。

CI/CD 组件迁移示例

此部分将展示将 CI/CD 模板和流水线配置迁移到可重用的 CI/CD 组件的实用示例。

CI/CD 组件迁移示例:Go

一个完整的软件开发生命周期的流水线可能由多个作业和阶段组成。针对编程语言的 CI/CD 模板可能会在单个模版文件中提供多个作业。作为实践,下面的 Go CI/CD 模板应该被迁移。

default:
  image: golang:latest

stages:
  - test
  - build
  - deploy

format:
  stage: test
  script:
    - go fmt $(go list ./... | grep -v /vendor/)
    - go vet $(go list ./... | grep -v /vendor/)
    - go test -race $(go list ./... | grep -v /vendor/)

compile:
  stage: build
  script:
    - mkdir -p mybinaries
    - go build -o mybinaries ./...
  artifacts:
    paths:
      - mybinaries
note 您还可以先迁移一个作业,而不是迁移所有作业。遵循如下指南,仅在第一次迭代中迁移 build CI/CD 作业。

CI/CD 模板迁移涉及如下步骤:

  1. 分析 CI/CD 作业和依赖并定义迁移操作:
    • image 配置是全局的,需要移动到作业定义中
    • format 作业在一个作业中运行多个 go 命令。go test 命令应该移动到一个单独的作业中,以提高流水线效率。
    • compile 作业运行 go build 并且应该重命名为 build
  2. 定义优化策略以实现更好的流水线效率。
    • stage 作业属性应该可配置,以允许不同的 CI/CD 管道使用者。
    • image 键使用硬编码的 latest 标签镜像。image 键使用硬编码的镜像标签 latest。添加 golang_version 作为输入,并将 latest 作为默认值,以实现更灵活和可重用的流水线。该输入必须与 Docker Hub 镜像标签值匹配。
    • compile 作业将二进制文件构建到硬编码的目标目录 mybinaries 中,可以使用动态 输入 和默认值 mybinaries 进行增强。
  3. 为新组件创建模板 目录结构,每个作业一个模板。
    • 模板的名称应该遵循 go 命令,例如 format.ymlbuild.ymltest.yml
    • 创建一个新项目,初始化一个 Git 仓库,添加/提交所有更改,设置远程 origin 并推送。修改 CI/CD 组件项目的路径 URL。
    • 创建额外的文件,如 编写组件 所述:README.mdLICENSE.md.gitlab-ci.yml.gitignore。以下 shell 命令初始化 Go 组件结构:
    git init
    
    mkdir templates
    touch templates/{format,build,test}.yml
    
    touch README.md LICENSE.md .gitlab-ci.yml .gitignore
    
    git add -A
    git commit -avm "Initial component structure"
    
    git remote add origin https://gitlab.example.com/components/golang.git
    
    git push
    
  4. 创建 CI/CD 作业作为模版。从 build 作业开始。
    • spec 部分定义以下输入:stagegolang_versionbinary_directory
    • 添加动态作业名称定义,访问 inputs.golang_version
    • 使用类似的模式为动态 Go 镜像版本,访问 inputs.golang_version
    • 将阶段分配给 inputs.stage 值。
    • inputs.binary_directory 创建二进制目录,并将其作为参数添加到 go build
    • 将 artifacts 路径定义为 inputs.binary_directory

      spec:
        inputs:
          stage:
            default: 'build'
            description: 'Defines the build stage'
          golang_version:
            default: 'latest'
            description: 'Go image version tag'
          binary_directory:
            default: 'mybinaries'
            description: 'Output directory for created binary artifacts'
      ---
      
      "build-$[[ inputs.golang_version ]]":
        image: golang:$[[ inputs.golang_version ]]
        stage: $[[ inputs.stage ]]
        script:
          - mkdir -p $[[ inputs.binary_directory ]]
          - go build -o $[[ inputs.binary_directory ]] ./...
        artifacts:
          paths:
            - $[[ inputs.binary_directory ]]
      
    • format 作业模板遵循相同的模式,但只需要 stagegolang_version 输入。

      spec:
        inputs:
          stage:
            default: 'format'
            description: 'Defines the format stage'
          golang_version:
            default: 'latest'
            description: 'Golang image version tag'
      ---
      
      "format-$[[ inputs.golang_version ]]":
        image: golang:$[[ inputs.golang_version ]]
        stage: $[[ inputs.stage ]]
        script:
          - go fmt $(go list ./... | grep -v /vendor/)
          - go vet $(go list ./... | grep -v /vendor/)
      
    • test 作业模板遵循相同的模式,但只需要 stagegolang_version 输入。

      spec:
        inputs:
          stage:
            default: 'test'
            description: 'Defines the format stage'
          golang_version:
            default: 'latest'
            description: 'Golang image version tag'
      ---
      
      "test-$[[ inputs.golang_version ]]":
        image: golang:$[[ inputs.golang_version ]]
        stage: $[[ inputs.stage ]]
        script:
          - go test -race $(go list ./... | grep -v /vendor/)
      
  5. 为了测试组件,修改 .gitlab-ci.yml 配置文件,并添加 tests
    • build 作业指定不同的 golang_version 输入。
    • 修改 CI/CD 组件路径的 URL。

      stages: [format, build, test]
      
      include:
        - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/format@$CI_COMMIT_SHA
        - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA
        - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA
          inputs:
            golang_version: "1.21"
        - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/test@$CI_COMMIT_SHA
          inputs:
            golang_version: latest
      
  6. 添加 Go 源码来测试 CI/CD 组件。go 命令需要一个 Go 项目,其中包含根目录中的 go.modmain.go

    • 初始化 Go 模块。修改 URL 以匹配您的 CI/CD 组件路径。

      go mod init example.gitlab.com/components/golang
      
    • 创建 main.go 文件,包含一个 main 函数,打印 Hello, CI/CD component

      // Specify the package, import required packages
      // Create a main function
      // Inside the main function, print "Hello, CI/CD Component"
      
      package main
      
      import "fmt"
      
      func main() {
        fmt.Println("Hello, CI/CD Component")
      }
      
    • 目录树如下所示:

      tree
      .
      ├── LICENSE.md
      ├── README.md
      ├── go.mod
      ├── main.go
      └── templates
          ├── build.yml
          ├── format.yml
          └── test.yml
      

遵循在将 CI/CD 模板转换为组件部分中剩余的步骤,以完成迁移:

  1. 提交并推送变更,并验证 CI/CD 流水线结果。
  2. 遵循编写组件的指导,更新 README.mdLICENSE.md 文件。
  3. 发布组件并验证它在 CI/CD 目录中。
  4. 将 CI/CD 组件添加到您的 staging/production 环境中。