C40

基础设施即代码

点击正文段落选定收藏或划线位置;未选定时使用屏幕内第一段。

课内目录

1 基础设施即代码要回答什么

订单 API 刚上线时,工程师可以在控制台创建计算实例、开放端口、连接数据库,再把监控打开。问题出现在下次:测试环境需要再建 1 套,生产环境需要扩容,原来的负责人却休假了。团队能够看到资源,却说不清它们为什么存在、哪些配置必须相同,以及删掉某个组件会影响谁。基础设施即代码,简称 IaC,要回答的是:如何把资源的期望状态变成可以审阅、重复执行、追踪责任的工程材料。

这里的基础设施不只有服务器。对订单 API 来说,它包括承载服务的计算资源、允许服务读取数据库的身份、限制入站流量的网络规则、保存订单的数据资源,以及记录请求失败的日志与告警。应用代码描述怎样处理订单,基础设施代码描述这段程序在什么条件下运行。两者需要协作,但更改实例数量不等于发布业务版本,修改数据库结构也不应被含混地归为“更新云资源”。

代码的价值首先在于表达意图。例如,“API 只接受来自入口组件的请求”“运行身份只能访问订单数据库”“生产环境至少有 2 个服务副本”,都应成为可检查的配置与约束。配置进入版本管理后,评审者可以讨论具体差异,而不必根据聊天记录猜测。资源名称、标签、输出的连接标识也能随配置管理,使下游部署能找到正确对象。

订单 API 的资源意图与管理闭环
订单 API 的资源意图与管理闭环

但配置文件不是云资源本身。工具需要调用云平台的管理接口,把意图变成实际资源;接口可能超时,权限可能不足,配额也可能耗尽。代码描述正确,并不意味着执行必然完成。IaC 因而需要配套的执行记录、状态管理、权限控制和失败处理。把控制台操作翻译成脚本只是起点,建立可解释的变更过程才是本课的重点。

可重复也不等于在任何时间、任何账号执行都会得到完全相同的结果。区域可用规格、镜像版本、默认参数和外部依赖都会影响结果。团队应明确输入、固定关键依赖版本,并记录执行环境。对于尚未纳入管理的已有资源,需要先决定是否接管、怎样建立关联;不能仅因配置里出现了相同名称,就假定工具认识那项资源。

本课始终围绕 1 个小型订单服务:公网入口连接私有网络中的 API,API 使用独立运行身份访问数据库,日志与指标进入观测系统。我们关心的是这些资源如何被安全地改变,不展开模板语言、插件协议或复杂编排算法。学完后,你应能读懂变更计划,发现替换与删除风险,并说明某次执行失败后应该先保存什么证据。

2 声明式状态、漂移与变更窗口

声明式配置描述“希望是什么”,而不是逐条规定“先点哪里,再点哪里”。例如,期望 API 有 2 个副本、数据库不接受公网访问、服务身份具有指定权限。执行工具根据配置、已有资源关联信息和读到的远端属性计算动作。声明式工具仍然会执行创建、修改和删除;声明式指的是意图表达方式,并不是操作没有顺序,也不是执行过程天然具有事务性。

理解这件事需要分清 3 份信息:配置是期望状态,状态记录保存资源地址与真实对象的关联及已知属性,远端资源是此刻实际存在的对象。状态记录不是实时监控数据库,也不是订单数据备份。工具读取云接口后,才能在权限与接口能力允许的范围内刷新认识。读不到某项属性、读取存在延迟,都会限制它对现实的判断。

配置、状态记录与远端资源如何形成计划
配置、状态记录与远端资源如何形成计划

漂移是实际资源偏离受管理期望的现象。例如,有人在控制台把 API 的入站范围从入口安全组改成任意来源,代码仍然保留原限制。下次计划可能提出收紧规则。漂移本身不是恶意行为的同义词,也不意味着应立即覆盖:它可能来自应急操作,也可能来自其他自动化。正确处理从解释差异开始,确认意图后再决定恢复代码期望,还是把被接受的变化写回代码。

