GitLab CI/CD 教程:逐步构建复杂流水线

适用层级:Free、Premium、Ultimate。适用部署:GitLab.com、GitLab Self-Managed、GitLab Dedicated。

本教程通过小步迭代,逐渐配置一条更复杂的 CI/CD 流水线。每一步的流水线都可完整运行,只是在前一步基础上增加能力。最终目标是构建、测试并部署一个文档网站。

完成后,你会拥有一个新的 GitLab.com 项目,以及一个基于 Docusaurus 的可运行文档站点。

教程包括以下步骤:

  1. 创建存放 Docusaurus 文件的项目。
  2. 创建初始流水线配置文件。
  3. 添加构建站点的作业。
  4. 添加部署站点的作业。
  5. 添加测试作业。
  6. 开始使用合并请求流水线。
  7. 减少重复配置。

前提条件

  • 拥有 GitLab.com 账户。
  • 熟悉 Git。
  • 本机已经安装 Node.js。例如在 macOS 上,可运行 brew install node 安装。

创建存放 Docusaurus 文件的项目

添加流水线配置前,先在 GitLab.com 上准备 Docusaurus 项目:

  1. 在自己的用户名下创建新项目,而不是在群组中创建。点击右上角“新建”(Create new,加号图标),选择“新建项目/仓库”(New project/repository)。
  2. 选择“创建空白项目”(Create blank project)。
  3. 填写项目详情。在 Project name 中输入名称,例如 My Pipeline Tutorial Project;勾选“使用 README 初始化仓库”(Initialize repository with a README);点击“创建项目”(Create project)。
  4. 在项目概览页面右上角点击“代码”(Code),复制 SSH 或 HTTP 克隆地址,并将项目克隆到本地。例如,下面使用 SSH 克隆到本机的 pipeline-tutorial 目录:
git clone git@gitlab.com:my-username/my-pipeline-tutorial-project.git pipeline-tutorial

进入项目目录,生成一个新的 Docusaurus 网站:

cd pipeline-tutorial
npm init docusaurus

Docusaurus 初始化向导会询问站点配置,此处全部使用默认选项。

向导将网站放在 website/ 中,但本教程需要网站位于项目根目录。把文件上移一级,然后删除旧目录:

