第二章:CI/CD基础与持续集成流程归纳总结
聊聊CI/CD吧。很多人一听到这个词就想到Jenkins、GitLab CI、GitHub Actions这些工具,但我觉得理解它背后的逻辑更重要。CI——持续集成,说白了就是让开发团队频繁地把代码合入主干,然后自动跑构建和测试。CD呢,可以是持续交付或持续部署,这取决于你愿不愿意一键把东西推上线。
我自己刚带团队那会儿,对CI的理解就卡在“自动编译”这个层面。以为上了流水线就算完成了。结果呢?代码合进去,编译是过了,但一跑单元测试就挂,一跑集成测试就炸。后来我意识到,真正的CI不是“能编译”,而是“能验证”。
2.1 从“手工作坊”到“自动流水线”
先回忆一下没有CI的日子。开发写完代码,手动上传到测试服务器,跑一遍测试,发现问题再改……这种模式有个名字——集成地狱(Integration Hell)。
| 传统集成 | 持续集成 |
|---|---|
| 代码提交间隔长(天/周) | 代码提交频繁(小时/天) |
| 手动构建、手动测试 | 自动构建、自动测试 |
| 发现问题晚,修复成本高 | 问题暴露快,修复成本低 |
| 发布周期长 | 随时可发布 |
嗯,这张表我当年在团队里贴过。有个开发兄弟跟我说:“我们每天就合一次代码,够频繁了吧?”我说:“那你们每天看一次集成结果,发现失败时,中间已经过了8个commit了。查错累不累?”他沉默了。实际落地时,我建议至少每2~3小时合入一次,配合自动验证,效果比预期好得多。
2.2 持续集成流水线的标准阶段
一条像样的CI流水线,至少包含下面这几个环节。我在多个项目中迭代过这个模板,它很稳。
- 代码提交与触发 —— push、PR、定时都可以触发。我个人习惯用Webhook实时触发,避免人为等待。
- 静态分析与代码检查 —— 检查语法、风格、安全漏洞。我记得有个项目因为没做这个步骤,一个SQL注入风险在线上躺了三个月。
- 构建 —— 编译、打包成可执行的产物。这里要注意环境一致性,我踩过一个大坑:开发机器上编译OK,CI机器上却报错,因为jdk版本差了0.0.1。
- 单元测试 —— 跑所有单元测试,要求覆盖率门槛。我一般设80%作为硬性门禁。
- 集成测试/契约测试 —— 验证模块间的交互。尤其涉及Mock回放的地方,这一步是核心。后面几章会深入讲。
- 制品归档与通知 —— 把构建产物存到仓库,通知团队结果。成功与失败都要发。
一个小技巧: 把流水线分成“快速反馈”和“深度验证”两个阶段。快速阶段10分钟内必须跑完,深度阶段可以并行跑半小时以上。这样开发不用等太久就能知道大部分问题。
2.3 自己画的持续集成流程总图
下面这张图是我在培训时经常展示的,它把上面几个步骤串了起来。每个节点都是必要环节,缺一个都可能出问题。
2.4 CI的核心原则(我的经验提炼)
这么多年下来,我总结了几条铁律。不一定适合所有项目,但至少能让流水线少出幺蛾子。
- 每次提交都触发全量验证 —— 别偷懒只跑增量测试。我有过一次惨痛经历:只加了几个新方法的测试,老的被改了却没测试覆盖,结果上线炸了。
- 失败即为阻塞 —— 流水线变红,就应该冻结代码合入,除非是已知的、非关键的例外。我曾经允许团队在红线上继续合代码,结果三天后修复成本翻了5倍。
- 环境一致性 —— 开发环境、CI环境、生产环境尽量镜像。我最推荐用Docker image固化,包括工具链版本。
- 反馈要快 —— 20分钟以上就算慢。想办法拆分并行阶段,或者用Mock减少外部依赖。
2.5 一个简单的CI配置文件示例
拿GitLab CI举个例子。这是我比较常用的模板,去掉了业务细节,只保留骨架。
stages:
- lint
- build
- test
- integration
- archive
lint_job:
stage: lint
script:
- eslint src/
build_job:
stage: build
script:
- docker build -t myapp:$CI_PIPELINE_ID .
artifacts:
untracked: false
unit_test_job:
stage: test
script:
- npm test -- --coverage
coverage: '/Lines\:\s(\d+\.\d+%)/'
integration_test_job:
stage: integration
script:
- npm run test:integration
dependencies:
- build_job
archive_job:
stage: archive
script:
- docker push registry.example.com/myapp:$CI_PIPELINE_ID
only:
- main
曾经踩过一个坑: 我让单元测试和集成测试用同一个Docker镜像,但集成测试需要依赖外部Mock服务。结果单元测试阶段Mock服务还没就绪,导致测试全挂。后来我把Mock服务的启动提前到`before_script`中,并且用wait-for-it脚本等它ready。
2.6 小结与下一步方向
这一章我们把CI/CD的骨架搭起来了。持续集成不是花架子,它需要你认真对待每个环节。尤其是集成测试这一步,很多人用真实服务跑,慢且不稳定。所以我才在后期大力推广Mock回放技术——它能让集成测试跑得像单元测试一样快。