计划是基于某个时刻观察结果得到的拟议动作,不是对未来的保证。上午生成的计划到了下午,资源、配置、凭据或依赖可能已经变化。即便保存了计划文件,执行时也仍可能遇到接口拒绝与运行环境变化。工具通常会对部分状态变化进行检查,但检查范围取决于实现。因此需要规定计划有效期、重新计划条件,以及生产变更允许发生的窗口。

变更窗口的意义是让执行风险与业务承受能力对齐。把无状态 API 的副本从 2 调到 3,通常与替换数据库实例不是同等风险。窗口内需要有人观察请求错误率、数据库连接与后台任务,并提前确认值班和停止条件。审批通过后若修改了配置、依赖版本或关键变量,就应重新生成并审阅计划,不能继续把旧审批当作当前变化的依据。

还有 1 个常见误解:连续执行后“没有变化”,就证明服务可用。无差异只说明工具在其管理范围和观察能力内没有发现待执行动作;应用可能返回错误,网络连通也可能不满足预期。基础设施收敛检查与业务健康检查应该相邻设置。前者回答资源是否符合配置,后者回答订单能否提交成功,它们的证据不能互相替代。

3 模块、工作区与权限边界

当测试和生产都需要订单 API 时,复制整份配置很快会造成分叉。模块把有明确职责的资源组合成可复用单元,例如“私有 API 运行环境”可以包含服务计算资源、运行身份和必要网络规则。模块对外提供输入与输出,内部保留实现细节。调用者给出副本数量、网络标识、镜像版本,再接收服务入口或运行身份标识,而不必依赖内部每项资源的名字。

模块边界应围绕共同生命周期和责任来画。数据库往往需要长期保存数据,API 计算资源则可能频繁替换。把二者强行放在相同删除边界内,会让日常部署携带数据风险。也不能为了复用,把整个公司所有资源封进 1 个巨型模块,使任何小改动都难以评审。入门阶段可以先区分网络基础、服务运行环境和数据资源,明确它们之间通过哪些输出连接。

模块不是权限隔离机制。模块参数写着“生产”,不代表执行凭据只能接触生产;名称带有“只读”,也不代表真实权限已经受限。需要区分执行身份和运行身份:前者供流水线创建与修改云资源,后者供 API 在运行时访问数据库。执行身份可能拥有资源管理权限,API 的运行身份通常不应拥有这些权限,更不能把执行凭据放入应用容器。

模块复用与环境权限边界
模块复用与环境权限边界

工作区常用于分隔状态或变量上下文,但不同工具的具体含义不同。有的工作区主要切换状态,有的还绑定远端执行设置。无论名称如何,都不能仅凭切换工作区就认定账号、网络、凭据和数据已隔离。生产边界还需要明确目标账号或项目、远端状态位置、执行身份与审批入口,并在执行前输出可核对的环境标识。

状态记录也需要按敏感材料管理。它可能包含连接信息和敏感属性,即使配置输出被标为敏感,也不能据此认为底层状态不保存明文值。访问状态的权限应尽量小,存储要有合适的加密、版本保留和恢复机制。密钥材料优先通过专门的秘密管理方式引用;不要把密码直接提交进代码仓库,也不要把完整计划和状态随意贴进工单。

模块版本应当能追溯。生产引用的模块更新后,需要像应用依赖升级那样审阅差异,而不是随时跟随一个持续变化的地址。共享网络被多个服务使用时,还要明确谁能修改输出契约、谁负责通知下游。资源复用节约的是重复工作,不会消除环境隔离与所有权问题;清楚的负责人和可解释的权限范围,才让模块化真正可维护。

4 动手:对照一次计划与应用差异

下面做 1 次纸面演练,不需要账号,也不调用任何真实云接口。假设订单 API 正常运行,已知资源与状态记录相符,服务有 2 个副本。需求是把副本改成 3,并收紧入口规则。我们拿到的是工具无关的教学配置与计划摘要;其中的资源类型和符号仅用于阅读,不是可以直接交给某个工具执行的配置文件。

期望配置
service.order_api.replicas = 3
network.api_ingress.source = gateway_group
data.order_db.public_access = false

