GitLab 流水线效率

来自泡泡学习笔记
BrainBs讨论 | 贡献2023年9月16日 (六) 16:37的版本 (创建页面,内容为“CI/CD 流水线 是 GitLab CI/CD 的基本构建模块。 使流水线更高效可帮助您节省开发人员的时间,从而: * 加快您的 DevOps 流程 * 降低成本 * 缩短开发反馈循环 新团队或项目以缓慢且低效的流水线开始,并随着时间的推移通过反复试验来改进其配置,这是很常见的。更好的流程是使用流水线功能,立即提高效率,并更早地获得更快的软件开发生命周期。 <…”)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳到导航 跳到搜索

CI/CD 流水线 是 GitLab CI/CD 的基本构建模块。 使流水线更高效可帮助您节省开发人员的时间,从而:

  • 加快您的 DevOps 流程
  • 降低成本
  • 缩短开发反馈循环

新团队或项目以缓慢且低效的流水线开始,并随着时间的推移通过反复试验来改进其配置,这是很常见的。更好的流程是使用流水线功能,立即提高效率,并更早地获得更快的软件开发生命周期。


识别瓶颈和常见故障

检查低效流水线的最简单指标是作业、阶段的运行时间和流水线本身的总运行时间。流水线总持续时间受到以下因素的严重影响:

  • 仓库的大小
  • 阶段和作业的总数。
  • 作业之间的依赖关系。
  • “关键路径”,代表最小和最大流水线持续时间。


需要注意的其他点与 GitLab Runners 相关:

  • Runner 的可用性以及为他们提供的资源。
  • 构建依赖项及其安装时间。
  • 容器镜像大小。
  • 网络延迟和连接缓慢。


流水线频繁发生不必要的故障也会导致开发生命周期的放缓。您应该寻找作业失败的有问题的模式:

  • 片状单元测试随机失败,或产生不可靠的测试结果。
  • 与该行为相关的测试覆盖率下降和代码质量。
  • 可以安全地忽略但会停止流水线的故障。
  • 在长流水线结束时失败的测试,但可能在较早的阶段,导致延迟反馈。


流水线分析

分析流水线的性能以找到提高效率的方法。分析可以帮助识别 CI/CD 基础架构中可能的阻碍因素。这包括分析:

  • 工作量。
  • 执行时间的瓶颈。
  • 整体流水线架构。

理解和记录流水线工作流并讨论可能的操作和更改非常重要。重构流水线可能需要 DevSecOps 生命周期中的团队之间进行仔细的交互。


流水线分析可以帮助识别成本效率问题。例如,托管有付费云服务的 runners 可以导致:

  • 比 CI/CD 流水线所需的资源多,浪费金钱。
  • 没有足够的资源,导致运行缓慢和浪费时间。


流水线洞察

单元测试、集成测试、端到端测试、代码质量测试等测试确保 CI/CD 流水线自动发现问题,可能涉及许多流水线阶段,导致运行时间过长。

您可以通过在同一阶段并行运行测试不同事项的作业来改进运行时间,从而减少整体运行时间。缺点是您需要同时运行更多 runner 来支持并行作业。


有向无环图 (DAG) 可视化

有向无环图 (DAG) 可视化可以帮助分析流水线中的关键路径并了解可能的阻塞。


流水线配置

配置流水线时请谨慎选择,以加快流水线速度并减少资源使用。这包括利用 GitLab CI/CD 的内置功能,使流水线运行得更快、更高效。


减少作业运行的频率

尝试找出哪些作业不需要在所有情况下都运行,并使用流水线配置来阻止它们运行:

  • 当旧流水线被新流水线取代时,使用 interruptible 关键字停止旧流水线。
  • 使用 rules 跳过不需要的测试。例如,当仅更改前端代码时跳过后端测试。
  • 不那么频繁地运行非必要的计划流水线。


快速失败

确保在 CI/CD 流水线中尽早检测到错误。需要很长时间才能完成的作业可以防止流水线返回失败状态,直到作业完成。

设计流水线,以便可以快速失败的作业更早运行。例如,添加一个早期阶段并在其中做移动语法、样式检查、Git 提交消息验证和类似的工作。