mv website/* .
rm -r website

在 docusaurus.config.js 中填写 GitLab 项目信息:

  • 将 url: 设为 https://<my-username>.gitlab.io/ 形式。
  • 将 baseUrl: 设为项目名称对应的路径,例如 /my-pipeline-tutorial-project/。

提交修改并推送到 GitLab:

git add .
git commit -m "Add simple generated Docusaurus site"
git push origin

创建初始 CI/CD 配置文件

先使用尽可能简单的流水线配置,确认项目启用了 CI/CD,且存在可运行作业的 runner。

本步骤引入两个概念:

  • 作业(Jobs):流水线中相互独立、负责执行命令的单元。作业运行在 runner 上,runner 与 GitLab 实例分离。
  • script:定义作业要执行的命令。以数组形式提供多个命令时,会按顺序执行,每条命令都像在命令行中执行一样。默认情况下,如果某条命令失败或返回错误,作业会标记为失败,剩余命令不再运行。

在项目根目录创建 .gitlab-ci.yml,内容如下:

test-job:
  script:
    - echo "This is my first job!"
    - date

提交并推送到 GitLab,然后:

  1. 进入“构建 → 流水线”(Build → Pipelines),确认只有这一个作业的流水线开始运行。
  2. 打开流水线,再打开作业日志,应该能看到 This is my first job!,随后是日期。

项目已有 .gitlab-ci.yml 后,后续流水线配置修改都可以通过流水线编辑器进行。

添加构建站点的作业

CI/CD 的常见工作是先构建代码,再部署。首先添加用于构建网站的作业。

本步骤引入:

  • image:告诉 runner 用哪个 Docker 镜像运行作业。runner 会下载并启动容器,把 GitLab 项目克隆进容器,然后依次执行 script 命令。
  • artifacts:作业彼此独立,不共享资源。如果希望在一个作业中生成的文件被另一个作业使用,必须先将其保存为构件,后续作业才能下载这些文件。

把 test-job 替换为 build-job:

  1. 通过 image 使用最新的 node 镜像。Docusaurus 是 Node.js 项目,该镜像已经包含所需的 npm 命令。
  2. 运行 npm install,在正在运行的 node 容器中安装 Docusaurus 依赖;随后运行 npm run build 构建站点。
  3. Docusaurus 将输出保存到 build/,将此目录保存为 artifacts。
build-job:
  image: node
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - "build/"

使用流水线编辑器把配置提交到默认分支,然后查看作业日志。你可以看到 npm 命令执行及网站构建过程,确认结束时构件已保存,并在作业完成后点击日志右侧的“浏览”(Browse),查看构件文件内容。

添加部署站点的作业

确认 build-job 能构建 Docusaurus 站点后,再添加部署作业。

本步骤引入:

  • stage 和 stages:常见流水线配置会将作业划分到不同阶段。同一阶段的作业可以并行运行,后面的阶段必须等前面的阶段完成。如果一个作业失败,整个阶段会被视为失败,后续阶段不再开始。
  • GitLab Pages:用于托管静态网站。

添加一个获取构建结果并部署的作业。原文示例使用名为 pages 的 GitLab Pages 作业。build-job 的构件会自动被下载并解压到该作业中。由于 Pages 从 public/ 读取网站,脚本需要将构建结果移动到此目录。

添加 stages 并为作业指定阶段:build-job 先在 build 阶段运行,pages 随后在 deploy 阶段运行。

stages:          # List of stages for jobs and their order of execution
  - build
  - deploy

build-job:
  stage: build   # Set this job to run in the `build` stage
  image: node
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - "build/"

pages:
  stage: deploy  # Set this new job to run in the `deploy` stage
  script:
    - mv build/ public/
  artifacts:
    paths:
      - "public/"

通过流水线编辑器将配置提交到默认分支,再从流水线列表打开详情,确认:

  • 两个作业分别在 build 和 deploy 阶段运行。
  • pages 完成后出现 pages:deploy 作业,这是 GitLab 部署 Pages 站点的内部过程。该作业完成后,就可以访问新的 Docusaurus 网站。

访问网站:

  1. 在左侧边栏进入“部署 → Pages”(Deploy → Pages)。
  2. 确保“使用唯一域名”(Use unique domain)已关闭。
  3. 在“访问 Pages”(Access pages)下点击链接。地址形式应类似 https://<my-username>.gitlab.io/<project-name>。更多说明见 GitLab Pages 默认域名文档。

如果必须使用唯一域名,则在 docusaurus.config.js 中把 baseUrl: 设为 /。

添加测试作业

网站构建和部署正常后,可以增加测试与 lint 检查。例如 Ruby 项目可能运行 RSpec;Docusaurus 主要使用 Markdown 和生成的 HTML,因此本教程检查这两类文件。

本步骤引入:

  • allow_failure:间歇性失败或预期会失败的作业,可能降低效率或难以排查。设置该项可以允许作业失败,同时不停止整个流水线。
  • dependencies:列出要从哪些作业下载构件,从而控制每个作业的构件下载行为。

按以下方式扩展流水线:

  1. 在 build 和 deploy 之间添加 test 阶段。如果配置未定义 stages,这三个阶段就是默认阶段。
  2. 添加 lint-markdown,运行 markdownlint 检查 Markdown 格式。Docusaurus 生成的示例 Markdown 位于 blog/ 和 docs/。该工具只扫描原始 Markdown,不需要 build-job 生成的 HTML,因此使用 dependencies: [] 避免下载构件,加快作业。部分示例文件违反默认规则,所以先设置 allow_failure: true,让流水线仍能继续。
  3. 添加 test-html,运行 HTMLHint 检查生成的 HTML 是否存在已知问题。
  4. test-html 和 pages 都需要 build-job 的 HTML 构件。默认会下载所有前置阶段作业的构件,但这里显式设置 dependencies,避免后续修改流水线时意外下载其他构件。
stages:
  - build
  - test               # Add a `test` stage for the test jobs
  - deploy

build-job:
  stage: build
  image: node
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - "build/"

lint-markdown:
  stage: test
  image: node
  dependencies: []     # Don't fetch any artifacts
  script:
    - npm install markdownlint-cli2 --global           # Install markdownlint into the container
    - markdownlint-cli2 -v                             # Verify the version, useful for troubleshooting
    - markdownlint-cli2 "blog/**/*.md" "docs/**/*.md"  # Lint all markdown files in blog/ and docs/
  allow_failure: true  # This job fails right now, but don't let it stop the pipeline.

test-html:
  stage: test
  image: node
  dependencies:
    - build-job        # Only fetch artifacts from `build-job`
  script:
    - npm install --save-dev htmlhint                  # Install HTMLHint into the container
    - npx htmlhint --version                           # Verify the version, useful for troubleshooting
    - npx htmlhint build/                              # Lint all markdown files in blog/ and docs/

pages:
  stage: deploy
  dependencies:
    - build-job        # Only fetch artifacts from `build-job`
  script:
    - mv build/ public/
  artifacts:
    paths:
      - "public/"

把配置提交到默认分支并查看流水线详情。lint-markdown 会因示例 Markdown 违反默认规则而失败,但这是被允许的。对此可以:

  • 暂时忽略,这些格式问题不要求在教程中修复。
  • 修复 Markdown 格式,再将 allow_failure 改为 false,或者直接删掉,因为未定义时默认就是 false。
  • 添加 markdownlint 配置文件,限制需要报告哪些规则。