候选计划
~ service.order_api: replicas 2 -> 3
~ network.api_ingress: source any -> gateway_group
-/+ data.order_db: replace, reason = storage_layout_changed

先用需求逐项对照计划。前 2 项看起来符合意图,但第 3 项涉及数据库替换,与本次需求没有对应关系。此时不能只数“总共改变 3 项”,也不能因为配置里仍写着禁止公网访问,就忽略数据库的其他属性。应追查候选配置、变量、模块版本或工具适配层版本是否同时改变,了解该属性为何需要替换,以及现有数据将怎样处理。

本次演练设定,排查发现提交中误带了数据库存储布局参数的修改。移除这项无关修改后,重新生成计划,数据库动作消失。新的计划只包含副本调整和入口规则收紧。审批应指向这个新计划及对应的代码版本,同时记录环境、变量来源和执行身份。已经审阅过旧计划,并不等于新的执行材料自动获得相同结论。

接着模拟应用结果:第 3 个 API 副本创建成功,但网络接口因缺少修改规则的权限而拒绝请求。整体执行返回失败,远端却已经有 3 个副本。这里没有“失败所以什么都没发生”的保证。部分成功的结果还可能受状态写回是否完成影响,不能只根据终端最后 1 行推断状态。应保留执行日志,读取服务实际副本和网络规则,并检查状态存储是否正常。

从计划审阅到部分失败后的再计划
从计划审阅到部分失败后的再计划

把演练结果整理成可比较的证据:计划要求副本为 3、来源为入口组;实际副本已为 3,来源仍是任意来源;执行日志显示网络修改被拒绝。修复执行身份的必要权限后,在相同目标环境重新生成计划。若状态记录与现实关联正确,且没有其他变化,预期剩余动作只有收紧网络规则;若仍出现额外创建或删除,应先解释原因再继续。

动手的核心是分别读懂“希望改变什么”“工具打算怎么做”和“实际完成了什么”。对最终执行,还要验证入口能够访问 API、非预期来源被拒绝,以及订单读写正常。真实使用某款工具前,再按其文档准备后端、配置和凭据;本节的演练已经能训练审阅顺序,却不能代替真实环境中的权限核验和业务验收。

5 可变与不可变基础设施取舍

可变基础设施是在现有对象上修改,例如调整服务副本、更新网络规则,或给长期运行的实例安装新组件。不可变基础设施则倾向于创建符合新版本要求的对象,验证后把流量切过去,再退役旧对象。这里的“不可变”描述发布策略,不是云平台真的禁止修改。生产环境往往混合使用两者:计算资源可以替换,持久数据与身份关联则需要谨慎延续。

可变策略的优点是动作可能较小,短期不必保有完整的新旧两组资源;缺点是长期累积的手工修改容易让实例彼此不同。不可变策略能让镜像与部署版本更清楚,也便于在旧实例仍可用时切回流量,但会增加过渡期资源占用,并要求应用支持健康检查、连接排空和流量切换。创建新实例成功只是开始,新实例还需要具备正确身份与网络访问能力。

可以把订单 API 想成 2 个正常副本,每个副本的资源容量固定。若先增加 1 个新副本、确认健康再退役 1 个旧副本,过渡时可能达到 3 个,资源容量是基线的 1.5 倍。若先创建完整的 2 个新副本再切换,峰值可能达到 4 个,是基线的 2 倍。这里计算的是实例资源容量,既不是账单报价,也没有包含负载入口、数据存储和替换持续时间。

可变更新与替换更新的检查点
可变更新与替换更新的检查点

不可变更新能否降低风险,取决于应用约束。如果旧版与新版对数据库字段的理解不兼容,保留旧实例也未必能回退。若会话只保存在本机内存,切换可能导致用户状态丢失;若后台任务没有去重,重叠运行可能重复处理订单。因此基础设施计划应和应用发布计划对齐,先确认版本兼容与请求处理方式,再选择替换节奏。

数据库更不能照搬计算资源的替换逻辑。创建空数据库、连通 API,然后删除旧数据库,并不等于迁移完成。数据需要备份、复制、校验与写入切换策略,回切时还必须处理新写入的数据。本课只要求识别这一边界:看到有状态资源的替换动作,应停下来要求专门的数据变更设计,不把它当作普通的实例滚动更新。

