LabHub
学习 学习路径 课程

GitLab CI/CD

用配置造出流水线的执行模型

在 LabHub 中继续学习

目标

通过八个步骤逐步构建一份 .gitlab-ci.yml,亲手实现 GitLab CI 的执行模型:阶段顺序、needs 构成的 DAG、rules 决定是否创建作业,以及 artifactscache 的区别。最后自行编写计算该模型的解析器。

为什么重要

此 Pod 中没有 GitLab 服务器,也没有 GitLab Runner,因此本实验不会假装流水线真的在运行。它只涵盖没有 Runner 也能如实学习的内容:配置语言及其执行模型。实际工作中,流水线受阻通常不是因为不知道命令,而是无法解释为何某个作业未创建、为何另一个作业仍在等待;答案都来自该文件的结构。评分也不会肉眼扫描文件,而会用 YAML 解析器读取并检查结构,因为缩进错一格就可能让作业挂到错误键下,人眼看来正常,实际却无法运行。

步骤

  1. 创建 /root/glci/.gitlab-ci.yml。顶层 stages 按顺序包含 buildtestdeploy;创建 build-app 作业,设置 stage: build,并至少写一条 script
  2. 添加作业,填满三个阶段。lintunit-test 使用 stage: testdeploy-staging 使用 stage: deploy。所有作业都必须有 script,所有 stage 值都必须位于 stages 列表中。
  3. 创建隐藏作业 .python-base,加入 imagebefore_script,让 build-applintunit-test 通过 extends: .python-base 继承。继承后的作业不要重复写 image。在顶层 default 中设置默认 image
  4. 添加 needs 构建 DAG。lint 使用 needs: []unit-testneeds 包含 build-appdeploy-stagingneeds 包含 unit-testbuild-app 不设置 needs
  5. deploy-staging 添加 rules:当 $CI_COMMIT_BRANCH == "main" 时使用 when: on_success,最后用无条件 when: never 收尾。新增 deploy-prod 作业,使用 stage: deploy,在 needs 中加入 deploy-staging;相同分支条件下设置 when: manualallow_failure: false,同样以无条件 when: never 收尾。不要使用 onlyexcept
  6. build-app 添加 artifacts,写明 pathsexpire_in。将 unit-testneeds 改为长格式,设置 job: build-appartifacts: true;将 deploy-stagingneeds 设置为 artifacts: false
  7. 创建有内容的 /root/glci/requirements.txt。为 build-appunit-test 添加 cache,其 key 使用 files 指向 requirements.txtbuild-app 使用 policy: pull-pushunit-test 使用 policy: pullcache.paths 不得与 artifacts.paths 重叠。
  8. 创建 /root/glci/plan.py。读取参数指定的配置文件,将 {"stages": [...], "waves": [[...], ...]} 作为 JSON 输出到标准输出。存在 needs 时只等待该列表,否则等待前一阶段的全部作业(默认 stagetest)。以点开头的键和保留键(stagesvariablesdefaultincludeworkflow)不是作业。每个 wave 按名称排序。如果没有任何作业可启动,输出 cycle 并以非 0 退出码结束。最后将针对自身配置运行的输出保存到 /root/glci/plan.json

参考

阶段顺序与第一个作业

创建 /root/glci/.gitlab-ci.yml。顶层 stages 按顺序包含 buildtestdeploy;创建 build-app 作业,设置 stage: build,并至少写一条 script

创建 /root/glci/.gitlab-ci.yml,在顶层放置 stages 列表,其顺序就是默认执行顺序。在下方添加顶层键 build-app,将 stagescript 缩进到其中。评分使用 YAML 解析器而非 grep,因此缩进错一格就会使作业成为其他键的子项并失败。

填满三个阶段

添加作业,填满三个阶段。lintunit-test 使用 stage: testdeploy-staging 使用 stage: deploy。所有作业都必须有 script,所有 stage 值都必须位于 stages 列表中。

lintunit-test 加入 test 阶段,把 deploy-staging 加入 deploy 阶段。同一阶段的两个作业会并行运行。若使用了 stages 列表中不存在的名称作为 stage,GitLab 会拒绝整个配置,因此请检查拼写。每个作业都必须有 script

