8.1

查看英文版

8.1 CI/CD 与交付

概述与动机

持续集成(continuous integration)和持续交付(continuous delivery)(CI/CD)是连接“写代码”与“安全地将其呈现给用户”这两端的结缔组织。持续集成意味着每一次变更都频繁地合并到一条共享主线中,然后自动构建和测试,这样集成问题会在几分钟内浮现,而不是拖到一个漫长发布周期的末尾。持续交付意味着每一个通过流水线的变更都保持在可部署状态,因此把它发布到生产环境就成了一个业务决策,而不是一场工程上的手忙脚乱。持续部署(continuous deployment)更进一步,自动发布每一个通过测试的变更,不设人工关卡。

对大型团队而言,这些区别至关重要。当数百名工程师向相互重叠的系统提交代码时,手动集成和手动测试的成本会呈非线性增长。一条共享的自动化流水线,是让众多贡献者获得快速、可信反馈的唯一实际可行的方式,也是防止一个团队的变更悄悄破坏另一个团队成果的唯一实际可行的方式。流水线成为判断软件是否健康的唯一真相来源,并强制执行一种在大规模下,任何文档或良好意愿都无法保证的一致性。

企业和政府场景又增加了一个维度:可审计性和变更控制。监管机构、安全官员和审计人员需要证据,证明变更经过了评审、测试和批准,并且生产环境中运行的制品正是被构建和审查过的那一个。一条设计良好的 CI/CD 流水线,能把这些合规义务从文书负担变成日常工程工作流的自动副产品。做得好的话,交付会同时变得更快、更安全,而这正是对领导层来说最重要的结果。

另见: 第 8.4 章(平台工程与开发者体验)、第 8.5 章(测试与流程自动化),以及第 7.4 章(产品分析与实验)中关于功能开关(feature flag)(在不重新部署的情况下向用户暴露功能的运行时开关)和渐进式交付(逐步发布一项变更,同时自动监测其健康指标)所支持的实验实践。

关键原则

  • 频繁集成小变更;长期存在的分支是持续集成的大敌。
  • 只构建一次制品,并将同一个制品在每个环境中推广。
  • 让流水线成为权威关卡:如果是绿色,变更就可以发布;如果是红色,工作就要停下来直到修复为止。
  • 不遗余力地为快速反馈进行优化,让开发者保持心流状态,并在上下文还清晰的时候就捕获缺陷。
  • 把一切重复性的工作自动化,包括测试、安全扫描、资源配置和部署。
  • 把流水线定义当作受版本控制、需要评审的代码,而不是可以点点鼠标完成的控制台配置。
  • 为安全、可逆的发布而设计,使任何一次部署都能被迅速撤销。
  • 用功能开关把部署(安装代码)和发布(向用户暴露功能)分离开来。

建议

把流水线设计为一系列质量关卡

把流水线组织成从廉价快速到昂贵彻底依次递进的阶段:先编译和单元测试,然后是集成测试、安全和许可证扫描,最后是部署到预发布和生产环境。每个阶段都是一道变更必须通过的关卡。把最快、最可能失败的检查排在最前面,这样能让开发者在最短时间内得到反馈。尽可能把提交阶段的反馈循环控制在十分钟以内。超过这个时间,开发者就会切换上下文,生产力随之下降。

构建一次,处处推广

在构建阶段产出一个不可变的制品,并将这个确切的制品推广到测试、预发布和生产环境。永远不要按环境重新构建,因为重新构建可能会悄悄引入差异。因环境而异的配置应该在部署时注入,而不是烘焙进不同的构建中。这种做法也正是让你能够确定地告诉审计人员:生产环境中的二进制文件,就是通过了每一道关卡的那一个。

让流水线成为策略的强制执行点

把必需的检查项(代码评审批准、测试覆盖率阈值、安全扫描结果、签名提交)直接编码进流水线和分支保护规则中。存在于 wiki 里的人工策略,在截止日期压力下会被例行绕过。编码进流水线的策略,会被统一且自动地应用到每一次变更上。

让主线始终保持可发布状态

采用主干开发(trunk-based development),将所有工作集成到一条共享分支中,几乎没有或完全没有长期存在的分支;或者使用短生命周期的功能分支,并依靠功能开关来隐藏未完成的工作,而不是依靠长期存在的分支。这样能让合并冲突保持较小的规模,并让主线始终处于可部署状态,而这正是真正持续交付的前提条件。