确定在较快作业的快速反馈之前尽早运行长作业是否重要。最初的失败可能表明流水线的其余部分不应运行,从而节省了流水线资源。


有向无环图 (DAG)

在基本配置中,作业总是在运行之前等待早期阶段的所有其他作业完成。这是最简单的配置,但在大多数情况下也是最慢的。有向无环图和父/子流水线更灵活,效率更高,但也会使流水线更难理解和分析。


缓存

另一种优化方法是 cache 依赖项。如果您的依赖项很少更改,例如 NodeJS /node_modules,缓存可以使流水线执行速度更快。

即使作业失败,您也可以使用 cache:when 缓存下载的依赖项。


Docker 镜像

下载和初始化 Docker 镜像可能是整个作业运行时间的很大一部分。

如果 Docker 镜像减慢作业执行速度,请分析基本镜像大小和与注册表的网络连接。如果示例在云中运行,请查找供应商提供的云容器镜像库。除此之外,您可以使用 GitLab 容器镜像库,相比其它镜像库,GitLab 实例可以更快地访问。


优化 Docker 镜像

构建优化的 Docker 镜像,因为大型 Docker 镜像会占用大量空间,下载时间较长,连接速度较慢。如果可能,请避免对所有作业使用一个大镜像。使用多个较小的镜像,每个镜像用于特定任务,下载和运行速度更快。

尝试使用预装软件的自定义 Docker 镜像。下载更大的预配置镜像通常比每次使用通用镜像并在其上安装软件要快得多。Docker 的编写 Dockerfiles 的最佳实践 有更多关于构建高效 Docker 镜像的信息。


