1 CI/CD 要回答什么
想象你维护着一个订单 API:顾客提交订单,服务校验商品与金额,把订单写进数据库,再返回订单编号。服务运行在云上的计算资源中,通过网络入口接收请求,使用应用身份访问数据,并把日志和指标送往观测系统。现在你修改了优惠计算规则。代码保存之后,怎样证明它可以发布,怎样把正确版本送到生产,又怎样知道真实订单仍能正常提交?CI/CD 要把这些问题变成可重复执行、可以追溯的交付过程。
CI 是持续集成,重点是让代码变更频繁地进入共享代码线,并及时得到构建、测试等反馈。开发者不必积攒很久才发现自己的修改与其他人的修改冲突。持续集成需要团队配合:变更尽量小,失败及时修复,共享代码保持可用。仅仅安装一个自动运行脚本的工具,并不代表团队已经实现了持续集成。
CD 常有 2 种含义。持续交付强调代码经过规定检查后,始终处于可以发布的状态,生产发布仍可以保留人工决定。持续部署则让满足规则的变更自动进入生产。它们都需要可信证据;区别主要在生产放行方式。人工批准不是质量证明,自动放行也不是天然冒险。关键是放行规则是否明确,检查是否覆盖主要风险,失败后是否有可执行的恢复办法。
本课把代码、制品和部署分开理解。代码是开发者维护的源文件;制品是构建产生、可以交付的结果,例如容器镜像或应用安装包;部署是让目标环境运行指定制品的操作。代码提交成功,不等于制品构建成功;构建成功,不等于测试通过;部署命令返回成功,也不等于顾客能成功下单。每个边界都需要自己的判断依据。
云平台提供计算、身份、网络、数据和观测能力,流水线把这些能力连接起来。它可以使用受限身份更新服务,部署到指定网络中的环境,并读取验证结果。流水线不会自动替你设计数据库兼容性,也不会因为名称中有“持续”就保证业务连续。学习的起点是能回答:这次交付是谁触发的,检查了什么,交付了什么,运行在哪里,以及出了问题由什么信号发现。

2 构建、测试与制品晋升边界
构建把选定的源代码和依赖转换成可执行制品。订单 API 可能需要编译代码、安装依赖、打包运行文件,再生成镜像。构建记录至少应能定位代码提交、流水线运行、构建配方和依赖版本。若依赖每次都取“当前最新版”,即使提交没有变化,构建结果也可能变化。入门阶段先使用明确版本和依赖锁定文件,理解“同样的输入应尽量产生可解释的结果”,更深入的可复现构建机制可以以后再学。
测试回答的是特定条件下的行为问题。单元测试可以检查折扣计算,集成测试可以检查服务与数据库的交互,接口测试可以检查提交订单后的响应。测试通过的含义应限制在已执行的检查范围内。使用模拟数据库的测试,不能证明生产数据库的权限和网络一定正确;只检查首页返回成功,也不能证明订单写入过程正确。测试报告需要说明对象、环境和结果,避免把不同层次的通过混在一起。
制品晋升是允许某个已确定的制品进入后续环境。常用原则是“构建一次,逐级晋升”:测试、预生产和生产使用同一个制品标识,环境差异通过受控配置提供。如果每到一个环境就重新构建,测试通过的可能是 A,生产运行的却是 B。重新构建并非永远不允许,但它会产生新的候选制品,需要重新建立适用的检查证据,不能直接继承旧制品的通过结论。
标签方便人阅读,例如 release-12 或 latest,但标签可能被重新指向其他内容。摘要通常根据制品内容计算,用来精确标识该内容。部署记录应保存实际摘要,批准也应指向确定摘要;只有名字相同不足以证明内容相同。摘要让我们能对齐对象,却不能单独证明对象安全。来源、检查结果和批准规则仍然必不可少。
制品与配置还要分开记录。数据库地址、功能开关和服务副本数可能随环境变化,密码等秘密则应通过受控方式注入,不写进镜像或普通日志。相同制品配上错误配置,同样可能故障。因此“构建一次”不等于“所有环境完全相同”,而是把可执行内容固定下来,同时明确配置、秘密引用和数据条件如何变化。数据库结构变更尤其需要关注旧版与新版应用是否都能使用过渡期的数据结构。

