plan 与 apply
这一课你将能做到
- 看懂 plan 输出中的
+、-、~、-/+和资源数量汇总 - 用
-out保存计划、用show审查、把计划原样 apply,杜绝「计划和执行不一致」 - 说出 apply 的执行顺序逻辑,并用 target、replace 处理日常特殊场景
为什么要学
plan 是 Terraform 最有价值的设计:在动任何真实资源之前,先给你一份「变更说明书」。线上事故里,有相当一部分不是 Terraform 本身的问题,而是人没认真看计划:plan 里明明写着要销毁数据库,有人一路回车。学会读 plan、保存 plan、按计划执行,是区分「会用 Terraform」和「敢在生产敲 apply」的分水岭。
人话:plan 像手术前的知情同意书——医生告诉你切哪、缝哪、风险是什么;你不看就签字,出了事不能怪手术刀。
先建立图像
plan 中四种标记:
+ create 新建资源
~ update 原地修改部分属性
- destroy 删除资源
-/+ replace 删除后重建(或先建后删,取决于参数)
一次变更的汇总行示例:
Plan: 2 to add, 1 to change, 1 to destroy.
执行顺序不是文件顺序,而是 Terraform 根据引用关系画出的依赖图:被依赖的先建(VPC → 子网 → EC2),删除时反过来。
跟着做
步骤 1:准备可反复演练的配置
新建 plan-demo/main.tf:
terraform {
required_version = ">= 1.7.0"
}
variable "instance_count" {
type = number
default = 1
}
resource "null_resource" "app" {
count = var.instance_count
triggers = {
index = count.index
role = "springboot-app"
}
}
执行 terraform init。
步骤 2:第一次 plan
terraform plan
输出片段:
Terraform used the selected providers to generate the following execution plan.
...
# null_resource.app[0] will be created
+ resource "null_resource" "app" {
+ id = (known after apply)
+ triggers = {
+ index = 0
+ role = "springboot-app"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
(known after apply) 表示这个值要等云端创建后才知道,比如 IP、ID。
步骤 3:保存计划并审查
terraform plan -out=tfplan
terraform show tfplan
-out 把计划写成二进制文件,里面锁定了当时的配置、状态和参数。之后 apply 这个文件,执行的内容和你看到的完全一致,不会被中途的代码改动影响。CI/CD 的标准姿势就是「一个 job 生成 plan 供评审,下一个 job apply 该文件」。
步骤 4:按计划执行
terraform apply tfplan
注意:apply 一个保存好的计划文件时不会再问 yes(审批在 plan 环节已经完成)。直接 terraform apply 不带参数则会现场重新算一次计划并要求确认——这存在「显示的计划」和「执行的计划」之间配置被改动的风险。
步骤 5:体验 update 与 replace
修改配置,把 role 改掉:
triggers = {
index = count.index
role = "springboot-web"
}
再 plan:
~ triggers = {
~ role = "springboot-app" -> "springboot-web"
}
Plan: 0 to add, 1 to change, 0 to destroy.
~ 表示原地更新。AWS 中有些属性不可变(如 EC2 的 AMI、子网的 CIDR),修改它们会显示 -/+,即销毁重建——这是看 plan 时最需要警惕的信号。
步骤 6:定向操作与强制替换
只想操作某一个资源(应急用,别当日常):
terraform plan -target=null_resource.app[0]
terraform apply -target=null_resource.app[0]
怀疑某个资源在云端状态异常,想强制重建:
terraform plan -replace=null_resource.app[0]
-replace 是 Terraform 1.7 推荐写法(等价旧的 taint 思路),只影响下一次计划。
步骤 7:销毁计划也要审
terraform plan -destroy -out=destroy.plan
terraform show destroy.plan
terraform apply destroy.plan
养成习惯:destroy 前同样先出计划,确认销毁清单里没有「还要留着」的资源(比如忘了加 lifecycle 保护的数据库)。
讲清楚要点
- plan 是只读的:它刷新真实资源状态、计算差异,但绝不修改任何东西。可以随时跑、放心跑。
- 「计划即执行」要靠 -out 保证:人肉看的是终端输出,apply 现场重算可能不同;用 plan 文件把两者绑定。
- 颜色和符号对应:终端里绿色新增、黄色修改、红色删除。色盲或日志场景看
+ ~ -字符即可。 lifecycle规则能防误删:资源块内加lifecycle { prevent_destroy = true },任何计划销毁该资源的操作直接报错,数据库等关键资源建议加。- -target 是逃生舱不是常规手段:频繁 target 会让配置整体状态和你的心智模型脱节,用完要尽快做一次完整 plan 收敛。
- apply 失败可以安全重试:部分成功后 state 已记录完成项,重新 apply 会从断点继续,不会重复创建已完成资源。
你可能会懵的地方
-
plan 里出现
(known after apply)正常吗? 正常。资源 ID、自动分配的 IP、RDS 端点这些都是创建后才生成的。但如果下游硬依赖它,会看到一连串 known after apply,属于预期行为。 -
为什么我改了一行,plan 却显示要重建资源? 那个属性对云平台是不可变的(ForceNew)。plan 的
-/+行就是答案;真要避免停机,考虑蓝绿/先建后删(create_before_destroy)。 -
apply 到一半失败了怎么办? 读错误信息修好问题(权限、配额、参数),直接重新 apply。Terraform 按依赖图继续,已完成的资源不会重来。
-
plan 结果会过期吗? plan 文件不感知之后的云端变化。生产上 plan 和 apply 间隔不宜过久,apply 前可重新确认;CI 里通常 plan 审批后立即执行。
-
terraform apply -auto-approve什么时候能用? 临时/测试环境的自动化流水线可以;任何涉及生产数据的 apply 都应保留人工审批环节。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 只看 Plan 汇总行,不看明细 | 汇总 0/1/0 里也可能换了数据库 | 逐个查看 ~ 和 -/+ 的属性 |
人肉看 plan,却 terraform apply 重算 | 中途配置被改,执行了别的内容 | plan -out 后 apply 计划文件 |
| 用 -target 做日常发布 | 其余资源长期不收敛 | target 只用于应急,随后全量 apply |
| 数据库不加保护就 destroy | 数据永久丢失 | lifecycle.prevent_destroy 或分离策略 |
| apply 失败后手工去控制台补 | 状态与现实进一步分裂 | 修配置/权限后重跑 apply |
卡点急救
- plan 报凭证/权限错误:plan 也会调用云 API 刷新状态。检查环境变量、
aws sts get-caller-identity确认身份,核对 IAM 权限。 - apply 卡住无输出:可能在等锁(远程 state 被占用)或云 API 慢。看是否有 lock 信息,必要时
terraform force-unlock <锁ID>(确认持锁操作真的已死)。 Saved plan is stale:计划生成后配置或 state 已变化,重新 plan 出新文件即可,不要强行执行旧文件。
面试常问
-
Terraform plan 做了什么?为什么生产环境一定要先 plan? 刷新资源真实状态,与配置和 state 对比生成增删改计划,供人工评审。它把「变更」前置成可审计的文本,是防止误操作(尤其是销毁和重建)的关键关卡。
-
如何保证审批的计划和最终执行的是同一份?
terraform plan -out保存计划文件,评审后terraform apply 该文件;文件内含状态快照,不可被中途改动,CI 中还可配合指纹/加密存储防篡改。 -
什么是 tainted / replace?-target 有什么风险? 标记资源为「下次替换」会让 Terraform 销毁重建它(1.7 用
-replace)。-target 只对部分资源操作,会造成整体状态长期偏离配置,只适合应急排障。
自测三问
- plan 中的
~和-/+有什么本质区别?对线上服务的影响分别是什么? - 为什么推荐
plan -out+apply 文件,而不是 apply 直接确认? - EC2 的实例类型能原地修改吗?修改 AMI 为什么会触发重建?
小结一句话
plan 是变更说明书:看懂 +/−/~ 和重建信号,用 -out 锁定计划再 apply,是在生产安全使用 Terraform 的核心纪律。
延伸
- Command: plan 官方文档
- Command: apply 官方文档
- Resource Lifecycle 生命周期规则
- 下一课:State 管理——本地状态、远程后端、锁、import 与 move。