3.2 基础设施即代码


文档摘要

3.2 基础设施即代码 本节摘要:基础设施即代码(IaC)把服务器、网络、数据库等环境资源用声明式代码描述,进版本库、走评审、由工具自动创建和变更。它解决三个老问题:环境靠手工搭建导致的不可复现、变更无记录导致的不可审计、灾难后无法快速重建。本节讲声明式思想、代码化工作流、不可变基础设施,以及状态漂移的治理。 读完这节你能回答 解释声明式与命令式基础设施管理的本质区别 走完一次"改代码→评审→计划→应用"的 IaC 变更流程 说明不可变基础设施为什么比"在线修补"更可靠 检测并治理环境的状态漂移 一、手搓环境的三宗罪 先看没有 IaC 的世界。某团队要搭一套新的预发环境,资深运维工程师老王凭记忆和一份过时的 Wiki 操作:装系统、配网络、改内核参数、建库建账号。两天后环境"看起来能用了"。

3.2 基础设施即代码

本节摘要:基础设施即代码(IaC)把服务器、网络、数据库等环境资源用声明式代码描述,进版本库、走评审、由工具自动创建和变更。它解决三个老问题:环境靠手工搭建导致的不可复现、变更无记录导致的不可审计、灾难后无法快速重建。本节讲声明式思想、代码化工作流、不可变基础设施,以及状态漂移的治理。

读完这节你能回答

  1. 解释声明式与命令式基础设施管理的本质区别
  2. 走完一次"改代码→评审→计划→应用"的 IaC 变更流程
  3. 说明不可变基础设施为什么比"在线修补"更可靠
  4. 检测并治理环境的状态漂移

一、手搓环境的三宗罪

先看没有 IaC 的世界。某团队要搭一套新的预发环境,资深运维工程师老王凭记忆和一份过时的 Wiki 操作:装系统、配网络、改内核参数、建库建账号。两天后环境"看起来能用了"。三个月后该环境需要扩容,老王休假,接手的工程师照着 Wiki 重搭,跑不起来——Wiki 没记老王临时改的那三个参数。半年后生产出事故,审计要求说明"这台服务器的防火墙规则是谁在什么时候改成这样的",无人能答。一年后机房故障需要整体重建,估算重建时间:三周。

这就是手搓环境的三宗罪:不可复现(没人能搭出一模一样的第二套)、不可审计(变更历史只存在于人的记忆里)、不可重建(灾难恢复时间以周计)。

IaC 的处方:环境的一切用代码描述。要第二套环境?再执行一遍代码。要知道谁改了防火墙?查版本库的提交历史。机房全毁?在新机房执行代码库,以小时计恢复。代码化把环境从"手工艺品"变成"工业品"。

二、声明式:说出"要什么",不指挥"怎么做"

IaC 有两种写法,差别深远。

命令式:一步步指挥机器。"创建一个虚拟机、给它绑定一块盘、在盘上装数据库、执行初始化脚本。"工具照做,但如果中间某步失败,重跑会怎样?可能创建出第二个虚拟机(重复执行不可幂等)。一年后脚本膨胀到两千行,没人敢动。

声明式:只描述期望的终态。"应存在一个 4 核 8G 的虚拟机,绑定 200G 存储,运行 16 版数据库,网络放行 5432 端口。"工具负责对比现状与期望,算出差异并执行——已经是终态就不动,有差异就补齐。声明式天然幂等:同一份描述执行一百遍,结果一致。

# 声明式基础设施代码片段:一个带数据库的预发环境 resource "compute_instance" "staging_app" { name = "staging-app-01" machine_type = "4c-8g" image = "app-base-v3.2" # 不可变镜像,见后文 network_interface { subnet = "staging-subnet" tags = ["staging", "app"] } } resource "managed_db" "staging_pg" { engine_version = "16" instance_size = "2c-4g" storage_gb = 200 backup_window = "03:00-04:00" allow_cidr = ["10.20.0.0/16"] # 只放行预发网段 } output "db_endpoint" { value = managed_db.staging_pg.endpoint # 端点自动注入应用配置 }

注意最后一处细节:数据库的连接端点通过输出变量自动注入应用配置——环境之间的差异信息由 IaC 工具在创建时自动流转,而不是靠工程师手工抄写。手工抄写正是配置错误的温床。

三、IaC 的工作流:像改业务代码一样改环境