3 流水线门禁与环境推进
门禁是进入下一阶段必须满足的条件。它可以是构建成功、必需测试通过、扫描结果符合规则,也可以是指定角色批准生产变更。门禁应该表达为可判断的事实,例如“订单接口测试全部通过,报告对应候选摘要”,而不是“看起来没问题”。执行检查和作出放行决定也应区分:某个检查任务运行完毕,只能说明任务结束,门禁还必须读取结果并判断是否允许继续。
一个入门流程可以是:提交后执行快速检查,合并后生成候选制品,在测试环境执行接口和数据交互测试,再在预生产环境验证配置与部署过程,最后申请生产放行。环境名称不是强制标准,团队可以根据服务风险调整数量。重要的是每个环境有明确用途。预生产若缺少关键依赖,或者配置长期偏离生产,就不能因为名称相似而被视为充分证明。
推进时应携带一组关联信息:提交标识、制品摘要、报告位置、配置版本、目标环境和批准记录。若批准之后换了候选摘要或重要配置,应重新判断原批准是否仍然适用。对于订单 API,审批者批准的是某个具体候选在指定条件下进入生产,而不是给未来任意版本一张通行证。并发发布也要受控,避免较早的部署任务稍后完成,意外覆盖较新的版本。
流水线身份需要按照任务分配权限。普通代码检查通常不需要生产访问权;构建任务可能需要写入制品仓库;测试部署任务只能更新测试环境;生产部署任务才能更新指定生产服务。应用运行时访问订单数据库的身份,又是另一种责任。把这些权限分开,可以让某个检查脚本的问题不至于直接变成生产修改权限。身份凭据优先采用短期、受约束的授权方式,具体接入方法取决于所用平台。
网络与数据也构成环境边界。测试任务能访问测试数据库,不代表它应该能连接生产数据库;来自外部贡献的代码更不能直接取得生产秘密。部署后的健康检查应覆盖服务启动、依赖连通和必要的业务动作。对于会写入数据的探针,要有专用测试账户、清晰标记和清理规则,避免验证动作混进真实交易。生产放行之后还应保留观察阶段,只有运行验证满足条件,交付才算完成。

4 动手:追踪一次提交到部署的证据
这次练习的目标是沿着 1 次交付找到证据,不要求购买云资源。可以使用已有练习项目的流水线界面;如果暂时没有环境,就按照下列教学记录完成纸面核对。示例中的提交 a17、运行 204 和摘要 sha256:7b21…都是缩写,真实操作应保存完整标识。先选定一个很小的修改,例如调整订单金额字段的校验,写清期望结果:金额小于 0 的请求被拒绝,正常订单仍可提交。
先从提交页面找到触发的流水线运行,确认它检查的确实是这次提交。有些流程会检查合并后的候选结果,因此被测试的提交可能不是开发分支的原始提交;应记录实际测试对象及其与变更的关系。接着打开构建阶段,找到输出制品的完整摘要。若页面只给出易读标签,继续查看制品详情或构建输出中的解析结果,不能把标签当作最终内容标识。
再查看测试和扫描报告。记录执行时间、检查范围、结果与候选摘要之间的关联。部分测试可能在打包前针对源代码执行,此时要能通过同一次受控构建的输入关联到候选制品,并补充必要的制品级验证。不要为了填满表格而声称所有测试都直接运行在镜像上。证据的价值在于真实描述做过什么,以及哪些条件还没有覆盖。
进入部署记录后,核对目标环境、批准摘要、实际部署摘要和配置版本。部署任务若只记录了一个标签,要检查它在执行时解析成了什么。随后从运行实例的版本信息、部署平台记录或受控版本接口确认实际运行内容。公开版本接口不应暴露秘密或完整内部配置;它只需提供足以关联发布的信息。多实例服务还要检查是否仍有旧实例在接收流量,不能只查看最先更新的实例。
最后查看部署时间附近的请求错误率、延迟和订单成功信号,并执行受控接口验证。把结果写成简短记录:“运行 204 从提交 a17 生成摘要 7b21…,测试通过,配置版本 cfg-18,批准后部署到生产,运行摘要一致,观察窗口内订单探针通过。”实际报告应补上时间、报告链接和观察阈值。若任何环节找不到关联,结论应是证据缺口,而不是凭前面的绿色图标推断成功。
| 证据对象 | 本次要核对的内容 | 能回答的问题 |
|---|---|---|
| 提交与流水线 | 实际输入提交、运行编号 | 这次检查从哪里开始 |
| 制品与报告 | 完整摘要、报告范围、结果 | 检查对应哪个候选 |
| 批准与部署 | 摘要、配置、环境、执行身份 | 谁允许什么进入哪里 |
| 运行与观测 | 实例版本、业务探针、指标 | 目标版本是否运行正常 |

