3. Terraform 状态管理:本地状态 vs 远程状态、状态锁、状态迁移实战
好,咱们今天聊聊 Terraform 的状态管理。说实话,这玩意儿是 Terraform 的「命根子」。你想想看,Terraform 怎么知道你上次创建了哪些资源?怎么知道哪些要改、哪些要删?全靠这个状态文件。
我刚开始用 Terraform 的时候,觉得状态文件就是个记录,没啥大不了的。直到有一次,团队里两个人同时跑 terraform apply,结果资源乱成一锅粥。嗯,从那以后,我再也不敢轻视状态管理了。
3.1 本地状态:简单但危险
默认情况下,Terraform 把状态存在本地,就是一个 terraform.tfstate 文件。这文件长啥样?说白了就是个 JSON,记录了所有资源的属性。
{
"version": 4,
"terraform_version": "1.5.0",
"resources": [
{
"mode": "managed",
"type": "aws_instance",
"name": "web",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"attributes": {
"id": "i-0a1b2c3d4e5f67890",
"ami": "ami-0c55b159cbfafe1f0",
"instance_type": "t2.micro"
}
}
]
}
]
}
本地状态的好处是简单,一个人玩完全够用。但问题也很明显:
- 共享困难:你没法让同事直接拿到你的状态文件
- 容易丢失:本地硬盘坏了,状态就没了
- 并发冲突:两个人同时操作,状态就乱了
terraform.tfstate 提交到 Git 仓库!里面可能包含敏感信息,比如数据库密码、密钥对等。我见过有人把 AWS 密钥直接写在状态文件里,然后 push 到公开仓库...后果你懂的。
3.2 远程状态:团队协作的基石
为了解决本地状态的问题,Terraform 支持远程状态存储。说白了,就是把状态文件放到一个共享的地方,比如 S3、Azure Storage、Consul 等。
我个人最常用的是 S3 + DynamoDB 的组合。S3 存状态文件,DynamoDB 做状态锁。为什么这么配?因为 S3 便宜、可靠,DynamoDB 能保证原子操作。
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "prod/network/terraform.tfstate"
region = "us-west-2"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}
配置好之后,每次 terraform apply 都会先加锁,操作完再释放。这样就不会出现两个人同时改状态的问题了。
3.3 状态锁:防止并发冲突
状态锁是远程状态的核心功能。它的作用很简单:同一时间只允许一个人操作状态。
为什么会需要锁?你想想看,两个人同时跑 terraform apply,都读取了同一个状态文件,然后各自修改,最后写回去。后写的人会覆盖先写的人的操作,导致资源状态不一致。
状态锁的实现方式取决于后端:
| 后端类型 | 锁机制 | 特点 |
|---|---|---|
| S3 + DynamoDB | DynamoDB 条件写入 | 强一致性,支持自动解锁 |
| Azure Storage | Blob 租约 | 内置锁,无需额外组件 |
| Consul | Session 锁 | 支持健康检查,自动释放 |
| HTTP | 自定义锁 | 需要自己实现锁逻辑 |
terraform apply 跑了很久,锁一直没释放,导致其他操作全部阻塞。后来我设置了合理的锁超时时间,并加了手动解锁的脚本。
3.4 状态迁移实战
状态迁移,说白了就是把状态文件从一个地方搬到另一个地方。比如从本地搬到远程,或者从一个 S3 桶搬到另一个 S3 桶。
迁移的步骤其实很简单:
- 备份当前状态:先把
terraform.tfstate复制一份 - 修改 backend 配置:在代码里改
terraform { backend "..." } - 执行 init:
terraform init -migrate-state - 确认迁移:Terraform 会提示你是否要复制状态文件
# 从本地迁移到 S3
terraform init -migrate-state
# 从 S3 迁移到另一个 S3
terraform init -migrate-state -backend-config="bucket=new-bucket"
这里有个坑要注意:迁移过程中不要执行任何 apply 或 plan 操作。我建议在迁移前先锁住状态,防止别人操作。
3.5 状态管理的避坑指南
做了这么多年运维,我总结了几条状态管理的经验:
- 永远不要手动修改状态文件:除非你真的知道自己在做什么。手动改状态文件很容易导致 Terraform 和实际资源不一致。
- 定期备份状态:远程存储虽然可靠,但也要定期备份到其他地方。我习惯每周把状态文件下载到本地加密存储。
- 使用状态隔离:不同环境(dev、staging、prod)用不同的状态文件。这样不会互相影响。
- 监控状态锁:如果发现锁长时间未释放,要能及时发现并处理。
terraform plan,看看 Terraform 要做什么。如果发现计划里要删除重要资源,赶紧停下来检查状态文件是不是有问题。
好了,状态管理这块就聊这么多。记住一句话:状态文件是 Terraform 的「记忆」,保护好它,你的基础设施才能稳定运行。下一章咱们聊聊 Terraform 模块化设计,到时候见。