选择时可以问 3 件事:对象能否随时重建,过渡期有没有额外容量,以及应用能否在新旧版本共存时保持正确。答案决定采用原地修改、逐步替换还是单独维护窗口。没有某种策略适合所有资源;入门阶段最重要的是能够从计划中认出更新和替换,并把它们各自依赖的健康检查与恢复条件写清楚。

6 常见坑:手工改资源制造漂移

某晚订单接口超时,值班工程师在控制台临时放宽了安全组规则,随后服务恢复。第 2 天,自动化执行把规则收回,超时再次出现。表面看是 IaC “覆盖了修复”,实际是团队留下了 2 套互相冲突的意图:配置继续要求原限制,控制台保留临时放宽后的状态,而事件记录没有告诉后续执行者哪项变化仍被需要。

手工修改并非任何时候都不允许。真正的要求是给应急路径规定负责人、作用范围和后续收敛方式。操作前尽量记录原值,操作后记录新值、时间、目标对象和原因,并关联事件。恢复业务后,应确定临时修改是否暴露了设计缺陷:如果 API 本来就依赖另一个受控入口,应补足正确规则;如果只是排查手段,应验证后撤销,而不是永久保留宽泛访问。

发现漂移后,不要直接执行,也不要立刻把远端所有值复制回配置。先确认对象身份,再对比代码、状态和远端读取结果,寻找控制台审计或其他流水线记录。计划中的修改可能是恢复期望,也可能暴露出配置落后于已经批准的需求。只有解释了差异来源,才能选择以代码为准、采纳远端变化,或者暂停相关执行等待业务决策。

漂移发现后的排查与收敛路径
漂移发现后的排查与收敛路径

另一个坑是对所有经常变化的字段统一忽略。例如自动伸缩控制器负责副本数量,IaC 若也不断把副本改回固定值,就会形成多个控制者争抢。可以通过明确所有权,让某个字段由自动伸缩系统管理,并在配置中使用工具支持的相应机制。但忽略字段只会改变工具的比较行为,不会替你监控风险,更不应拿来隐藏公网暴露或权限扩大的差异。

删除状态记录也不是清除漂移的快捷方式。这样可能只让工具失去对真实对象的关联,远端资源仍在收费和提供服务,下次执行却尝试再建。对于已有资源的接管、资源地址调整或状态修复,应使用所选工具支持的流程,准备备份与评审。不要用随意编辑状态文件的方法,把暂时看不懂的差异“消掉”。

漂移治理需要闭环:定期生成只读检查结果,给差异分配负责人,在授权窗口完成收敛,并把结论写回代码或事件记录。检查频率应适合资源的重要性,不能把每天发现差异误认为每天都要自动修复。对于入口规则和运行身份,还应借助独立的安全与访问观测发现异常,因为 IaC 只能比较它能够读取且确实纳管的范围。

7 案例:状态锁丢失导致并发破坏

下面是 1 个教学案例。订单服务的 2 条发布流水线共用同一份生产状态,后端原本支持协作锁。流水线 A 正在更新运行环境,执行较慢。工程师看到锁占用,以为任务已经卡死,在没有确认 A 是否停止的情况下强制解除锁。流水线 B 随后拿到锁并开始执行,A 却仍持有有效云凭据,可以继续向云接口发出请求。

B 的计划基于当时看到的资源生成,准备替换运行身份与部分网络关联;A 则继续按自己的计划修改这些对象。即使后端能拒绝 A 的过期状态写入,也不代表它可以撤销 A 已发出的云请求。案例中,B 更新了 API 的身份绑定,A 又删除了自己认为应退役的旧关联,导致部分副本访问数据库失败。远端实际结果已经不能仅靠任何 1 份计划解释。

状态锁首先用于协调对同一状态的访问,不应被理解成覆盖所有云资源操作的全局事务锁。具体实现是否有租约、续租或额外校验,需要查相应后端文档。即使同一状态的锁工作正常,如果 2 份不同状态同时管理同一远端对象,也可能互相覆盖。因此案例的根因不只是“有人点了强制解锁”,还包括并发控制、资源所有权和任务存活确认缺失。