有意识地选择部署策略

让部署策略与服务的风险和影响半径相匹配:

  • 滚动(rolling)部署逐步替换实例,是无状态服务的合理默认选择。
  • 蓝绿(blue-green)维护两套完全相同的环境,并一次性切换流量,提供即时的回滚路径。
  • 金丝雀(canary)发布把一小部分流量导向新版本,观察健康指标,只有在信号良好时才扩大范围。
  • 功能开关把发布与部署解耦,让你能够在不重新部署的情况下,为特定用户或群体启用某项功能。

采用带自动回滚的渐进式交付

渐进式交付把金丝雀发布与对错误率、延迟和饱和度等指标的自动化分析结合起来。提前定义客观的健康标准,然后让系统根据这些信号自动推广或回滚。自动回滚消除了那种会把一次小事故拖成一次大事故的人为迟疑。

为受监管环境提供发布管理和变更控制

在受监管的场景中,保留一份轻量但真实的变更管理记录。自动记录每次变更是谁批准的、运行了哪些测试、部署了哪个制品。对真正高风险的变更使用变更咨询流程,但要把它们只留给那些确实需要的场合。让每一次常规变更都走一遍每周评审会,会摧毁自动化的价值。相反,应该追求那种能无需仪式性流程、直接流过流水线的标准的、预先批准的变更类型。

权衡取舍:利与弊

方法优点缺点最适合
持续交付(人工发布关卡)业务掌控发布时机;适合受监管的发布窗口需要纪律来保持主线可发布有变更窗口的企业
持续部署(完全自动)反馈最快;批次最小需要成熟的测试和可观测性高信任、高频率的团队
蓝绿部署即时回滚;心智模型简单切换期间环境成本翻倍需要快速回退的关键服务
金丝雀 + 渐进式交付限制影响半径;数据驱动构建复杂;需要良好的指标大规模的面向用户系统
功能开关把部署与发布解耦若不清理会形成开关债务需要安全地发布未完成工作的团队

核心的权衡在于速度与控制之间,但这往往是一种伪选择。成熟的自动化能同时兼得两者:发布更快是因为批次更小,更安全是因为每一次都经过验证且可逆。真正的成本在于测试覆盖率、可观测性和流水线工程方面的前期投入,加上持续保持它们健康所需的纪律。在这项投入上吝啬的组织,得到的是没有安全性的速度,这比缓慢的人工流程还要糟糕。