用隐藏作业和 extends 消除重复

创建隐藏作业 .python-base,加入 imagebefore_script,让 build-applintunit-test 通过 extends: .python-base 继承。继承后的作业不要重复写 image。在顶层 default 中设置默认 image

名称以点开头的键是不会执行的配置片段。在 .python-base 中加入 imagebefore_script,让 build-applintunit-test 通过 extends 继承。继承后不要在作业中重复写 image。为不使用该片段的作业,在顶层 default 中设置默认镜像。

用 needs 跨越阶段壁垒

添加 needs 构建 DAG。lint 使用 needs: []unit-testneeds 包含 build-appdeploy-stagingneeds 包含 unit-testbuild-app 不设置 needs

unit-test 通过 needs 只等待 build-app,让 deploy-staging 只等待 unit-test。为 lint 设置 needs: []——没有该键和空列表含义相反,只有空列表才能不等待任何前置阶段、立即启动。build-app 不设置 needs

用 rules 设置分支条件与手动批准

deploy-staging 添加 rules:当 $CI_COMMIT_BRANCH == "main" 时使用 when: on_success,最后用无条件 when: never 收尾。新增 deploy-prod 作业,使用 stage: deploy,在 needs 中加入 deploy-staging;相同分支条件下设置 when: manualallow_failure: false,同样以无条件 when: never 收尾。不要使用 onlyexcept

deploy-staging$CI_COMMIT_BRANCH == "main" 时使用 when: on_successdeploy-prod 在相同条件下使用 when: manual。两个作业最后都必须是无条件 when: never,因为 rules 只采用第一条匹配项;无条件项放在中间会使其后规则全部失效。不要使用 only/exceptdeploy-prod 属于 deploy 阶段并等待 deploy-staging

定义 artifacts 及其接收者

build-app 添加 artifacts,写明 pathsexpire_in。将 unit-testneeds 改为长格式,设置 job: build-appartifacts: true;将 deploy-stagingneeds 设置为 artifacts: false

build-appartifacts 中用 paths 指定要传递的目录,并设置 expire_in。随后把 needs 改成长格式(- job: 이름 / artifacts: true|false):真正使用产物的 unit-test 设为 true,仅等待顺序的 deploy-staging 设为 false。让无关作业下载产物会悄悄拖慢流水线。

用锁文件哈希生成 cache 键

创建有内容的 /root/glci/requirements.txt。为 build-appunit-test 添加 cache,其 key 使用 files 指向 requirements.txtbuild-app 使用 policy: pull-pushunit-test 使用 policy: pullcache.paths 不得与 artifacts.paths 重叠。

先创建 /root/glci/requirements.txt,缓存键必须有真实文件可供哈希。然后为 build-appunit-test 添加 cachekey 不用固定字符串,而用 files 形式指向该文件。创建缓存的 build-app 使用 policy: pull-push,只读取的 unit-test 使用 policy: pullcache.paths 不得与 artifacts.paths 重叠。

用流水线解析器计算执行顺序

创建 /root/glci/plan.py。读取参数指定的配置文件,将 {"stages": [...], "waves": [[...], ...]} 作为 JSON 输出到标准输出。存在 needs 时只等待该列表,否则等待前一阶段的全部作业(默认 stagetest)。以点开头的键和保留键(stagesvariablesdefaultincludeworkflow)不是作业。每个 wave 按名称排序。如果没有任何作业可启动,输出 cycle 并以非 0 退出码结束。最后将针对自身配置运行的输出保存到 /root/glci/plan.json

/root/glci/plan.py <설정파일> 调用时,标准输出必须为 {"stages": [...], "waves": [[...], ...]}。wave 规则有三项:存在 needs 时只等待该列表;否则等待前一阶段所有作业(默认 stage 为 test);以点开头的键和保留键不是作业。每个 wave 按名称排序。若陷入无人可启动状态,则输出 cycle 并以非 0 退出码结束。诊断信息写入标准错误,才能让标准输出保持 JSON。最后将针对自身配置运行的结果保存为 /root/glci/plan.json