发生冲突时,先阻止新的相关执行,并确认现有执行者已停止发出变更;必要时暂停流水线并处理仍有效的执行凭据。与此同时,按服务应急方案恢复用户请求。保留状态版本、计划、代码版本、执行标识与云审计记录,核对哪些接口调用真正成功。不要让第 3 次盲目应用扩大影响,也不要仅把旧状态文件覆盖回去,就宣称真实资源已经恢复。

恢复需要重新建立可信关联。逐项核对 API 当前绑定的身份、身份权限、网络关联与实际资源标识,再选择经过审阅的修复方案。若状态写回曾失败,要结合工具提供的恢复机制处理,并保存现有证据。状态文件回到过去只会改变工具的认识,既不会重新生成已删除的资源,也不会恢复数据库里已经改变的数据。

预防措施要落实到入口:同一环境的变更串行进入执行队列;同一资源只由明确的状态与团队管理;锁占用超时先查任务存活与日志;强制解锁必须确认原执行者停止。再为状态启用适合的版本保护与访问审计。锁是必要的协作机制,但它需要正确的使用流程,不能替代部署队列、权限治理和业务恢复准备。

8 把变更与回滚写进基础设施方案

基础设施方案不能只写“采用 IaC,支持自动回滚”。这句话没有说明谁能执行、回到哪个版本、资源替换后数据怎么办。有效的方案应从变更对象开始,列出入口、计算、身份、网络、数据和观测资源的负责人,标明哪些由本份配置管理,哪些是外部依赖,再说明各环境的状态、凭据与审批边界。

每次变更至少应有可追踪的输入与输出。输入包括代码版本、模块与工具依赖版本、变量来源和目标环境;输出包括经过审阅的计划、执行记录以及验收结果。敏感内容应保存在适合的受控位置,审阅记录引用其标识即可。评审重点是创建、更新、替换和删除对服务的影响,尤其关注访问范围扩大、身份权限变化与持久数据处理。

回滚应按对象分别定义。对于无状态 API,回退镜像并把流量切回健康旧副本可能可行;对于原地修改的网络规则,恢复已知原值后还要验证连通性;对于数据库删除或不兼容的数据迁移,恢复代码版本通常无效,需要独立的数据恢复方案。某些情况下,向前修复比退回旧版本更安全,应把判断责任和触发条件写出来。

停止条件也应可观察。例如计划出现未批准的数据库替换就不进入执行;执行中 API 错误率持续超过团队规定阈值,就暂停后续批次并交由值班处理。阈值、持续时间和判断窗口需要结合服务目标确定,不能拿任意示例值当生产标准。执行工具报错、业务指标恶化和观测系统失联,都应有不同的确认与处理路径。

“回退代码再应用”仍然是 1 次新的基础设施变更,需要重新生成计划。旧资源可能已删除,名称可能不能立即复用,权限与配额也可能变化。重新计划后若发现替换、删除或数据风险,就要重新评审,而不能因为提交名叫“回滚”便跳过检查。保留旧资源同样有期限,要规定何时确认稳定、何时退役以及谁负责清理。

最后,方案需要证明恢复路径可用。可以先在隔离环境演练副本更新失败、权限不足或状态写入异常,确认团队能够定位目标对象并恢复服务。生产验收则结合资源差异、访问验证和订单业务检查完成。演练的目标不是制造复杂故障,而是让负责人知道出现部分成功时,怎样停止、取证、再计划,并用证据判断恢复完成。

9 从基础设施即代码走向可观测性(C41)

IaC 为环境提供了可审阅的意图,仍需要可观测性回答运行中的事实。订单 API 的配置可能完全收敛,用户却因为数据库连接池耗尽而无法提交订单;替换实例可能顺利完成,新实例却没有发出日志。下一课将讨论的指标、日志和追踪,正是把资源变化与用户体验连接起来的证据来源。

用变更标记连接执行记录与运行信号
用变更标记连接执行记录与运行信号

