CI/CD 流水线基础

说实话,我在给团队搭流水线之前,总会先问一句:「你们到底想要持续集成,还是持续部署?」这两个词经常被混着用,但背后的流程和风险完全不一样。先把这个搞清楚,后面集成 KMS 才不会跑偏。

从持续集成说起

持续集成(CI)的核心就一句话:频繁地把代码合并到主干,并且每次合并都触发自动构建和测试。

我自己经历过一个项目,团队每周才合并一次代码,结果合并那天所有人都坐在会议室里盯着冲突发呆。后来改成每天至少合并三次,每次合并都跑一遍自动化测试——冲突少了,问题也能在半小时内暴露。

我的一点习惯:CI 阶段至少要跑单元测试和代码静态分析。单元测试帮你把逻辑漏洞堵在门口,静态分析则能揪出潜在的密钥硬编码——这在我们后续讲 KMS 集成时特别有用。

为什么 CI 这么重要?因为反馈越快,修复成本越低。你想想看,代码提交后 10 分钟就能看到测试结果,和隔天才知道测试挂了,完全是两个世界。

持续交付 vs 持续部署

这两个概念经常被混为一谈,我在这里把它们掰开说清楚。

维度 持续交付(CD – Continuous Delivery) 持续部署(CD – Continuous Deployment)
最终产物 可随时部署的发布包 自动上线的新版本
人工介入 需要人工点击“发布”按钮 全自动化,无需人工确认
适用场景 金融、医疗等需要审批的环境 互联网、SaaS 等高频发布场景
风险控制 有手动卡点,降低生产风险 依赖强大的自动化测试和灰度策略

在我带过的团队里,90% 的新人都会把“持续交付”和“持续部署”统称为“CD”,但后来在配置生产环境发布策略时吃了亏——没有审批就直接推上生产,结果依赖的密钥没更新,整个服务挂了。所以我的建议是:明确你的 CD 指的是哪种,并在流水线设计里体现出来。

流水线的核心阶段

一条典型的 CI/CD 流水线,无论你用 Jenkins、GitLab CI 还是 GitHub Actions,基本都会包含以下几个阶段。我习惯把它分成“CI 左半段”和“CD 右半段”:

  1. 代码检出 —— 从仓库拉取最新代码和密钥引用。
  2. 依赖安装 —— 这里经常踩坑:如果依赖里包含私有包,注意不要硬编码凭证。
  3. 代码检查 —— 静态分析、格式校验,顺便扫一遍有没有密钥泄露。
  4. 构建 —— 编译、打包、生成制品。
  5. 测试 —— 单元测试、集成测试、安全扫描。
  6. 制品存储 —— 将经过验证的产物推送到制品仓库。
  7. 部署 —— 按环境推送(开发→测试→预发布→生产)。
  8. 验证与监控 —— 确认服务健康,回滚失败版本。

下面这张图我用最常用的流程来画,你可以对照着理解每个阶段的位置。

CI/CD 流水线典型阶段 持续集成 (CI) 持续交付 / 持续部署 (CD) 代码提交 构建 单元测试 集成测试 制品仓库 部署 验证与监控 通过单元测试后推送制品 已通过安全扫描 的版本才能部署
我曾经踩过的坑: 把制品存储放到部署之后,结果每次部署都要重新构建,效率极低。正确的顺序是「构建 → 测试 → 存储制品 → 部署」。而且存储制品时,别忘了把密钥版本的引用也存起来,否则部署时怎么找到正确的密钥都是个问题。

常见流水线工具对比

选工具就像选搭档,合适比流行更重要。我用过 Jenkins、GitLab CI 和 GitHub Actions,简单对比一下它们的差异:

特性 Jenkins GitLab CI GitHub Actions
部署方式 自托管为主 自托管 + SaaS SaaS + 自托管 runner
流水线定义 Jenkinsfile(Groovy) .gitlab-ci.yml(YAML) .github/workflows(YAML)
密钥管理 凭据插件 + 环境变量 CI/CD Variables(可分组) Secrets(环境级)
与 KMS 集成 需要安装插件或写脚本 通过 CI 变量或 OIDC 通过 OIDC 或 Actions(自建)
我的偏好 适合复杂定制的企业 GitOps 流派最爱 开源项目 + 小型团队

我个人更倾向用 GitLab CI 来做 KMS 集成,因为它的 CI/CD Variables 支持按环境分组,还能直接引用外部密钥服务。但如果你团队用 GitHub,配合 OIDC 也是不错的选择。

密钥管理在流水线中的位置

你现在可能会想:密钥管理到底应该在流水线的哪个环节出现?我按经验总结三个必须介入的点:

  • 代码检出后、构建前:从 KMS 拉取当前版本需要的密钥,注入到构建环境。
  • 测试阶段:确保测试环境使用的密钥是独立的,不要和生产环境混用。
  • 部署前:验证目标环境是否有权访问对应的密钥版本,避免部署后无法解密。
记住这个原则: 密钥永远不应该硬编码在代码里,也不应该明文出现在流水线日志中。我见过有人把数据库密码直接写在 Dockerfile 里,然后推到公共仓库——那场面,真是天降大坑。