1. 课程导入与全景认知

在开始学习 GitLab CI/CD 之前,先建立一个整体印象:一条流水线并不是“把命令写进 YAML 然后跑起来”,而是一条从代码变更出发、经过构建与验证、最终产出可发布制品的受控通道。理解这条通道的组成要素和它们之间的依赖关系,比记住某个关键字更重要。

1.1 三个核心概念:Pipeline、Stage、Job

GitLab CI/CD 的最小执行单元是 Job(作业)。一个 Job 通常对应一条脚本或一组命令,例如“安装依赖并运行单元测试”。多个 Job 按逻辑分组,形成 Stage(阶段),例如 build、test、deploy。而一次完整的执行过程称为 Pipeline(流水线),它由若干 Stage 按顺序组成。

关键规则是:同一 Stage 内的 Job 默认并行执行,不同 Stage 之间串行执行;只有当前 Stage 的全部 Job 成功,才会进入下一个 Stage。这条规则决定了流水线的“快慢”和“卡点”位置——把耗时的测试放在靠前阶段会拖慢反馈,把部署放在测试之前则可能把未验证的版本推上环境。

此外还有两个贯穿全局的对象:Runner 是真正执行 Job 的代理进程,可以是 Shell、Docker 或 Kubernetes 执行器;Artifact(制品) 是 Job 产出的文件,例如编译后的二进制、测试报告、镜像地址清单,它让后续 Stage 能够拿到前面阶段的成果,而不必重复构建。

1.2 流水线的价值:把“人工检查”变成“可重复的门禁”

在没有流水线时,一次发布往往依赖开发者本地执行测试、手工打包、口头确认环境。这种方式的问题不是“慢”,而是不可重复:同一份代码在不同人手里可能得到不同结果。流水线的价值在于把验证条件固化为代码,使每次提交都经过同一套门禁。

具体来说,它带来三方面收益:第一,快速反馈——提交后几分钟内就能知道是否破坏了已有功能;第二,一致性——构建和测试在统一的 Runner 镜像中执行,减少“在我机器上是好的”这类问题;第三,可追溯——每个 Pipeline 都绑定具体的 commit、分支和制品,出问题时能定位到是哪次变更引入的。

1.3 自动化测试与发布的关系:验证在前,发布在后

自动化测试和发布不是两个独立环节,而是同一条流水线上的前后依赖。测试阶段回答的是“这个版本是否满足质量要求”,发布阶段回答的是“把满足要求的版本放到哪里、以什么方式对外提供”。因此,发布的前置条件必须是测试通过,而不是“测试和发布并行跑,谁先完成算谁”。

在 GitLab 中,这种依赖通过 Stage 顺序和 needs 关键字表达。默认情况下,deploy Stage 只有在 test Stage 全部成功后才会启动;如果使用 needs 做有向无环图(DAG)调度,也必须显式声明依赖的 Job,不能绕过验证直接部署。

1.4 一条完整流水线的组成要素

把上述概念串起来,一条典型的、面向 Web 服务的流水线包含以下要素:

  • 触发条件:由 rules 或 only/except 定义,例如“仅当 main 分支有推送,或创建了 Merge Request 时触发”。
  • 运行环境:由 image 指定容器镜像,例如 node:20-alpine;由 tags 指定 Runner 标签。
  • 缓存与依赖:cache 缓存 node_modules 等目录,artifacts 在 Stage 之间传递构建产物。
  • 验证作业:静态检查、单元测试、集成测试,通常产出 JUnit 报告供 GitLab 展示。
  • 构建作业:生成可部署制品,例如打包镜像并推送到镜像仓库。
  • 发布作业:将制品部署到目标环境,通常配合 environment 记录部署位置,并设置 when: manual 作为人工确认点。

下面这张图把上述要素组织成一条从代码提交到可发布版本的信号链,重点标出“验证门禁”和“制品传递”两条关键路径:

GitLab CI/CD 流水线全景:从提交到可发布版本 Stage 串行推进 · 同 Stage 内 Job 并行 · 制品沿箭头向下传递 触发条件 push / merge_request 运行环境 image + Runner tags 缓存与依赖 cache / artifacts 验证门禁 失败即阻断后续 Stage Stage 1 · build Job: install-deps Job: compile 产出:dist/ 目录 → artifacts Stage 2 · test Job: lint(并行) Job: unit-test(并行) 产出:junit.xml 测试报告 Stage 3 · package Job: docker-build Job: push-image 产出:镜像 tag → registry 门禁:test 全部成功 → 才允许进入 package Stage 4 · deploy Job: deploy-staging(manual) environment: staging 镜像制品 传递到部署 Stage 串行推进 示例参数:Node.js 20 / Docker 执行器 / 镜像仓库为 GitLab Container Registry

1.5 一个最小可运行的示例

为了把概念落到可执行层面,下面给出一个最小但完整的 .gitlab-ci.yml 示例。它包含两个 Stage:test 和 deploy,其中部署作业设置为手动触发,避免每次提交都直接发布。

stages:
  - test
  - deploy

default:
  image: node:20-alpine
  cache:
    key: "$CI_COMMIT_REF_SLUG"
    paths:
      - node_modules/

run-unit-test:
  stage: test
  script:
    - npm ci
    - npm run test -- --reporter=junit --outputFile=report.xml
  artifacts:
    when: always
    reports:
      junit: report.xml

deploy-staging:
  stage: deploy
  script:
    - echo "部署到 staging 环境,版本 $CI_COMMIT_SHORT_SHA"
  environment:
    name: staging
  when: manual
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

这段配置里,npm ci 保证依赖安装与 package-lock.json 严格一致;artifacts.reports.junit 让 GitLab 在 Merge Request 页面直接展示测试通过率;when: manual 把发布变成一个人工确认动作,而不是自动执行。运行后,可以在 GitLab 项目的 Build → Pipelines 页面看到每个 Stage 的状态,点击具体 Job 查看日志,点击 Artifacts 下载测试报告。

判断一条流水线是否“完整”,可以问三个问题:它是否在每次代码变更时自动触发?它是否在发布前执行了可重复的验证?它是否把验证结果和制品绑定到具体的 commit 上?三个答案都是“是”,这条流水线才具备基本的工程价值。

1.6 本课程的学习路径

后续章节会沿着这条全景图逐步展开:先掌握 .gitlab-ci.yml 的语法与 Job 编写,再深入 Runner 的选型与缓存策略,然后分别讲解单元测试、集成测试、代码质量检查在流水线中的落地方式,最后进入镜像构建、环境部署、回滚与发布策略。每一部分都会回到这张全景图,说明它处在哪个位置、影响哪条路径。

现在需要记住的是:流水线是一条有方向的依赖链,验证在前,发布在后,制品是连接两者的载体。带着这个结构去学习后续细节,会比孤立地记忆关键字更容易形成体系。