5 速度与安全扫描取舍
流水线反馈太慢,开发者可能积攒更大的变更,或者不愿等待结果;检查太少,又可能让问题在更昂贵的阶段才暴露。取舍不应从“删掉哪项安全检查”开始,而应先问哪些问题值得尽早发现,哪些检查需要完整环境,以及哪些工作可以并行。对订单 API,语法错误和金额计算错误通常适合快速反馈,完整的数据交互验证则可能需要更长准备时间。
安全扫描也有不同对象。依赖扫描关注使用的组件是否存在已知风险;源代码检查寻找部分危险写法;制品扫描检查交付内容;秘密检测尝试发现误提交的凭据。它们各有盲区,不能把任意一种扫描通过写成“没有安全问题”。初学者先理解检查对象和处置规则,不必马上掌握所有扫描原理。规则应说明哪些结果阻断,哪些进入有时限的修复流程,以及由谁判断例外。
可以把检查安排在不同阶段:每次提交先运行快速且高价值的检查,发布候选再执行完整门禁,并根据需要安排定期复查。定期检查有意义,是因为代码未变时,依赖风险信息仍可能更新。复用旧报告时也应检查它对应的制品和规则是否仍适用。不能因为上周通过,就默认今天的候选和风险条件完全相同。
缓存与并行可以缩短等待,但也会带来解释成本。依赖缓存应与锁定文件等输入关联;测试结果不能随意跨不同输入复用。并行任务必须在所有必需结果齐备后才能放行,不能由最早完成的绿色任务代表全局。对不稳定测试,要记录失败频率并修复原因;反复重跑直到变绿会掩盖真实问题,也让通过结果失去清楚含义。
下面的滑块把提前执行检查带来的等待成本可视化。它只计算教学设定中的检查项,不估算漏洞发现概率。你会看到,提前检查越多,开发者等待越久,但更多问题有机会在提交阶段暴露;发布前的完整门禁仍然保留。实际方案还需要依据失败记录、检查耗时和服务风险调整,不能把演示数字直接当作团队承诺。
6 常见坑:跳过门禁只求绿灯
最容易出现的偏差,是把“页面全绿”当成交付目标。某项测试失败后,直接把它改成允许失败;扫描报出风险后,关闭扫描;部署验证超时后,只增加等待时间。这样可能让流水线恢复绿色,却没有解释问题是否解决。门禁调整当然可以合理进行,但应说明原检查为何不适用、替代证据是什么、风险由谁承担,并留下可追踪记录。
另一个陷阱是检查了错误对象。例如分支 A 测试通过,合并后的内容却发生变化;或者测试使用固定摘要,生产任务重新读取可变标签。它们看起来都有完整步骤,证据却没有指向同一个候选。解决方向不是简单增加更多阶段,而是核对每个阶段的输入、输出和关联标识。没有对象一致性的通过结果,难以支持后续晋升。
权限过宽也会绕过门禁。如果任何开发任务都持有生产凭据,受保护的生产发布阶段就可能失去约束。应在权限层面限制部署能力,并保护生产配置与审批规则。来自不可信变更的脚本尤其不应取得高权限秘密。日志里也要避免打印令牌、数据库密码和包含敏感字段的请求正文,否则用于追踪交付的记录本身会变成泄露渠道。
“回滚按钮已经准备好”也可能制造错觉。旧镜像可以重新启动,不代表旧程序能理解新版写入的数据。删除数据库字段、改变数据含义或执行外部扣款,都可能无法通过换回镜像恢复。对订单服务,应在发布前说明数据变更是否兼容,失败时如何限制影响,以及何时需要向前修复。恢复动作也需要验证业务结果,不能只确认旧版本启动成功。
紧急处理可以设置例外流程,但不能让例外成为日常捷径。例外记录至少应包含理由、适用制品、授权者、补偿检查和失效时间。事后要补齐检查并修复导致绕行的原因。没有期限的“临时放行”会逐渐变成新的默认规则。保留失败记录同样重要:它帮助团队辨认真正的瓶颈,也避免下一次事故只能凭记忆还原。
7 案例:错误制品被提升到生产
订单团队准备发布修复 A。运行 301 完成构建,把镜像标记为 latest,测试通过后获得生产批准。部署任务排队期间,另一次提交触发运行 302,生成候选 B,并把同一个 latest 标签改为 B。B 的接口测试尚未结束。随后运行 301 的生产部署任务开始执行,它没有使用批准时记录的摘要,而是重新读取 latest,于是生产实际拉取了 B。
页面仍然可能显示绿色:A 的测试通过,生产更新命令也成功返回。但这 2 个事实对应不同对象。B 恰好修改了订单字段映射,部分请求开始失败。这里的首要问题是晋升链断裂,不能只归咎于测试不足。即使 A 的测试再完整,也不能替 B 提供通过证明;即使 B 最终通过某些测试,也不能抹去本次未经适用批准的部署事实。