首先应让变更能够被定位。执行记录包含环境、代码版本、变更标识、执行起止时间和结果,观测平台上则留下相应的变更标记。出现错误率上升时,团队能把时间窗口与最近的身份、网络或计算变更对齐。时间接近只能提示调查方向,不能单独证明因果;仍需要查看请求路径、错误类型和资源事件,才能判断具体影响。

其次,观测资源本身也可以由 IaC 管理。例如为 API 创建错误率告警、规定日志保留时间、给关键组件配置仪表板。把这些配置纳入代码有助于让新环境具备相同的基本能力,却不能保证数据真的到达。实例上的采集设置、发送权限、网络通路与接收端都可能出问题,因此部署验收需要检查实际指标与日志是否出现。

对订单 API,可以准备相互补充的信号:请求数量与错误比例说明入口表现,响应时间说明用户等待,数据库连接与访问拒绝帮助判断依赖问题,实例健康与重启事件帮助发现替换异常。它们不必都来自相同系统,也不应把“日志里没有错误”直接当作正常。日志可能缺失,低流量时错误比例也可能缺乏代表性,需要结合采集状态与业务探测解释。

观测也能帮助收紧权限。若某次网络调整后只有部分来源失败,应检查来源路径和拒绝记录,而不是立即把所有来源放开。若身份调整后数据库拒绝访问,应对照实际运行身份和授权范围,而不是给执行账号增加无关权限。这样,IaC 提供变化的候选原因,观测提供运行事实,两者共同缩小排查范围。

走向 C41 时,可以带着这个问题:我们如何证明“配置已应用”之后,订单服务确实仍然可用?本课的答案是预留观测接口、记录变更标识、执行业务验收,并承认资源一致性与服务健康是不同判断。下一课再建立更完整的运行证据体系,让团队能够从用户症状出发,逐步定位到应用、依赖或基础设施,而不是只盯着最近修改的配置。

放进方案里

订单 API 的基础设施采用声明式配置管理,范围包含入口、服务计算资源、运行身份、网络规则、数据库外围配置与基础告警。数据库业务数据另设备份和恢复方案。网络、服务与数据资源按生命周期划分管理边界,复用模块固定版本,并通过明确输出连接。测试与生产分别确认目标账号、执行身份和状态位置,工作区名称仅作为环境标识的组成部分。

变更经版本评审进入环境执行队列,同一环境避免并发修改。执行前读取远端属性并生成计划,审批关联代码版本、变量来源、目标环境和计划标识。出现未批准的替换、删除或访问范围扩大时停止。批准后若配置或关键依赖变化,重新计划。状态受访问控制、加密和版本保护,执行凭据与应用运行身份分离,敏感材料不进入普通代码和工单附件。

应用在约定窗口执行,保留动作结果和失败记录。部分成功时先核对远端资源与状态关联,再生成修复计划;锁异常先确认原执行者停止,禁止以强制解锁替代故障调查。紧急手工修改须记录对象、原值、新值和原因,事后决定撤销或纳入配置,避免后续自动化反复覆盖。

无状态服务通过健康检查、连接排空与分批替换控制影响,额外容量按配额核验。数据库替换必须另附数据迁移与恢复设计。恢复优先选择经过验证的回切或向前修复路径,回退代码后仍需重新审阅计划。执行记录向观测平台提供变更标记,以入口访问、订单读写和实际遥测验证结果;资源无差异不能单独作为验收依据。

10 小结:下一步可观测性(C41)

基础设施即代码把资源意图、评审与执行连接起来,但可靠变更还依赖状态、锁、权限和恢复流程。读计划时,要区分更新与替换;看失败时,要考虑部分成功;谈回滚时,要区分代码、资源和业务数据。

进入 C41 前,试着口头回答:配置没有变化,为什么资源仍可能漂移?工作区不同,为什么还要核对执行身份?应用失败,为什么不能直接认定没有资源变化?如果你能分别用远端事实、权限边界和部分执行解释,就已经具备继续学习可观测性的基础。

同阶段相关课

AI 答疑

默认关闭。启用后,只发送本课标题、最近阅读的屏幕内正文摘录(最多 6000 字)与本次问题。

配置自己的服务