与团队讨论的问题

  1. 你们对提交阶段反馈时间的目标是什么?当测试套件的耗时超过十分钟时,会被砍掉的是什么? 缓慢的提交阶段会悄悄扼杀持续集成,因为开发者会不再等待绿色结果,转而开始批量攒变更。现在就定下这个数字(本章主张十分钟以内),并决定用什么机制来维持它:并行工作者、严格的测试金字塔,以及把耗时的集成检查移到后面的阶段。在企业规模下,这是一个平台层面的决策,因为数百名工程师共享同一条流水线,每增加一分钟都会在每次提交上乘数放大。带上真实数据参会:当前流水线耗时的 p50 和 p95、最慢的十个测试,以及人们选择重新运行而不是等待的频率。如果你说不出这个目标,也无法用数字为它辩护,那你的流水线正在滑向一个披着 CI 外衣的批处理流程。

  2. 每个服务使用哪种部署策略?这个选择由谁负责? 滚动、蓝绿和金丝雀并非可以互换的选项:它们在成本、回滚速度和复杂度上的权衡各不相同,而正确的选择取决于该服务的影响半径。蓝绿部署以切换期间环境成本翻倍为代价换来即时回滚,这对一个支付系统而言是值得的,对一个内部仪表盘而言则是浪费。金丝雀限制了暴露范围,但需要良好的健康指标和更多的流水线工程投入。对于一个大型或受监管的资产群而言,把这个决定留给每个团队各自的习惯,会在事故发生时暴露出不一致,所以要按服务层级约定默认策略并记录下这个决定。带上你的服务目录,为每个服务标注其策略、回滚路径,以及负责这个决定的人。

  3. 你们如何证明生产环境中的制品,正是通过了每一道关卡的那一个? “构建一次、推广同一个制品”是实现可审计性的全部关键所在,而一旦有人按环境重新构建,或对运行中的服务器打补丁,这个保证就会破裂。在企业和政府场景中,审计人员会要求你把一个正在运行的二进制文件追溯到它的提交、评审和批准,你希望这个答案只需几秒钟,而不是一周。决定你要如何强制执行它:不可变制品、签名镜像、部署时的签名验证,以及在部署时注入配置而不是烘焙进不同的构建中。把当前存在的缺口带到讨论桌上,比如任何会重新构建的阶段、任何手动热修复路径,以及任何配置会分叉出不同制品的地方。这个答案决定了你的合规证据是流水线的自然副产品,还是每次审计前的一场手忙脚乱。

  4. 当主线变红时,实际上会停下什么?你们如何处理不稳定的测试? 只有当一次红色构建真正让工作停下来时,流水线才是一道权威关卡,然而许多组织却悄悄容忍一条长期损坏的主线和一堆间歇性失败的积压,这会训练开发者不断重新运行直到变绿,并在失败之上继续发布。对大型团队而言,这种腐坏会不断复合,因为一个团队被忽视的不稳定测试,会成为所有人绕过关卡的借口,而对流水线的信任维持起来远比重建要便宜得多。权衡这两种相互竞争的拉力:严格的“停线”规则能保护质量,但可能因为一次糟糕的提交而卡住数百名工程师,而宽松的策略能保持吞吐量,却会侵蚀关卡的效力。把证据带到讨论中:你们当前主线处于红色状态的时长、被隔离或不稳定测试的数量、重新运行率,以及有多少变更是在检查失败的情况下被合并的。在企业和政府场景中,要指明谁负责不稳定测试的分诊、谁有权限冻结合并,因为一道没人负责强制执行的关卡,正是审计人员会发现被例行绕过的那道关卡。

  5. 你们的功能开关生命周期是什么样的?谁负责退役它们? 开关是让你能把部署与发布分离、隐藏未完成工作的手段,但每一个开关都是代码中一条不会自行清理的分支,不受管理的开关会累积成没人敢碰的条件复杂度。在一个大型资产群中,这种债务是危险的,因为一个陈旧的开关可能悄悄地拦住一个安全修复,或者把未经测试的代码路径切换进生产环境,而创建它的人往往早已离开。权衡这种张力:开关为你换来了安全的增量式交付,所以目标不是减少开关的数量,而是建立一套有负责人、有到期预期,以及有工具能发现陈旧开关的严谨生命周期。把当前的清单带到会上:有多少开关正在生效、最老的一个存在了多久、哪些没有负责人,以及是否有任何长期存在的开关如今实际上充当着本应放在别处的永久配置。对受监管环境而言,还要补充谁能在生产环境中变更一个开关,以及这种变更是否与一次部署同等严格地被记录下来,因为一次开关翻转即便流水线从未运行,也是一次发布。

  6. 带人工关卡的持续交付,与完全的持续部署之间的边界在哪里?谁来设定回滚阈值? 持续交付让人始终掌控发布时机,这适合有法定变更窗口和高影响半径的系统,而持续部署自动发布每一个通过测试的变更,要求成熟的测试、可观测性和自动回滚才能确保安全。对大型或受监管的组织而言,这个答案很少是统一的:你的营销网站可以持续部署,而你的支付核心系统则保留一道有文档记录的人工关卡,按服务层级划定这条界线,既能避免不必要的摩擦,也能避免鲁莽的自动化。相互竞争的考量因素是速度和批量大小,对比控制和可审计性,再加上自动回滚所需的健康指标带来的工程成本。带上证据:各服务的变更失败率、平均恢复时间、当前发布节奏,以及你愿意信任其在无需人工的情况下推广或回滚的客观信号(错误率、延迟、饱和度)。在政府和企业场景中,把每个层级与谁拥有回滚阈值、谁批准从有关卡的发布转向完全自动化绑定起来,使这个决定是深思熟虑的,而不是随意漂移的。

行业视角

初创企业。 从第一天起就依靠托管的 CI/CD:一个托管的运行器、一条流水线、一个不可变镜像,以及合并后自动部署到预发布环境。不要构建之后还得自己维护的流水线基础设施。功能开关能让两三名工程师安全地合并未完成的工作,并且每天多次发布,一次点击式的生产部署加上快速关闭开关,就是你在规模逼迫你做更多事之前所需要的全部变更控制。