代码化之后,环境变更的流程与业务代码完全同构,这是 IaC 最深刻的变化。

第一步,改代码。工程师修改描述文件,比如把预发库从 2 核升到 4 核。第二步,提交评审。合并请求里能看到这次变更的意图与范围,同事评审。第三步,计划预览。工具算出差异并展示:"将修改 1 个资源:managed_db.staging_pg 的 instance_size 从 2c-4g 变为 4c-8g。"这一步的价值巨大——变更在执行前就被完整预见,不会出现"脚本跑一半才发现删错了东西"。第四步,应用。确认计划无误后执行。第五步,状态入库。工具把"当前真实状态"记录到状态文件,供下次对比。

计划预览输出示例 ────────────────────────────────────────────── 计划执行操作: ~ managed_db.staging_pg instance_size: "2c-4g" → "4c-8g" ~ compute_instance.staging_app machine_type: "4c-8g" → "8c-16g" 计划:0 新增,2 修改,0 删除。 ────────────────────────────────────────────── 确认后应用:2 项变更完成,耗时 4 分 12 秒。

一个容易被忽视的坑:状态文件本身是关键资产。它记录了代码与现实之间的对应关系,丢失或损坏意味着工具"失忆"——明明存在的资源被当作不存在。状态要远端集中存储、加锁(防止两人同时应用互相覆盖)、纳入备份。

四、不可变基础设施:别修,换掉

IaC 解决了环境的"创建",还有一类问题来自"运行中的改动"。服务器在线跑了两年,期间打过几十个补丁、改过若干配置、删过几个临时文件——它早已不是当初创建它的那个样子。这样的服务器被称为"雪花服务器":独一无二、脆弱、谁也不敢动。

不可变基础设施的立场是:运行中的实例不做任何在线修改,要变更就换新实例。新版本发布不是"登录 40 台服务器逐台更新",而是"用新镜像启动 40 台新实例、切流量、销毁旧的"。任何差异都体现为新镜像,而镜像是由流水线从代码构建的、带版本号的制品。

这个模式与第 2 章的 CI 衔接得严丝合缝:代码提交 → 构建出镜像制品 → IaC 把镜像部署为实例。整个链条里没有任何"登录服务器手工做点什么"的环节,也就消除了"悄悄改"的可能。容器技术之所以成为云原生的基石,正是因为它把"不可变"做得最彻底——容器镜像就是打包好的文件系统快照,运行时只读。

03-02-fig01

五、状态漂移:现实的悄悄偏离

即使全面代码化,现实仍会漂移:某次紧急故障中工程师直接在控制台改了安全组(当时是合理的),某次云厂商系统维护重置了某个参数,某个测试脚本误操作改了实例规格。代码说 A,现实是 B,这就是状态漂移。漂移积累的终点,是代码描述与真实环境彻底脱节——代码沦为装饰。

治理漂移靠三件套。定期对账:调度任务每日对比代码声明与实际状态,输出漂移报告。修复路径收敛:紧急情况下的手工变更被允许(救火优先),但事后必须在 24 小时内把变更补录进代码,形成"手工救急、代码收口"的惯例。权限收口:生产资源的控制台修改权限逐步收紧,把"改环境"的自然入口引导到代码流程。

每日漂移检测报告(节选) ────────────────────────────────────────────── 资源 代码声明 实际状态 漂移原因 sg-staging-01 放行 3 端口 放行 5 端口 8月2日紧急排障手工放行 staging-app-03 4c-8g 8c-8g 压测前临时升配未回退 ────────────────────────────────────────────── 处置:sg → 评审后补录规则入库;升配 → 确认保留并更新声明

💡 关键直觉:IaC 的本质不是"用工具建机器",而是"给环境一个唯一可信的事实来源"。所有环境问题最终都是事实来源分裂的问题。

本节要点回顾

  • 三宗罪:手搓环境不可复现、不可审计、不可重建;代码化把环境从手工艺品变成工业品
  • 声明式优先:描述终态而非过程,天然幂等;端点等信息由工具自动流转,消灭手工抄写
  • 工作流同构:改环境=改代码=评审+计划预览+应用;计划预览让变更在执行前被完整预见
  • 状态文件是资产:远端集中、加锁、备份,丢失即失忆
  • 不可变基础设施:不在线修补,变更即换镜像换实例;容器是它的极致形态
  • 漂移治理三件套:每日对账、手工救急后代码收口、权限收口

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U