流水线阶段划分:源码获取、构建、测试、部署
做过CI流水线的人都知道,阶段划分是设计的"骨架"。骨架歪了,后面再怎么填肉都是白搭。我个人习惯把流水线切成四个标准阶段——源码获取、构建、测试、部署。为什么是这四个?说白了,每个阶段对应一个核心问题:代码从哪来、怎么变成制品、质量过不过关、怎么跑起来。
你想想看,如果阶段职责不清,就会出现"构建阶段偷偷改代码"或者"测试阶段才去装依赖"这种尴尬事。我在项目中遇到过好几次,因为阶段边界模糊,导致流水线一跑就是两个小时,最后还经常挂。后来我狠下心来把阶段拆干净,问题少了一大半。
阶段划分的底层逻辑
每个阶段必须有明确的输入、动作、产出。输入是上一阶段给你的东西,动作是你这个阶段要干的事,产出是交给下一阶段的成果。三个要素缺一个,这个阶段就不该存在。
我总结了一个对照表,帮你快速理解每个阶段的定位:
| 阶段 | 核心职责 | 典型任务数 | 关键产出物 | 粒度控制要点 |
|---|---|---|---|---|
| 源码获取 | 确保代码正确拉取与校验 | 3-5 | 源代码目录、检查报告 | 避免一次拉取过多分支,增量检出 |
| 构建 | 将源码转为可交付制品 | 3-5 | 构建产物、Docker 镜像 | 合理利用缓存,避免重复编译 |
| 测试 | 验证质量与功能正确性 | 4-6 | 测试报告、覆盖率数据 | 按金字塔模型分层并行 |
| 部署 | 将制品发布到目标环境 | 3-5 | 部署记录、监控告警 | 灰度策略与回滚能力必须内置 |
下面这张图展示了四个阶段的完整脉络,建议你先看一遍图,再往下读细节:
源码获取阶段——别让第一块多米诺骨牌倒下
源码获取是整个流水线的起点。很多人觉得这步简单,不就是 git clone 吗?其实不然。我见过太多流水线在这步埋雷——要么拉取了不该拉的分支,要么依赖锁了一堆没用的版本。
这个阶段我习惯拆成四个任务:
- 代码检出与分支校验——只拉取本次构建需要的分支,用 refs 规范控制,别一把全拉下来。
- 代码静态检查——在构建之前先跑 ESLint、Checkstyle 这类工具,早发现问题早止损。
- 依赖版本锁定——用 lockfile 确保每次构建的依赖版本一致,别让"昨天还能跑今天挂了"的事发生。
- 增量变更分析——对比上一次构建,只处理有变化的模块。这能省下大量时间。
我曾经在一个大型 monorepo 项目里,没做增量分析,每次都是全量检出。结果流水线跑一次要40分钟,其中光代码检出就占了8分钟。后来加上增量策略,检出时间降到了30秒以内。
构建阶段——把原材料变成产品
构建阶段的输入是源码,输出是制品。说白了,就是"编译 + 打包 + 镜像"。这个阶段的粒度设计直接影响流水线的速度。
我常用的拆分方式:
- 依赖安装与缓存——npm install、mvn install 这类操作,必须配缓存。没缓存的构建就是在烧钱。
- 编译与代码生成——真正的编译过程,比如 TypeScript → JavaScript,或者 Java 源码 → class 文件。
- 制品打包与签名——生成 jar、war、zip 等交付物,加上签名确保完整性。
- 镜像构建与推送——如果是容器化部署,这里把制品打成 Docker 镜像并推送到仓库。
你想想看,如果把依赖安装和编译混在一起,缓存就没法用了。每次都得重新下载依赖,那速度能快才怪。我记得有一次帮一个团队优化流水线,就是把依赖安装单独拎出来并配了缓存,构建时间从15分钟降到了3分钟。
测试阶段——质量门禁不能形同虚设
测试阶段的核心是"分层 + 并行"。按照测试金字塔,从上到下是:端到端测试、集成测试、单元测试。每层的任务粒度和并行策略都不一样。
我习惯这样拆:
- 单元测试与覆盖率——单测必须快,跑完立刻出覆盖率报告。我一般要求单测在3分钟内跑完,超过5分钟就说明拆得不够细。
- 集成测试与契约——验证模块之间的接口是否对得上。用契约测试替代部分端到端测试,能省不少时间。
- 端到端冒烟测试——只覆盖核心链路,别想着全量跑。全量E2E测试放到夜间或预发布环境去做。
- 质量门禁卡点——覆盖率低于阈值?静态检查有严重问题?直接卡住,不让往下走。
这里有个坑。我曾经把E2E测试和单元测试放在同一个阶段里串行跑,结果每次都要等40分钟才知道单测过了没。后来我把单元测试和E2E拆成独立子任务,并行执行,整体时间缩短了60%。
部署阶段——把制品交到用户手里
部署阶段是流水线的最后一环,也是风险最高的一环。很多团队在这个阶段偷懒,搞个 scp 上去就完事了。但我告诉你,部署阶段的任务粒度设计直接决定了你的"回滚能力"。
标准的部署阶段应该包含:
- 环境初始化与检查——确认目标环境的资源、配置、依赖是否就绪。避免"部署到一半发现磁盘满了"的尴尬。
- 灰度发布与回滚——先推给一小部分实例,观察没问题再全量。同时准备好回滚脚本,一旦异常立马切回来。
- 健康检查与监控——部署完成后自动检查接口、日志、指标。别等用户报错你才发现。
- 自动化验证——用 smoke test 验证核心功能在目标环境上正常运转。
我个人习惯把"灰度发布"和"全量发布"拆成两个独立子任务,中间加一个人工审批卡点。这样做的好处是,灰度阶段发现了问题,只需要回滚灰度的那一小部分,影响面非常小。
阶段间的数据传递
四个阶段不是孤立的,它们之间需要传递数据。最常见的传递方式有两种:
- 制品传递——构建阶段产生的 jar、镜像等产物,通过制品仓库传给测试和部署阶段。
- 报告传递——源码获取阶段的检查报告、测试阶段的覆盖率报告,通过持久化存储共享。
这里有一个关键点:阶段之间不要直接传递文件。你想想看,如果构建阶段直接把产物放在本地磁盘上,测试阶段去读,那流水线就没法分布式跑了。正确的做法是把产物推送到中间仓库,下游阶段再去拉。
说到底,阶段划分没有银弹。但只要你抓住"职责边界清晰、任务粒度适中、数据传递规范"这三个要点,流水线就不会跑偏。我见过太多团队在阶段划分上栽跟头,其实就是没想清楚每个阶段到底该干什么。