核心概念:流水线、阶段、任务、触发器

聊CI/CD,不管用什么工具,你都得先搞懂四个最基础的概念:流水线、阶段、任务、触发器。说白了,这就是整个自动化交付的骨架。我刚开始带团队的时候,很多人一上来就写流水线配置,结果阶段乱分、任务拆得太碎,一跑就崩。嗯,今天咱们就一个一个拆开讲清楚。

什么是流水线?

流水线,英文叫 Pipeline,你可以把它想象成一条「自动化生产线」。代码从提交开始,一路经过编译、测试、打包、部署……每一步自动化完成,最终产出可交付的产物。我个人习惯把流水线比作一个工厂的传送带,每个环节都有专门的机器在处理。

举个例子,一个最简单的流水线配置(YAML 格式,以 GitLab CI 为例):

stages:
  - build
  - test
  - deploy

这行代码定义了三个阶段,注意这里只定义了阶段名称,具体每个阶段干什么,还得靠「任务」来填充。别急,后面会讲。

核心点:流水线是一个容器,它定义了整个交付流程的「骨架」。没有流水线,你的自动化和手动操作没什么区别。

阶段(Stage)—— 流水线的关卡

阶段,是把流水线切分成逻辑上的「大步骤」。比如你肯定要有编译阶段、测试阶段、部署阶段。为什么要分阶段?因为阶段之间通常有先后顺序,而且一个阶段失败,后续阶段可以自动跳过。

我记得在一个电商项目中,测试阶段总是跑很久。后来我们把单元测试和集成测试拆成两个阶段,集成测试放后面。这样单元测试失败时,集成测试根本不会启动,整个流水线能快 40%。

阶段的关键属性:

  • 顺序执行:默认阶段按定义顺序串行,前一个成功才能执行下一个
  • 并行?:可以配置允许并行阶段(例如同时跑两种环境的构建),但工具不同语法不同
  • 失败策略:可以设置某个阶段失败后是否继续,但通常建议失败即停

避坑指南:我曾经把「准备依赖」和「编译」放在同一个阶段,结果依赖缓存没更新导致编译失败。后来把依赖准备单独拎成一个阶段,问题就解决了。阶段划分太粗,排查问题很痛苦。

任务(Job)—— 阶段里的最小单元

任务,也叫 Job 或 Step(不同工具有区别,但本质一样)。一个阶段可以包含一个或多个任务。任务才是真正干活的——跑脚本、编译代码、执行测试命令。

比如「测试阶段」里可以拆成两个任务:run_unit_tests 和 run_integration_tests。它们可以并行跑,也可以串行跑,取决于你配置。

看个 GitLab CI 的实际例子:

stages:
  - test

unit_tests:
  stage: test
  script:
    - npm test -- --coverage

integration_tests:
  stage: test
  script:
    - npm run test:integration

这里 test 阶段里有两个任务,默认并行执行。注意任务的名字(unit_tests、integration_tests)在工具里是唯一的标识。

任务的常见属性:

  • script:要执行的命令
  • artifacts:需要保留的文件(如构建产物、测试报告)
  • needs:任务间的依赖关系(可以跳过阶段顺序限制)
  • tags:指定运行器(Runner)

我的建议:任务粒度不宜太小也不宜太大。单任务如果把编译、测试、部署全写了,那和手动执行没区别;但如果拆分得太细(比如安装一个依赖就一个任务),流水线配置会变得冗长且难以维护。一般一个阶段 2-3 个任务比较合适。

触发器(Trigger)—— 谁启动流水线

流水线不是凭空跑的,得有个「扳机」扣动它。这个扳机就是触发器。最常用的当然是代码推送(push)和合并请求(merge request / pull request)。但还可以是定时触发、手动触发、或者上游流水线触发下游流水线。

你想想看,如果没有触发器,你每次都得手动点「运行」。那还叫什么自动化?

常见触发方式对比:

触发方式 场景 示例配置(GitLab CI)
代码推送 (push) 任何分支的提交 push: branches: [main]
合并请求 (merge_request) 提 MR 或更新 MR merge_requests: branches: [develop]
定时触发 (schedule) 夜间构建、定期清理 schedule: cron: '0 2 * * *'
流水线 API (manual) 手动确认、特殊发布 when: manual
上游触发 (pipeline) 微服务间链式调用 trigger: project: 'group/project'

这里要注意一个常见的坑:有些人把 when: manual 和 except/only 搞混。手动触发只是允许你在 Pipeline UI 上点一下,不代表它会自动跑。我曾经踩过一次:设置了手动触发,但忘了加 needs 依赖,结果上游任务跑完,下游任务等了三天还没人点……

避坑指南:触发器条件写得太宽泛,比如 branches: ['*'],结果每个人推了个 feature 分支都触发部署,把生产环境污染了。记住:触发器规则一定要结合分支策略设置白名单。特别是部署阶段的触发器,建议只针对 main 或 release/*。

它们的关系——一张图说清楚

下面这张图展示了一个典型 CI/CD 流水线的结构。从触发器出发,经过多个阶段,每个阶段包含多个任务,最终走向交付。

CI/CD 流水线核心概念关系图 触发器 (Trigger) 流水线 (Pipeline) 阶段 (Stage) 编译 阶段 (Stage) 测试 阶段 (Stage) 部署 任务:npm build 任务:单元测试 (并行) 任务:集成测试 (并行) 任务:部署到生产 说明: - 触发器启动整个流水线 - 流水线包含多个阶段,阶段按顺序执行 - 每个阶段包含一个或多个任务,任务可并行或串行

小结几个关键原则

  1. 流水线是骨架,先设计再填充。
  2. 阶段按逻辑切割,常见为:准备 → 构建 → 测试 → 部署。
  3. 任务要原子化,一个任务只做一件事。
  4. 触发器要精确,别让不该跑的跑了。

在实际项目中,我习惯先画一张类似上面的图,然后对照着写配置。尤其是微服务架构,每个服务可能一条流水线,理解这些概念能避免很多混乱。至于具体如何用 YAML 或 JSON 表达,后面的章节会详细展开。