减小 Docker 镜像大小的方法:

  • 使用小型基础镜像,例如 debian-slim。
  • 如果不是严格需要,不要安装像 vim、curl 等便利工具。
  • 打造专属的开发镜像。
  • 禁用由软件包安装的手册页和文档以节省空间。
  • 减少 RUN 层并结合软件安装步骤。
  • 使用 multi-stage builds 将多个使用构建器模式的 Dockerfile 合并为一个 Dockerfile,可以减少镜像大小。
  • 如果使用 apt,添加 –no-install-recommends 以避免不必要的包。
  • 清理最后不再需要的缓存和文件。例如 rm -rf /var/lib/apt/lists/* 适用于 Debian 和 Ubuntu,或 yum clean all 适用于RHEL和CentOS。
  • 使用 dive 或 DockerSlim 等工具来分析和缩小镜像。

为了简化 Docker 镜像管理,您可以创建一个专门的组来管理 Docker 镜像 并使用 CI/CD 流水线测试、构建和发布它们。

测试、记录和学习

改进流水线是一个迭代过程。做一些小的改变,监控效果,然后再次迭代。许多小的改进加起来可以大大提高流水线效率。

您可以直接在 GitLab 仓库中使用 Mermaid charts in Markdown 执行此操作。它可以帮助记录流水线设计和架构。

记录 CI/CD 流水线问题和问题中的事件,包括所做的研究和找到的解决方案。这有助于新团队成员的入职,还有助于识别 CI 流水线效率方面的反复出现的问题。

GitLab下游流水线

下游流水线是由另一个流水线触发的任何极狐GitLab CI/CD 流水线。 下游流水线可以是:

  • 一个父子流水线,它是与第一个流水线在同一个项目中触发的下游流水线。
  • 多项目流水线,它是在与第一个流水线不同的项目中触发的下游流水线。

父子流水线和多项目流水线有时可用于类似目的,但存在一些关键差异。

父子流水线

父流水线是在同一项目中触发下游流水线的流水线。 下游流水线称为子流水线。


子流水线:

  • 在与父流水线相同的项目、ref 和提交 SHA 下运行。
  • 不直接影响流水线运行相关的 ref 的整体状态。例如, 如果主分支的流水线出现故障,通常会说“主分支已损坏”。 如果子流水线由 strategy:depend 触发,子流水线的状态只影响 ref 的状态。
  • 当为同一 ref 创建新流水线时,如果使用 interruptible 配置流水线,则自动取消。
  • 不显示在项目的流水线列表中。您只能在其父流水线的详情页面查看子流水线。


嵌套子流水线

父子流水线的最大深度为两级子流水线。

一个父流水线可以触发多个子流水线,这些子流水线又可以触发自己的子流水线。您不能触发另一级别的子流水线。


多项目流水线

您可以跨多个项目设置极狐GitLab CI/CD,以便一个项目中的流水线可以触发另一个项目中的流水线。您可以在一个地方可视化整个流水线,包括所有跨项目的相互依赖关系。


多项目流水线:

  • 从另一个流水线触发,但上游(触发)流水线对下游(被触发)流水线没有太多控制。但是,它可以选择下游流水线的 ref,并将 CI/CD 变量传递给它。
  • 影响它运行的项目的 ref 的整体状态,但不影响触发流水线的 ref 的状态,除非用 strategy:depend。
  • 如果在上游流水线中为相同的 ref 运行新流水线,则在使用 interruptible 时不会在下游项目中自动取消。如果为下游项目上的相同 ref 触发了新流水线,它们可以自动取消。
  • 在下游项目的流水线列表中可见。
  • 是独立的,因此没有嵌套限制。


如果您在下游私有项目中触发流水线,在上游项目的流水线页面,您可以查看:

  • 项目名称。
  • 流水线的状态。


从 .gitlab-ci.yml 文件中的作业触发下游流水线

在 .gitlab-ci.yml 文件中使用 trigger 关键字来创建触发下游流水线的作业。该作业称为触发作业。


父子流水线示例:

trigger_job:
  trigger:
    include:
    - local: path/to/child-pipeline.yml


多项目流水线示例:

trigger_job:
  trigger:
    project: project-group/my-downstream-project


触发作业启动后,当极狐GitLab 尝试创建下游流水线时,作业的初始状态为 pending。如果下游流水线创建成功,则触发器作业显示 passed,否则显示 failed。或者,您可以设置触发作业显示下游流水线的状态。


使用 rules 控制下游流水线

在下游流水线中使用 CI/CD 变量或 rules 关键字来控制作业行为。


当您使用 trigger 关键字触发下游流水线时,适用于所有作业的 $CI_PIPELINE_SOURCE 预定义变量的值是:

  • 多项目流水线:pipeline。
  • 父子流水线:parent_pipeline。


例如,要在同时运行合并请求流水线的项目中,控制多项目流水线中的作业:

job1:
  rules:
    - if: $CI_PIPELINE_SOURCE == "pipeline"
  script: echo "This job runs in multi-project pipelines only"

job2:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script: echo "This job runs in merge request pipelines only"

job3:
  rules:
    - if: $CI_PIPELINE_SOURCE == "pipeline"
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script: echo "This job runs in both multi-project and merge request pipelines"


在不同的项目中使用子流水线配置文件

您可以在触发器作业中,使用 include:project 来使用不同项目中的配置文件触发子流水线:

microservice_a:
  trigger:
    include:
    - project: 'my-group/my-pipeline-library'
        ref: 'main'
        file: '/path/to/child-pipeline.yml'


组合多个子流水线配置文件

定义子流水线时,您最多可以包含三个配置文件。子流水线的配置由合并在一起的所有配置文件组成:

microservice_a:
  trigger:
    include:
    - local: path/to/microservice_a.yml
    - template: Security/SAST.gitlab-ci.yml
    - project: 'my-group/my-pipeline-library'
        ref: 'main'
        file: '/path/to/child-pipeline.yml'


动态子流水线

您可以从作业中生成的 YAML 文件而不是项目中保存的静态文件触发子流水线。这种技术对于生成针对已更改内容的流水线或构建目标和体系结构矩阵非常强大。

包含生成的 YAML 文件的产物不得大于 5MB。

您可以对 Dhall 或 ytt 等其他模板语言使用类似的过程。


触发动态子流水线

从动态生成的配置文件触发子流水线:

  1. 在作业中生成配置文件,保存为产物:

     generate-config:
       stage: build
       script: generate-ci-config > generated-config.yml
       artifacts:
         paths:
         - generated-config.yml
  2. 将触发器作业配置为在生成配置文件的作业之后运行,并将 include: artifact 设置为生成的产物:

     child-pipeline:
       stage: test
       trigger:
         include:
         - artifact: generated-config.yml
             job: generate-config


在此示例中,极狐GitLab 检索 generated-config.yml 并使用该文件中的 CI/CD 配置触发子流水线。

产物路径由极狐GitLab 而非 runner 解析,因此该路径必须与运行极狐GitLab 的操作系统的语法相匹配。如果极狐GitLab 在 Linux 上运行但使用 Windows runner 进行测试,则触发作业的路径分隔符为 /。使用 Windows runner 的作业的其他 CI/CD 配置,如脚本,使用 。


使用合并请求流水线运行子流水线

当不使用 rules 或 workflow:rules 时,流水线(包括子流水线)默认作为分支流水线运行。


要将子流水线配置为在从合并请求(父)流水线触发时运行,请使用 rules 或 workflow:rules。例如,使用 rules:

  • 将父流水线的触发作业设置为在合并请求时运行:

      trigger-child-pipeline-job:
        trigger:
          include: path/to/child-pipeline-configuration.yml
        rules:
          - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  • 使用 rules 将子流水线作业配置为在父流水线触发时运行:

      job1:
        script: echo "This child pipeline job runs any time the parent pipeline triggers it."
        rules:
          - if: $CI_PIPELINE_SOURCE == "parent_pipeline"
    
      job2:
        script: echo "This child pipeline job runs only when the parent pipeline is a merge request pipeline"
        rules:
          - if: $CI_MERGE_REQUEST_ID


在子流水线中,$CI_PIPELINE_SOURCE 的值始终为 parent_pipeline,因此:

  • 您可以使用 if: $CI_PIPELINE_SOURCE == “parent_pipeline” 来确保子流水线作业始终运行。
  • 您不能使用 if: $CI_PIPELINE_SOURCE == “merge_request_event” 来配置子流水线作业来运行合并请求流水线。 相反,使用 if: $CI_MERGE_REQUEST_ID 将子流水线作业设置为仅在父流水线是合并请求流水线时运行。父流水线的 CI_MERGE_REQUEST_* 预定义变量被传递给子流水线作业。


为多项目流水线指定一个分支

您可以指定在触发多项目流水线时使用的分支。极狐GitLab 使用分支头部的提交来创建下游流水线。例如:

staging:
  stage: deploy
  trigger:
    project: my/deployment
    branch: stable-11-2


使用:

  • project 关键字指定下游项目的完整路径。在 15.3 及更高版本中,您可以使用变量扩展。
  • branch 关键字指定 project 指定的项目中的分支或标签的名称。您可以使用变量扩展。


使用 API 触发多项目流水线

您可以将 CI/CD 作业令牌(CI_JOB_TOKEN)与流水线触发器 API 端点,从 CI/CD 作业中触发多项目流水线。极狐GitLab 将使用作业令牌触发的流水线设置为包含进行 API 调用的作业的流水线的下游流水线。


例如:

trigger_pipeline:
  stage: deploy
  script:
    - curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=main "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
  rules:
    - if: $CI_COMMIT_TAG
  environment: production


从上游流水线获取产物

使用 needs:project 从上游流水线获取产物:

  • 在上游流水线中,使用 artifacts 关键字将产物保存在作业中,然后使用触发器作业触发下游流水线:

      build_artifacts:
        stage: build
        script:
          - echo "This is a test artifact!" >> artifact.txt
        artifacts:
          paths:
          - artifact.txt
    
      deploy:
        stage: deploy
        trigger: my/downstream_project
  • 在下游流水线的作业中使用 needs:project 来获取产物。

      test:
        stage: test
        script:
          - cat artifact.txt
        needs:
          - project: my/upstream_project
          job: build_artifacts
          ref: main
          artifacts: true
    • 设置:

      • job 为创建产物的上游流水线中的作业。
      • ref 为分支。
      • artifacts 为 true。


从上游合并请求流水线中获取产物

当您使用 needs:project 来将产物传递到下游流水线时,ref 值通常是分支名称,例如 main 或 development。

对于合并请求流水线,ref 值的格式为 refs/merge-requests/<id>/head,其中 id 是合并请求 ID。您可以使用 CI_MERGE_REQUEST_REF_PATH CI/CD 变量检索此 ref。不要将分支名称用作合并请求流水线的 ref,因为下游流水线会尝试从最新的分支流水线中获取产物。


要从上游 merge request 流水线而不是 branch 流水线获取产物,请使用变量继承将 CI_MERGE_REQUEST_REF_PATH 传递给下游流水线:

  • 在上游流水线的作业中,使用 artifacts 关键字保存产物。

  • 在触发下游流水线的作业中,传递 $CI_MERGE_REQUEST_REF_PATH 变量:

      build_artifacts:
        stage: build
        script:
          - echo "This is a test artifact!" >> artifact.txt
        artifacts:
          paths:
          - artifact.txt
    
      upstream_job:
        variables:
          UPSTREAM_REF: $CI_MERGE_REQUEST_REF_PATH
        trigger:
          project: my/downstream_project
          branch: my-branch
  • 在下游流水线的作业中,使用 needs:project 和传递的变量作为 ref 从上游流水线获取产物:

      test:
        stage: test
        script:
          - cat artifact.txt
        needs:
          - project: my/upstream_project
          job: build_artifacts
          ref: UPSTREAM_REF
          artifacts: true

您可以使用此方法从上游合并请求流水线中获取产物,但不能从合并结果流水线中获取产物。


将 CI/CD 变量传递到下游流水线

您可以根据创建或定义变量的位置,使用几种不同的方法将 CI/CD 变量传递到下游流水线。


传递 YAML 定义的 CI/CD 变量

您可以使用 variables 关键字将 CI/CD 变量传递到下游流水线,就像处理任何其他作业一样。


父子流水线示例:

variables:
  VERSION: "1.0.0"

staging:
  variables:
    ENVIRONMENT: staging
  stage: deploy
  trigger:
    include:
    - local: path/to/child-pipeline.yml


多项目流水线示例:

variables:
  VERSION: "1.0.0"

staging:
  variables:
    ENVIRONMENT: staging
  stage: deploy
  trigger: my-group/my-deployment-project


ENVIRONMENT 变量被传递给下游流水线中定义的每个作业。当极狐GitLab Runner 选择作业时,它可以作为变量使用。

在以下配置中,MY_VARIABLE 变量被传递到在 trigger-downstream 作业排队时创建的下游流水线。这是因为 trigger-downstream 作业继承了全局变量块中声明的变量,然后我们将这些变量传递给下游流水线。


防止全局变量被传递

您可以使用 inherit:variables:false 阻止全局 CI/CD 变量到达下游流水线。


父子流水线示例:

variables:
  GLOBAL_VAR: value

trigger-job:
  inherit:
    variables: false
  variables:
    JOB_VAR: value
  trigger:
    include:
    - local: path/to/child-pipeline.yml


多项目流水线示例:

variables:
  GLOBAL_VAR: value

trigger-job:
  inherit:
    variables: false
  variables:
    JOB_VAR: value
  trigger: my-group/my-project


在此示例中,GLOBAL_VAR 变量在触发流水线中不可用,但 JOB_VAR 可用。


传递一个预定义的变量

使用预定义的 CI/CD 变量传递有关上游流水线的信息,使用插值。将预定义变量保存为触发作业中的新作业变量,传递给下游流水线。


父子流水线示例:

trigger-job:
  variables:
    PARENT_BRANCH: $CI_COMMIT_REF_NAME
  trigger:
    include:
    - local: path/to/child-pipeline.yml


多项目流水线示例:

trigger-job:
  variables:
    UPSTREAM_BRANCH: $CI_COMMIT_REF_NAME
  trigger: my-group/my-project


包含上游流水线的 $CI_COMMIT_REF_NAME 预定义 CI/CD 变量值的 UPSTREAM_BRANCH 变量在下游流水线中可用。

不要使用此方法将隐藏的变量传递到多项目流水线。CI/CD 隐藏配置不会传递到下游流水线,变量可能会在下游项目的作业日志中被取消隐藏。

您不能使用此方法将作业级持久变量转发到下游流水线,因为它们在触发器作业中不可用。

上游流水线优先于下游流水线。如果在上游和下游项目中定义了两个同名变量,则上游项目中定义的变量优先。