也可以修改 Markdown 内容,并在下一次部署后查看网站变化。

开始使用合并请求流水线

此前配置会在每次流水线成功后部署网站,这并不是理想的开发流程。更合适的方式是使用功能分支和合并请求,只在变更合并到默认分支时部署。

本步骤引入:

  • rules:为每个作业规定在哪些流水线中运行,例如合并请求、计划任务或其他情形。规则自上而下匹配,匹配后就将作业加入流水线。
  • CI/CD 变量:可在配置文件和脚本命令中使用环境变量控制行为。预定义变量由流水线自动注入,无需手动创建。变量通常写为 $VARIABLE_NAME,预定义变量通常以 $CI_ 开头。

新建一个功能分支,在该分支而不是默认分支上修改配置。为各个作业添加规则:网站仅针对默认分支的变更部署;其他作业则在合并请求或默认分支有变更时运行。

这样,在功能分支上工作时可以不运行作业,节省资源。准备验证变更时创建合并请求,即会启动相应作业。合并请求被接受并合并到默认分支后,再启动包含 pages 的新流水线;只要没有阻止执行的失败,就会部署站点。

stages:
  - build
  - test
  - deploy

build-job:
  stage: build
  image: node
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - "build/"
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'  # Run for all changes to a merge request's source branch
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH       # Run for all changes to the default branch

lint-markdown:
  stage: test
  image: node
  dependencies: []
  script:
    - npm install markdownlint-cli2 --global
    - markdownlint-cli2 -v
    - markdownlint-cli2 "blog/**/*.md" "docs/**/*.md"
  allow_failure: true
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'  # Run for all changes to a merge request's source branch
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH       # Run for all changes to the default branch

test-html:
  stage: test
  image: node
  dependencies:
    - build-job
  script:
    - npm install --save-dev htmlhint
    - npx htmlhint --version
    - npx htmlhint build/
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'  # Run for all changes to a merge request's source branch
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH       # Run for all changes to the default branch

pages:
  stage: deploy
  dependencies:
    - build-job
  script:
    - mv build/ public/
  artifacts:
    paths:
      - "public/"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH      # Run for all changes to the default branch only

合并该合并请求,以更新默认分支,然后确认新流水线包含部署网站的 pages 作业。

之后修改流水线配置时,都应使用功能分支和合并请求。创建 Git 标签、添加定时流水线等其他项目操作,不会触发这里的流水线,除非再为这些情形添加规则。

减少重复配置

目前有三个作业使用完全相同的 rules 和 image 配置。可以通过 extends 和 default 集中定义,避免重复。

本步骤引入:

  • 隐藏作业:名称以 . 开头的作业不会加入流水线,可用来保存希望复用的配置。
  • extends:在多个位置复用配置,通常复用隐藏作业。隐藏作业更新后,所有继承它的作业都会使用新配置。
  • default:为未显式定义相应关键字的作业设置默认值。
  • YAML 覆盖:复用配置时,可以在具体作业中显式设置关键字,覆盖 extends 或 default 的值。

具体调整如下:

  1. 添加隐藏作业 .standard-rules,存放 build-job、lint-markdown 和 test-html 重复使用的规则。
  2. 在这三个作业中通过 extends 继承 .standard-rules。
  3. 添加 default,将默认 image 设为 node。
  4. pages 不需要默认 node 镜像,因此显式使用体积很小、启动快速的 busybox。
stages:
  - build
  - test
  - deploy

default:               # Add a default section to define the `image` keyword's default value
  image: node

.standard-rules:       # Make a hidden job to hold the common rules
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

build-job:
  extends:
    - .standard-rules  # Reuse the configuration in `.standard-rules` here
  stage: build
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - "build/"

lint-markdown:
  stage: test
  extends:
    - .standard-rules  # Reuse the configuration in `.standard-rules` here
  dependencies: []
  script:
    - npm install markdownlint-cli2 --global
    - markdownlint-cli2 -v
    - markdownlint-cli2 "blog/**/*.md" "docs/**/*.md"
  allow_failure: true

test-html:
  stage: test
  extends:
    - .standard-rules  # Reuse the configuration in `.standard-rules` here
  dependencies:
    - build-job
  script:
    - npm install --save-dev htmlhint
    - npx htmlhint --version
    - npx htmlhint build/

pages:
  stage: deploy
  image: busybox       # Override the default `image` value with `busybox`
  dependencies:
    - build-job
  script:
    - mv build/ public/
  artifacts:
    paths:
      - "public/"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

通过合并请求将配置提交到默认分支。文件变得更简洁,但行为应与前一步相同。

至此,你已经建立了一条完整流水线,并对其配置做了精简。接下来可阅读 CI/CD YAML 语法参考,了解 .gitlab-ci.yml 的其他关键字,构建自己的流水线。


原文:Tutorial: Create a complex pipeline。本文为该文档的中文译文,示例代码保留原文。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容