排查从保留现场和暂停后续晋升开始。值班人员取出批准记录中的摘要 A,再查询生产实际运行摘要 B,并核对部署任务解析标签的时间。这样可以把“怀疑发布导致故障”缩小为“生产运行内容与批准内容不一致”。同时查看哪些实例已更新、哪些请求受影响,以及异常是否与数据库或配置变化有关,避免只修正标签却遗漏其他影响。
恢复时,如果已知可用版本与当前数据兼容,可以按其明确摘要部署,并验证订单提交与查询。如果存在不兼容的数据变更,则需先限制影响,再选择兼容修复或数据恢复方案。本例假设数据库结构未变,团队部署已知可用摘要,确认运行版本和订单探针恢复后再恢复发布。事故期间的部署、判断和验证仍需记录,不能为追求恢复速度而丢失证据。
后续改进包括:生产部署只接受确定摘要,审批绑定摘要与配置;部署前核对批准对象和目标对象,部署后核对实际运行对象;限制发布并发,防止旧任务覆盖新任务。仅仅禁止使用 latest 字样还不够,因为任何可变标签都可能产生相同问题。修复应覆盖规则与权限,使错误输入无法轻易绕过门禁。摘要证明内容一致,检查与批准证明为何允许交付,运行观测证明交付后的实际结果。

