CI/CD 是让团队能一天发布很多次、而且安全的那套自动化。它用一条会在每次变更上构建、测试、发布的流水线,取代了手动构建和手工部署服务器这种易错的仪式。回报不只是快:更小、更频繁的发布更安全,因为每次携带的改动更少、更容易回退。它与 容器(制品)和 Kubernetes(部署目标)天然搭配。
- CI = 持续集成——频繁合并到主干,并在每次提交上跑自动构建 + 测试,立刻发现破坏。
- CD = 持续交付(始终可部署、按按钮发布)或持续部署(每个通过的构建自动上线)。
- 一次构建,推同一个制品——一个不可变镜像不变地穿过 dev → staging → prod,所以你测的就是你发的。
- 流水线分阶段、带门禁——源码 → 构建 → 测试 → 安全扫描 → 部署,每个都是必须通过的门。
- 部署策略限制爆炸半径——滚动、蓝绿、金丝雀,在速度/成本与安全间取舍。
- 快速回滚是安全网——不可变制品让回滚变成切标签,而非重新构建。
持续集成在每次提交上跑自动构建和测试套件,让问题尽早暴露。持续交付让每个通过的构建都可部署;持续部署则自动上线。流水线一次构建出不可变制品,并让同一个制品在门禁后穿过各环境。用滚动/蓝绿/金丝雀策略安全发布,把回滚保持为一步快操作。更小、更频繁的发布比一次性大发布更安全。
为什么用 CI/CD
手动发布因为可预见的原因失败:分支分叉数周后集成很痛,机器之间构建不一致,半夜跑部署步骤的人会出错。把从提交到生产的路径自动化,让发布可复现、快、可逆。更深的洞见反直觉但已被充分验证:更频繁地部署反而降低风险,因为每次部署只含一个小而可理解的改动,出问题易于追踪和撤销——与含成千上万改动的季度发布正相反。
持续集成
CI 是把每个人的工作频繁(理想是一天多次)集成进一条共享主干的实践,并在每次推送上跑自动构建和测试套件。要点是在引发问题的那次提交后几分钟内、趁作者记忆犹新时,就抓住集成问题和回归,而不是几周后大合并时才发现一团乱麻。CI 依赖一套扎实、快速、可靠的测试;没有它,"绿色"流水线毫无意义。
流水线各阶段
CI/CD 流水线是一串阶段,每个都是必须通过才能进入下一个的门(gate):
提交 ─▶ [ 源码 ] ─▶ [ 构建 ] ─▶ [ 测试 ] ─▶ [ 扫描 ] ─▶ [ 部署 ]
│ │ │ │
编译 + 单测 + 安全/ 滚动 /
构建镜像 集成测试 lint/SAST 金丝雀
│ │ │ │
└── 任一阶段失败 → 停止、通知 ───────┘
只构建一次 → 产出制品(如 image:sha)→ 把它往后推
GitHub Actions、GitLab CI、Jenkins、CircleCI 等工具用声明式(通常是仓库里的一个 YAML 文件)定义这些阶段。流水线在触发时自动运行——push、pull request、合并到主干,或打 tag。
持续交付 vs 持续部署
"CD" 是两个相关但不同的概念,这个区别是经典面试点:
| 方面 | 持续交付(Continuous Delivery) | 持续部署(Continuous Deployment) |
|---|---|---|
| 最终状态 | 每个构建都可部署 | 每个通过的构建已被部署 |
| 到生产 | 人工审批(按一下按钮) | 全自动,无人介入 |
| 何时用 | 你想要一个人/业务的门 | 你完全信任你的测试 |
两者都需要同样有纪律的流水线;唯一区别是是否由人按下"发布"。持续部署是更进阶的终态,要求对自动化测试有非常高的信心。
一次构建,推同一个制品
一条基础原则:只构建一次制品,并让同一个制品穿过每个环境。如果你为 staging 重新构建、再为 prod 重新构建,你发布的就是从没测过的东西。用容器这很自然——流水线构建出 myapp:<git-sha>,对它跑测试,到生产的就是这同一个不可变镜像。制品带版本且不可变,这也正是回滚轻而易举的原因。
部署策略
你如何替换运行中的版本,决定了一次坏部署的爆炸半径:
滚动 : 一次替换几个实例 v1 v1 v1 → v2 v1 v1 → v2 v2 v2
蓝绿 : 起一整套 v2("绿"),切流量,留着 v1("蓝")以便回退
金丝雀 : 把 1% → 5% → 50% → 100% 流量导向 v2,边导边看指标
| 策略 | 做法 | 取舍 |
|---|---|---|
| 滚动 | 逐步替换实例 | 不需额外容量;短暂版本混跑;回滚较慢 |
| 蓝绿 | 整套并行环境,切流量 | 瞬时切换 & 回滚;部署期间双倍基础设施 |
| 金丝雀 | 先放一小部分,边看边放 | 爆炸半径最小;需要好的指标 + 自动化 |
金丝雀对高风险服务最安全,因为一次坏发布在自动监控抓到并停止前只命中一小撮用户——这也是它和 可观测性 紧密搭配的原因。
回滚
最重要的安全属性是能快速回到一个已知良好状态。因为制品不可变且带版本,回滚通常就是"重新部署上一个镜像标签"——几秒,而非重新构建。蓝绿让它变成切流量;金丝雀让它变成"停止放量"。推论:一个你没法快速回滚的部署,就是一个你不该做的部署。(数据库迁移是需要小心的例外——让 schema 变更向后兼容,使新旧代码能在过渡期共存;见 编码与演化。)
质量门禁与实践
- 自动化测试——单测、集成、端到端——是骨干;流水线只和它们一样可信。
- 静态分析 & 安全扫描——lint、SAST、依赖/CVE 与镜像扫描,有问题就让构建失败。
- 审批——交付模式下(或敏感改动)在 prod 前设一道人工门。
- 主干开发(trunk-based)——短生命周期分支频繁合并,让集成成本低。
- 特性开关(feature flags)——把部署和发布解耦:先把代码暗着发上去,再打开(或关掉)而无需重新部署。
常见坑
- 不稳定的测试(flaky)——非确定性失败侵蚀信任,直到大家无视红色构建;隔离并修掉它们。
- 流水线慢——CI 跑一小时,大家就攒着改动、不再频繁集成;并行化并加缓存。
- 雪花环境——和 prod 不一样的 staging 会藏 bug;用同样的镜像和基础设施即代码。
- 没有回滚预案——尤其围绕不可逆的迁移;永远准备一条经过验证的退路。
CI/CD 把发布变成一条可复现、自动化、可逆的流水线:每次提交都集成并测试,一次构建出不可变制品并往后推,用一种限制爆炸半径的策略(滚动/蓝绿/金丝雀)发布——以快速回滚作为安全网。文化上的回报是:小而频繁的部署比稀有的大发布更安全。
CI 对比 CD?CI = 每次提交自动构建+测试以尽早发现破坏;CD = 持续交付(始终可部署、人工发布)或持续部署(每个绿色构建自动上线)。
为什么只构建一次制品?把同一个不可变制品推过各环境,意味着你发的正是你测的;按环境重新构建有漂移风险。
滚动 vs 蓝绿 vs 金丝雀?滚动逐步替换(无额外容量);蓝绿在两套完整环境间切换(瞬时回滚、双倍基础设施);金丝雀先放一小部分并看指标(爆炸半径最小)。
怎么快速回滚?重新部署上一个不可变制品/标签(或切流量)。让数据库迁移向后兼容,使各版本代码能共存。
部署 vs 发布?特性开关把两者解耦——把代码暗着部署,再打开特性,无需再次部署。