小型企业。 没有专职的平台或发布工程师,应优先使用你的源代码托管平台自带的流水线(内置的 Actions 或同等工具)及其默认部署策略,而不是任何定制方案。诚实地权衡“买还是造”:一条托管流水线加上一个内置回滚功能的托管平台,其成本低于定制方案所消耗的工程师工时。保留基本要素()构建一次、推广同一个制品、方便回退()在流量证明有必要之前,跳过渐进式交付这套机制。

企业。 核心问题在于跨众多团队的一致性:标准化一套共享的流水线模板,强制执行评审、扫描、签名的不可变制品,以及按层级划分的部署策略,使质量不因团队而异。把流水线定义当作经过评审的代码来对待,自动采集变更控制证据,把功能开关和回滚阈值当作受治理的资产来管理,而不是每个团队自己的私下习惯。回报是更快的交付,以及作为副产品自然产生的审计证据,而不是每季度一次的手忙脚乱。

政府。 采购规则、法定变更窗口和公共问责制塑造着流水线。对于有重大后果的系统,倾向于选择带有文档记录的人工发布关卡的持续交付,而不是完全自动化;把常规工作归类为预先批准的标准变更;并为公民在狭窄的年度窗口期依赖的服务保留一条即时回滚路径(蓝绿部署或自动化金丝雀)。确保流水线记录每次变更是谁批准的、运行了哪些测试、部署了哪个制品,使透明度和审计义务通过正常工作流程得到满足,而不是通过人工文书工作。

示例

初创企业。 一家四人规模的 SaaS 初创公司搭建了一条单一的 GitHub Actions 流水线,运行单元测试、构建一个 Docker 镜像,并在每次合并到 main 分支时,自动将同一个镜像部署到预发布环境。生产部署只需一次点击,创始人们依靠功能开关,把未完成的工作合并在开关背后,而不必让一个分支存活数周。当一次糟糕的发布溜了进来时,他们几秒钟内就关闭开关,从容地修复问题,这让他们的小团队无需专职运维人员也能每天多次发布。

企业。 一家全球性银行把数十个团队各自维护的 Jenkins 作业整合成一套标准化的流水线模板,由每个产品团队继承使用。该模板强制执行静态分析、依赖扫描和签名的不可变制品,并通过金丝雀部署发布,自动回滚与错误率和延迟阈值挂钩。由于同一个制品从测试推广到生产、每一道关卡都被记录下来,该银行的审计人员能在几秒钟内把任何一个生产环境二进制文件追溯到它的提交、评审和批准,取代了原本每季度一次的人工证据收集工作。

政府。 一家正在实现申报系统现代化的国家税务机构,采用带有明确人工发布关卡的持续交付,以便在申报季期间遵守法定变更窗口。常规变更被归类为标准的预先批准变更,自动流向预发布环境。生产发布需要经过流水线记录的一次有文档记录的批准。蓝绿部署为该机构提供了一条即时回滚路径,以应对缺陷进入生产环境的情形,这在数百万公民在一个狭窄的年度窗口期依赖该服务时至关重要。

商业理由:动机、投资回报率与总拥有成本

CI/CD 投资的回报体现为更短的变更前置时间、更低的变更失败率,以及事故发生时更快的恢复速度:这些指标被研究持续地与交付绩效和组织成果联系在一起。更快、更小的发布能削减在规模化场景下消耗工程能力的协调开销,自动化验证也能削减扑灭生产缺陷这种昂贵且消磨士气的工作。

总拥有成本要权衡采纳的成本与不采纳的成本。采纳成本包括构建和维护流水线、提升测试覆盖率,以及投资可观测性和平台人员。不采纳的成本更大,却不那么显眼:缓慢的人工发布、集成的痛苦、损害声誉的生产事故,以及在受监管场景中审计失败和补救工作。对领导层而言,最好的论证方式是围绕风险降低和产能来展开。自动化把稀缺的资深工程师时间,从重复的发布苦役转化为产品工作,同时让中断变得更少见、更短暂。