8 把晋升规则写进交付方案
交付方案应能让没有参与开发的人判断一个候选是否可以推进。先定义候选的身份:它来自哪个提交,采用什么构建配方,以什么摘要标识,报告存放在哪里。再定义各环境的准入条件、执行身份和完成条件。不要只画“开发→测试→生产”的箭头,而要写清楚箭头由哪些证据支撑,谁有权触发,以及失败后停在哪里。
对订单 API,可以规定测试环境接收通过基础检查的候选,预生产要求接口、数据交互和配置检查通过,生产要求适用扫描结果、明确批准以及恢复准备就绪。实际检查项应随业务风险确定,不需要为了流程看起来完整而增加无用途的环境。每项门禁还应说明报告缺失、服务不可用或结果过期时如何处理;无法取得检查结果不应自动解释为通过。
例外与恢复规则应和正常流程一起设计。谁能够授权例外,哪些条件不可绕过,例外何时到期,都应有答案。恢复规则则说明已知可用制品如何定位,配置如何恢复,数据兼容性怎样确认,以及由什么业务指标判断恢复有效。若恢复需要其他团队配合,应提前约定交接信息,避免故障时才寻找负责人。
上线观察条件要能执行。例如部署后观察 10 分钟,将订单接口错误率与发布前基线比较,并运行受控订单探针。具体阈值必须根据服务目标和历史流量设定;低流量服务仅凭百分比可能难以判断,需要结合请求数量与主动验证。方案还应明确谁负责观察、异常时谁暂停发布,以及证据保留多久。这些约定让发布完成成为一个可以复核的结论。
9 从CI/CD走向基础设施即代码(C40)
到这里,应用制品已经能够被构建、检查和晋升,但目标环境从哪里来?测试环境需要网络、计算资源、身份授权、数据库和日志配置。如果这些资源都靠人在控制台逐项创建,环境之间容易出现未记录的差异。订单 API 在测试环境能连接数据库,生产却因安全组规则不同而失败,就是应用交付流程之外的环境问题。
基础设施即代码把资源的期望状态写成可审阅、可追踪的声明或程序,让环境变更也有明确输入和执行记录。C40 将进一步讨论如何描述资源、审阅变更并核对实际状态。本课先记住交接边界:CI/CD 负责组织检查和推进,基础设施代码描述环境如何建立与变化。它们可以协作,但不能因为都使用代码,就把应用发布与资源修改当作完全相同的操作。
环境变更可能具有更大的影响范围。修改网络规则可能同时影响多个服务,删除存储资源可能影响持久数据,因此基础设施任务需要独立的权限和适用门禁。应用部署成功也不能证明资源变更安全。交付证据应把应用摘要、配置版本和相关基础设施版本关联起来,让排查者知道当时的程序与环境分别是什么。
继续学习前,可以检查自己能否解释 3 个判断:测试通过为什么不能直接等同于生产可用;为什么相同标签不足以证明相同制品;为什么部署成功后仍需查看业务信号。能说清这些边界,就已经抓住本课核心。下一步把环境也变成可审阅对象,才能让“这次部署与上次有什么不同”得到更完整的回答。

放进方案里
为订单 API 编写交付条款时,先写明责任范围:流水线从受保护代码线取得输入,生成具有完整摘要的候选制品,记录提交、构建配方、依赖版本和运行编号。制品只构建 1 次,后续环境按摘要晋升;配置使用独立版本,秘密通过受控引用注入。任何重新构建产生的新候选,都重新满足适用门禁。
测试环境负责接口与数据交互验证,预生产负责部署过程和关键配置验证。生产准入要求必需测试通过、扫描结果符合当前规则、报告对应候选,且批准绑定目标摘要、配置版本和生产环境。报告缺失或结果无法确认时停止推进。普通检查任务不持有生产权限,生产任务使用限定目标服务的部署身份,并控制并发,防止旧任务覆盖新版本。
部署前比较批准摘要与目标摘要,部署后核对运行实例版本,并执行带有专用标记的订单探针。观察窗口设为 10 分钟,由当班负责人结合订单错误率、延迟、请求数量和发布前基线判断;具体阈值在上线前依据服务目标确定。观测不足时延长验证,不能直接按成功关闭发布。部署、配置、审批和观测记录应能互相定位,日志避免保存凭据和敏感订单内容。
恢复条款列明已知可用摘要、配置恢复方式与数据兼容检查。出现异常先暂停后续晋升,再按业务影响决定回退或兼容修复,恢复后再次验证订单路径。紧急例外必须记录原因、授权者、适用候选、补偿检查与失效时间,并安排补齐工作。涉及网络、身份、数据库等资源变化时,关联基础设施变更版本,交由独立权限与门禁处理,使应用和环境的责任都有明确归属。
10 小结:下一步基础设施即代码(C40)
CI/CD 把提交转化为有证据的交付:持续集成尽早反馈,持续交付保持可发布,持续部署按规则自动推进。可信晋升依赖确定的制品身份、适用的检查、受限权限和部署后的业务验证。
自测时可以设想:批准之后标签改变了,生产应读取什么?部署命令成功但订单失败了,交付是否完成?旧程序无法读取新数据时,回退镜像是否足够?答案分别指向摘要绑定、运行验证与数据兼容。下一课 C40 会把网络、身份和计算等环境资源纳入代码与审阅流程,补齐从应用到基础设施的交付链。