CI/CD核心概念:持续集成、持续交付、持续部署
聊到CI/CD,很多刚入行的朋友容易把这三个词搞混。我记得有一次面试,候选人信誓旦旦地说「我们团队已经做到持续部署了」,结果一问,其实只是每天手动打一次包扔到测试环境。嗯,这里面的门道,咱们今天一次说清楚。
持续集成(Continuous Integration)
持续集成,说白了就是「频繁地把代码合并到主干」。我个人习惯是每天至少合并两到三次。为什么要这么频繁?你想想看,如果大家各写各的,憋了两周才合并一次,那冲突解决起来简直是一场噩梦。
我在项目中遇到过最夸张的一次,两个团队各自开发了半个月,合并时产生了300多个冲突文件。那天整个团队加班到凌晨三点,就为了把代码缝起来。从那以后,我强制要求所有成员每天至少push一次代码。
持续集成的核心要点:
- 代码频繁合并到主干(每天多次)
- 每次合并自动触发构建和测试
- 快速发现集成问题(通常在几分钟内)
- 问题不隔夜,当天发现当天修
持续集成最关键的,其实是「自动化」。每次代码提交,自动拉代码、编译、跑单元测试、做静态分析。如果哪一步失败了,立刻通知到提交者。我曾经见过一个团队,CI跑一次要40分钟,大家提交完代码就去刷手机等结果。这其实已经违背了CI的初衷——快速反馈。
持续交付(Continuous Delivery)
持续交付是持续集成的自然延伸。代码通过CI验证后,自动部署到类生产环境,做进一步的验证。但注意,这里的关键区别是:部署到生产环境这个动作,仍然需要人工审批。
为什么需要人工?说白了,有些决策机器做不了。比如某个版本要配合市场活动发布,或者要等法务审核通过。我建议在持续交付阶段,保留一个「一键发布」的按钮,但按钮按不按,由人来决定。
我的经验:持续交付做得好,团队可以做到「随时可发布」。代码永远处于可部署状态,只是发不发由业务决定。我曾经带过一个项目,持续交付跑了一年多,每次发布前只需要点一下按钮,整个流程15分钟搞定。
持续部署(Continuous Deployment)
持续部署,就是完全自动化了。代码从提交到上线,中间没有任何人工干预。听起来很爽对吧?但我要泼一盆冷水:不是所有项目都适合持续部署。
我曾经在一个金融项目上尝试过持续部署,结果差点出事。那次是一个配置文件的改动,CI通过了,自动化测试也过了,但上线后导致某个交易接口超时。虽然很快回滚了,但领导层对自动化的信任度大打折扣。
避坑指南:我曾经以为持续部署就是「无脑自动化」,后来发现大错特错。持续部署的前提是:
- 完善的自动化测试覆盖(单元测试、集成测试、端到端测试)
- 灰度发布能力(先让1%的流量走新版本)
- 快速回滚机制(秒级回滚到上一个版本)
- 完善的监控和告警(发现问题自动停止发布)
三者的区别
用一个表格来对比,会更清晰:
| 维度 | 持续集成 | 持续交付 | 持续部署 |
|---|---|---|---|
| 自动化程度 | 构建+测试自动化 | 构建+测试+部署到预发布 | 全流程自动化 |
| 人工介入 | 需要人工合并代码 | 需要人工审批上线 | 无需人工介入 |
| 发布频率 | 每天多次 | 随时可发布 | 每次提交都发布 |
| 风险等级 | 低 | 中 | 高 |
| 适用场景 | 所有项目 | 大部分商业项目 | 互联网SaaS、内部工具 |
CI/CD流水线的基本组成
聊完了概念,咱们看看流水线到底长什么样。我习惯把流水线分成几个阶段,每个阶段都有明确的输入和输出。
阶段一:代码提交与触发
一切从开发者执行 git push 开始。流水线的触发方式有两种:
- Push触发:任何分支的push都触发,适合开发分支
- PR触发:只有创建Pull Request时才触发,适合主分支
我个人习惯在开发分支上用Push触发,主分支上用PR触发。这样既能快速反馈,又能保证主分支的质量。
阶段二:代码检查
这个阶段我称之为「守门员」。代码还没开始编译,先做静态检查:
# 一个典型的代码检查阶段
- lint(ESLint / Pylint / Checkstyle)
- 代码格式化检查(Prettier / Black)
- 安全扫描(SonarQube / Snyk)
- 代码复杂度检查
我曾经见过一个项目,代码里居然有硬编码的数据库密码。如果代码检查阶段没拦住,后果不堪设想。所以我现在对安全扫描特别重视。
阶段三:构建与编译
代码检查通过后,开始真正的构建。这个阶段要做的事情:
- 编译源代码
- 打包成可部署的制品(jar、docker镜像、npm包等)
- 生成版本号并打标签
小技巧:我建议每次构建都生成唯一的版本号,比如用 git commit SHA + 构建序号。这样出了问题,能精确追溯到是哪次提交、哪个构建。
阶段四:自动化测试
测试是流水线的核心环节。我一般按这个顺序跑:
- 单元测试:跑得最快,覆盖最细,先跑
- 集成测试:测试模块间的交互
- 端到端测试:模拟用户操作,跑得最慢,放最后
为什么要按这个顺序?说白了,如果单元测试都挂了,后面的测试根本没必要跑。尽早失败,尽早反馈。
阶段五:部署与发布
测试通过后,进入部署阶段。这里根据你的CI/CD成熟度,可以选择:
- 部署到测试环境(持续集成阶段)
- 部署到预发布环境(持续交付阶段)
- 部署到生产环境(持续部署阶段)
注意:生产环境的部署一定要有「一键回滚」的能力。我见过太多团队,部署上去容易,回滚回来要折腾半小时。这其实是不合格的。
一个完整的流水线示例
最后,给大家看一个我常用的流水线配置(简化版):
stages:
- lint
- build
- test
- deploy
lint:
script: npm run lint
build:
script: npm run build
artifacts:
paths:
- dist/
test:
script:
- npm run test:unit
- npm run test:integration
- npm run test:e2e
deploy:
script:
- docker build -t myapp:$CI_COMMIT_SHA .
- docker push myapp:$CI_COMMIT_SHA
- kubectl set image deployment/myapp myapp=myapp:$CI_COMMIT_SHA
only:
- main
这个流水线看起来简单,但每个阶段背后都有很多细节。比如测试阶段,我建议加上超时机制,防止某个测试卡住导致整个流水线挂掉。还有部署阶段,最好加上「人工确认」的步骤,除非你确定要做持续部署。
好了,CI/CD的核心概念和流水线组成就聊到这里。下一章咱们会深入讲讲如何搭建一条真正可用的流水线,包括工具选型、环境配置、以及我在实际项目中踩过的坑。到时候见。