反模式与陷阱

  • 雪花流水线。 每个团队手工搭建一条独特的流水线,导致改进和修复无法共享,质量参差不齐。
  • 按环境重新构建。 为每个阶段重新构建会打破“构建一次”的保证,让细微的差异得以进入生产环境。
  • 对红色构建置之不理。 容忍一条持续损坏的主线,会摧毁对流水线的信任,并使在失败之上继续发布变得正常化。
  • 不稳定测试被放任不管。 间歇性失败会训练开发者不断重新运行直到变绿,使关卡的意义荡然无存。
  • 人工审批作秀。 一个对一切都盖橡皮图章的变更咨询委员会,只会增加延迟而不增加安全性。
  • 开关债务。 从未被移除的功能开关会累积成无法维护的条件复杂度。
  • 部署等同于发布。 把这两者耦合在一起,意味着每一次面向用户的变更都需要一次有风险的重新部署。

成熟度模型

第 1 级:启动(Initiate)。 构建和部署主要是手动的、临时的、被动反应式的。集成发生得很晚,发布不频繁且充满压力,回滚意味着手动重新部署一个旧版本,也没有关于流水线关卡的共同认知。

第 2 级:发展(Develop)。 自动化的构建和单元测试在每次提交时运行,但各团队的实践参差不齐。部署已经脚本化,却仍需人工触发和监督,部分环境是一致的,制品也可能仍按阶段重新构建。即便存在流水线,也往往是一条无法共享的雪花流水线。

第 3 级:标准化(Standardize)。 一套有文档记录、标准化的流水线模板在各团队中被强制执行。它将同一个不可变制品推广到所有环境,应用自动化的质量和安全关卡,把评审批准和扫描结果等必需检查项编码进流水线,并自动采集变更记录。金丝雀或蓝绿等部署策略按服务层级被有意识地选择。

第 4 级:管理(Manage)。 交付被度量并对照基准进行控制。组织追踪变更前置时间、部署频率、变更失败率和平均恢复时间,以及流水线的 p50 和 p95 耗时、不稳定测试和重新运行率,还有功能开关的存续时长。回滚阈值根据观测到的错误率、延迟和饱和度数据来设定,关卡的强制执行依据的是证据而非习惯,每一项指标都有负责人,一旦偏离目标就会采取行动。

第 5 级:协同(Orchestrate)。 交付在整个组织范围内持续改进并相互整合。以指标驱动、带自动回滚的渐进式交付成为常态,发布通过治理良好的开关与部署解耦,合规证据作为副产品自动产生。流水线随资产群的变化而调整,交付指标反哺业务和风险规划,使投资流向杠杆效应最高的改进方向。

讨论要点

  • 对你们最关键的系统而言,带人工关卡的持续交付与完全的持续部署之间,正确的边界在哪里?
  • 你们如何在不把强制性的变更管理流程变成走过场的情况下,让它保持真正的意义?
  • 应该由哪些客观的健康指标来主导自动回滚?这些指标的阈值由谁负责?
  • 平台团队应该如何在标准化的流水线模板与那些有特殊需求的团队的合理诉求之间取得平衡?
  • 你们退役功能开关、防止其变成债务的策略和工具是什么?
  • 你们如何衡量更快的交付是否真正改善了业务成果,而不仅仅是发布了更多东西?

关键要点

  • CI、CD 和持续部署是不同的概念;选择与你的风险承受能力和成熟度相匹配的自动化水平。
  • 只构建一次制品,并将同一个制品推广到每个环境。
  • 把流水线设计为一系列为快速反馈而优化的有序质量关卡,并把它当作权威的发布决策。
  • 有意识地选择部署策略,并采用带自动回滚的渐进式交付来限制影响半径。
  • 用功能开关把发布与部署解耦,并管理好开关债务。
  • 在受监管环境中,自动采集变更控制证据,而不是通过人工文书工作。

参考文献与延伸阅读

  • Jez Humble and David Farley, Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation.
  • Nicole Forsgren, Jez Humble, and Gene Kim, Accelerate: The Science of Lean Software and DevOps.
  • Gene Kim, Jez Humble, Patrick Debois, and John Willis, The DevOps Handbook.
  • Gene Kim, Kevin Behr, and George Spafford, The Phoenix Project.
  • Betsy Beyer, Chris Jones, Jennifer Petoff, and Niall Richard Murphy (eds.), Site Reliability Engineering.
  • Pete Hodgson, “Feature Toggles (Feature Flags)” (essay).
  • ITIL (Information Technology Infrastructure Library), change management guidance.