核心概念:流水线、阶段、任务、触发器
聊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 流水线的结构。从触发器出发,经过多个阶段,每个阶段包含多个任务,最终走向交付。
小结几个关键原则
- 流水线是骨架,先设计再填充。
- 阶段按逻辑切割,常见为:准备 → 构建 → 测试 → 部署。
- 任务要原子化,一个任务只做一件事。
- 触发器要精确,别让不该跑的跑了。
在实际项目中,我习惯先画一张类似上面的图,然后对照着写配置。尤其是微服务架构,每个服务可能一条流水线,理解这些概念能避免很多混乱。至于具体如何用 YAML 或 JSON 表达,后面的章节会详细展开。