State 管理
这一课你将能做到
- 说清楚 state 文件里存了什么、Terraform 为什么离不开它
- 把本地 state 迁移到 S3 + DynamoDB 远程后端,理解锁的工作方式
- 用 import、state mv、state rm、state list 处理资源纳入管理、改名和摘除
为什么要学
State 是 Terraform 的命门,也是初学者最容易惹祸的地方:误删 state 文件,Terraform 下次 apply 会把整套资源「再建一遍」;多人共用本地 state,会互相覆盖;有人离职留下一台控制台手建的老机器,需要 import 纳管;重命名资源块不做 state mv,结果是删旧建新。日常运维工作里,和 state 打交道的频率不亚于写资源本身。这一课全是保命操作。
人话:state 像房产登记簿——房子本身在云上架着,但「这套房子归 Terraform 的哪个配置项管」全靠登记簿证明。簿子丢了或写错了,不是房子没了,而是 Terraform「不认识」房子了,后果往往比房子没了更麻烦。
先建立图像
State 的三大职责:
1. 映射:配置地址 (aws_instance.app) ↔ 真实资源 ID (i-0abc123)
2. 缓存:记录云端属性,减少 plan 时的 API 调用
3. 同步:远程后端 + 锁,保证同一时刻只有一人/一个流水线在改
本地后端 vs 远程后端:
| 维度 | 本地(默认) | S3 远程后端 |
|---|---|---|
| 文件位置 | 目录下 terraform.tfstate | S3 桶中的 key |
| 多人协作 | 不可行 | 天然共享 |
| 并发锁 | 无 | DynamoDB 表锁 |
| 版本历史 | 无 | S3 版本化可回滚 |
| 加密 | 依赖磁盘 | SSE 加密 + 桶策略 |
跟着做
步骤 1:观察本地 state
用上一课的 plan-demo 目录,apply 后执行:
terraform state list
输出当前管理的全部资源地址,如 null_resource.app[0]。查看某个资源的详细状态:
terraform state show null_resource.app[0]
直接看文件:terraform.tfstate 是 JSON,里面能找到资源 ID、属性、依赖关系。注意它默认是明文,包含数据库地址等敏感信息,所以团队场景必须上远程后端。
步骤 2:准备 S3 后端底座
先手动(或用另一个 Terraform 配置)创建:一个开启版本化和加密的 S3 桶、一张 DynamoDB 表(主键名为 LockID,类型 String)。
用 AWS CLI 示例:
aws s3api create-bucket --bucket my-tfstate-12345 --region ap-northeast-1 \
--create-bucket-configuration LocationConstraint=ap-northeast-1
aws s3api put-bucket-versioning --bucket my-tfstate-12345 \
--versioning-configuration Status=Enabled
aws dynamodb create-table --table-name tf-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
桶名必须全局唯一,换成你自己的。
步骤 3:配置远程后端并迁移
在现有配置中新建 backend.tf:
terraform {
backend "s3" {
bucket = "my-tfstate-12345"
key = "plan-demo/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "tf-locks"
encrypt = true
}
}
执行:
terraform init
Terraform 检测到后端从 local 变为 s3,会提示:
Do you want to copy existing state to the new backend?
输入 yes,本地 state 被上传到 S3,之后本地不再保留权威副本。去 S3 控制台能看到该 key,桶版本历史里有第一个版本。
步骤 4:观察锁
保持一个 apply 正在运行时,另开终端再跑 apply,会得到类似:
Error: Error acquiring the state lock
... Lock ID: 一串 UUID
这就是 DynamoDB 锁在防止并发写。操作正常结束后锁自动释放。确认持锁进程已经死掉(比如流水线任务被删除)才能:
terraform force-unlock <锁ID>
别在操作正常运行时强解,否则 state 可能被写坏。
步骤 5:import 纳管已有资源
场景:一台控制台手工创建的 EC2(i-0abcd1234)要纳入 Terraform。
Terraform 1.7 的 import 有两种形式。先看经典形式——先在配置里写一个空壳资源:
resource "aws_instance" "legacy" {}
再执行:
terraform import aws_instance.legacy i-0abcd1234
成功后 state 中出现该资源,随后 terraform plan 会提示你「配置参数缺失,需要补齐」——按 plan 提示把属性补进资源块,直到 plan 无差异为止。
Terraform 1.7 还支持声明式 import 块(推荐,可进 CI):
import {
to = aws_instance.legacy
id = "i-0abcd1234"
}
plan 阶段即可预览导入结果,apply 时完成导入,之后删掉 import 块。
步骤 6:state mv 重命名
把资源本地名从 legacy 改为 app:
resource "aws_instance" "app" { ... }
改代码同时迁移 state:
terraform state mv aws_instance.legacy aws_instance.app
这样 plan 不会显示「删 legacy、建 app」,资源原地保留。跨模块迁移同样用 mv,写法为 module.old.aws_instance.x → module.new.aws_instance.x。
步骤 7:state rm 摘除管理
只想让 Terraform「别再管这个资源」,但保留云端对象:
terraform state rm aws_s3_bucket.temp
资源从登记簿消失,云端对象不受影响;之后可手工删除或交给别人。rm 不删资源,destroy 才删资源——两者绝不能混。
讲清楚要点
- 备份 state 再动手:操作 state 前先
terraform state pull > backup.tfstate存一份,出问题能回推。 - S3 后端支持多环境分 key:如
prod/terraform.tfstate、staging/terraform.tfstate,或干脆每个环境一个独立配置目录/桶前缀。 - 敏感数据会进 state:数据库密码、用户数据都可能明文存在 state 中,桶策略要收紧、日志里别贴 state 全文。
- state 操作只改登记簿:mv/rm 都不碰真实资源;它们解决「配置地址」和「资源 ID」的对应问题。
- workspace 不是多环境银弹:workspace 能在同一后端隔离 state(state 路径带 env 前缀),适合临时/小差异环境;强隔离的生产环境更推荐独立目录或独立后端,第 10 课再细讲。
你可能会懵的地方
-
state 文件丢了怎么办? 远程后端靠 S3 版本化恢复;本地丢失只能靠资源 ID 逐个 import 重建映射。预防比补救便宜,这也是必须上远程后端的原因。
-
为什么 plan 说资源不存在但我在控制台看得到? 那是没被 state 登记的资源,Terraform 不认识它。需要 import,或者它本来就该由别的系统管理。
-
import 之后 plan 为什么还有一堆要改? import 只搬「身份映射」,你配置里的参数和云端实际属性还没对齐。按 plan 逐项补齐配置是 import 的必要后半程。
-
能让 state 里的密码不明文吗? 没有可靠的字段级加密方案。正确做法是限制谁能读 state 桶,并尽量把密钥放到 Secrets Manager,在资源里引用而非写入。
-
terraform state pull有什么用? 手工查看远程 state、做备份、或排查流水线问题。pull 是只读获取,想推回要用 push(极少需要,风险高)。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 直接删 state 文件「重来」 | apply 时重复创建全套资源 | state rm 精确摘除,或先备份 |
| 改本地名不做 state mv | 资源被删旧建新 | 改名 + terraform state mv |
| 控制台手建资源不 import | Terraform 视野外的野资源 | import 纳管并补齐配置 |
| S3 桶不开版本化 | state 写坏无法回滚 | 创建桶时开启 versioning + 加密 |
| 不确认就 force-unlock | 并发写坏 state | 确认持锁进程已终止再解 |
卡点急救
- state lock 一直不释放:先查 DynamoDB 表里的锁记录和持锁人信息,确认对应流水线/进程已结束,再 force-unlock。
- init 迁移后端失败中断:修复后端参数(桶名、权限)后重跑 init,它会再次询问迁移;必要时用
-reconfigure或-migrate-state。 - state 疑似损坏:用
terraform state pull导出,对比备份;无法修复时从最近的 S3 版本恢复,再 plan 验证。
面试常问
-
Terraform state 的作用?为什么推荐远程 state? state 维护配置地址与真实资源 ID 的映射、缓存属性、支撑协作同步。远程后端提供共享存储、加密、版本历史和并发锁,解决本地 state 无法协作、易丢失、易泄露的问题。
-
如何把一台手工创建的机器纳入 Terraform 管理? 写资源块,用 terraform import(或 import 块)把云资源 ID 关联到配置地址,然后按 plan 输出补齐参数直到无差异,此后变更全部走代码。
-
state mv 和 state rm 的区别?分别在什么场景用? mv 改变资源在登记簿中的地址(重命名/跨模块),真实资源不动;rm 把资源从登记簿摘除(不再管理),云端对象保留。两者都不会删除真实资源。
自测三问
- 为什么误删 state 不等于资源被销毁?它会造成什么实际后果?
- S3 后端中 DynamoDB 表扮演什么角色?不开它会怎样?
- import 一台 EC2 后,还差哪一步才算「真正纳管」?
小结一句话
state 是配置与现实之间的登记簿:上 S3+DynamoDB 远程后端解决共享与锁,import 纳管存量、mv 改名、rm 摘管,动手前先 pull 备份。
延伸
- State 官方文档
- S3 Backend 配置参考
- Import 块与 import 命令
- 下一课:变量、输出与 tfvars——让配置在多环境间复用。