状态管理详解
什么是 Terraform 状态
Terraform 状态(State)是 Terraform 跟踪管理资源的数据文件,记录了配置文件与真实基础设施的映射关系。
状态文件的作用
┌──────────────────┐
│ 配置文件 (.tf) │
│ │
│ resource "..." {│
│ ... │
│ } │
└────────┬─────────┘
│
│ terraform apply
▼
┌──────────────────┐ ┌──────────────────┐
│ 状态文件 │<─────>│ 真实基础设施 │
│ terraform.tfstate│ │ │
│ │ │ 云平台资源 │
│ { │ │ (AWS/Azure/...) │
│ "resources": [│ │ │
│ { │ └──────────────────┘
│ "id": "..." │
│ } │
│ ] │
│ } │
└────────────────┘
核心功能:
- 资源映射:配置文件中的资源对应到真实 ID
- 元数据存储:依赖关系、资源属性
- 性能优化:避免每次都查询云 API
- 协作基础:团队共享状态,协同工作
本地状态文件
默认行为
# 执行 terraform apply 后
ls -la
# .
# ├── main.tf
# ├── terraform.tfstate # 当前状态
# └── terraform.tfstate.backup # 上一次状态
terraform.tfstate 结构:
{
"version": 4,
"terraform_version": "1.6.6",
"serial": 3,
"lineage": "abc123-def456-...",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_instance",
"name": "web",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"id": "i-1234567890abcdef0",
"ami": "ami-0c55b159cbfafe1f0",
"instance_type": "t2.micro",
"public_ip": "54.123.45.67"
}
}
]
}
]
}
状态文件命令
# 查看状态
terraform show
# 列出资源
terraform state list
# 查看特定资源
terraform state show aws_instance.web
# 刷新状态(同步真实状态)
terraform refresh
远程状态
为什么使用远程状态
本地状态的问题:
- ❌ 单人工作,无法协作
- ❌ 状态文件容易丢失
- ❌ 并发修改会冲突
- ❌ 敏感信息存储不安全
远程状态的优势:
- ✅ 团队协作
- ✅ 状态锁定,防止并发冲突
- ✅ 自动备份
- ✅ 加密存储
- ✅ 版本历史
S3 后端(推荐)
配置 S3 后端:
# backend.tf
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks" # 状态锁定
}
}
创建 S3 Bucket 和 DynamoDB 表:
# 一次性设置(在其他目录运行)
resource "aws_s3_bucket" "terraform_state" {
bucket = "my-terraform-state"
lifecycle {
prevent_destroy = true
}
tags = {
Name = "Terraform State"
}
}
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_public_access_block" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# DynamoDB 表用于状态锁定
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = {
Name = "Terraform State Lock"
}
}
迁移到远程状态:
# 1. 添加 backend 配置到 main.tf 或 backend.tf
# 2. 重新初始化
terraform init
# Terraform will ask:
# Do you want to copy existing state to the new backend?
# Enter "yes"
# 3. 验证
terraform state list
# 4. 删除本地状态文件(可选)
rm terraform.tfstate
rm terraform.tfstate.backup
其他后端选项
Azure Storage:
terraform {
backend "azurerm" {
resource_group_name = "terraform-state-rg"
storage_account_name = "tfstate"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}
}
Google Cloud Storage:
terraform {
backend "gcs" {
bucket = "my-terraform-state"
prefix = "prod"
}
}
Terraform Cloud:
terraform {
cloud {
organization = "my-org"
workspaces {
name = "my-workspace"
}
}
}
Consul:
terraform {
backend "consul" {
address = "consul.example.com:8500"
path = "terraform/state"
}
}
状态锁定
为什么需要锁定
防止多人同时修改基础设施:
User A: terraform apply
│
├── 获取状态锁 ✅
├── 读取状态
├── 计划变更
│
User B: terraform apply
│
└── 尝试获取锁 ❌ (被 User A 持有)
等待...
User A:
├── 应用变更
├── 更新状态
└── 释放锁 ✅
User B:
└── 获取锁 ✅
继续执行...
S3 + DynamoDB 锁定
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks" # 锁定表
encrypt = true
}
}
锁定流程:
- Terraform 尝试在 DynamoDB 表中创建锁记录
- 如果锁已存在,等待或失败
- 操作完成后,删除锁记录
强制解锁
# 如果锁异常(如进程崩溃)
terraform force-unlock <LOCK_ID>
# 获取 LOCK_ID
# Error: Error acquiring the state lock
# Lock Info:
# ID: abc123-def456-...
# Path: ...
# Operation: OperationTypeApply
# Who: user@host
# Version: 1.6.6
# Created: 2024-01-09 10:00:00
⚠️ 谨慎使用:确保没有其他 Terraform 进程在运行。
状态操作
查看状态
# 完整状态
terraform show
# 列出所有资源
terraform state list
# 查看特定资源
terraform state show aws_instance.web
# 输出 JSON 格式
terraform show -json > state.json
移动资源
# 重命名资源
terraform state mv aws_instance.example aws_instance.web
# 移动到模块
terraform state mv aws_instance.web module.web.aws_instance.server
# 移动模块
terraform state mv module.old_module module.new_module
删除资源
# 从状态中删除(不删除真实资源)
terraform state rm aws_instance.old
# 删除整个模块
terraform state rm module.old_module
导入资源
# 导入现有资源
terraform import aws_instance.web i-1234567890abcdef0
# 导入到模块
terraform import module.web.aws_instance.server i-1234567890abcdef0
替换资源
# 强制重建资源
terraform apply -replace=aws_instance.web
# 标记污染(下次 apply 时重建)
terraform taint aws_instance.web
terraform apply
# 取消污染
terraform untaint aws_instance.web
工作空间(Workspace)
工作空间允许在同一配置下管理多个状态文件。
基本操作
# 列出工作空间
terraform workspace list
# 创建新工作空间
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# 切换工作空间
terraform workspace select dev
# 查看当前工作空间
terraform workspace show
# 删除工作空间
terraform workspace delete dev
使用场景
# 根据工作空间设置不同配置
locals {
env = terraform.workspace
instance_type = {
default = "t2.micro"
dev = "t2.micro"
staging = "t2.small"
prod = "t2.large"
}
}
resource "aws_instance" "web" {
ami = "ami-xxx"
instance_type = local.instance_type[local.env]
tags = {
Name = "web-${local.env}"
Environment = local.env
}
}
状态文件位置:
# 本地状态
terraform.tfstate.d/
├── dev/
│ └── terraform.tfstate
├── staging/
│ └── terraform.tfstate
└── prod/
└── terraform.tfstate
# S3 后端
# key = "env:/${terraform.workspace}/terraform.tfstate"
工作流程
# 开发环境
terraform workspace select dev
terraform apply
# 预发布环境
terraform workspace select staging
terraform apply
# 生产环境
terraform workspace select prod
terraform apply
状态安全
1. 加密存储
S3 加密:
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "terraform.tfstate"
region = "us-east-1"
encrypt = true # 启用加密
kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/xxx"
}
}
2. 访问控制
S3 Bucket 策略:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/TerraformRole"
},
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-terraform-state/*"
}
]
}
3. 版本控制
启用 S3 版本控制:
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
恢复旧版本:
# 列出版本
aws s3api list-object-versions \
--bucket my-terraform-state \
--prefix terraform.tfstate
# 恢复版本
aws s3api copy-object \
--bucket my-terraform-state \
--copy-source my-terraform-state/terraform.tfstate?versionId=VERSION_ID \
--key terraform.tfstate
4. 敏感数据处理
# ❌ 避免在状态中存储敏感数据
resource "aws_db_instance" "database" {
password = "hardcoded-password" # 会存储在状态文件
}
# ✅ 使用 Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "db-password"
}
resource "aws_db_instance" "database" {
password = data.aws_secretsmanager_secret_version.db_password.secret_string
}
# 标记为敏感
output "db_password" {
value = aws_db_instance.database.password
sensitive = true # 不会在输出中显示
}
状态迁移
本地到远程
# 1. 配置远程后端
cat >> backend.tf <<EOF
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
}
}
EOF
# 2. 初始化并迁移
terraform init -migrate-state
# 3. 验证
terraform state list
更换后端
# 1. 更新后端配置
# 2. 重新初始化
terraform init -reconfigure
# 或强制重新配置
terraform init -migrate-state -force-copy
最佳实践
1. 始终使用远程状态
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "${var.project}/${var.environment}/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
2. 启用状态锁定
# S3 后端自动锁定(需要 DynamoDB)
dynamodb_table = "terraform-locks"
3. 版本控制和备份
- 启用 S3 版本控制
- 定期备份状态文件
- 使用 lifecycle 策略管理旧版本
4. 分离环境状态
# 选项 1:使用不同的 key
backend "s3" {
key = "env/${var.environment}/terraform.tfstate"
}
# 选项 2:使用 Workspace
terraform workspace new dev
terraform workspace new prod
# 选项 3:完全分离的后端
# dev/backend.tf
backend "s3" { bucket = "dev-terraform-state" }
# prod/backend.tf
backend "s3" { bucket = "prod-terraform-state" }
5. 不要手动编辑状态文件
# ✅ 使用 Terraform 命令
terraform state mv
terraform state rm
terraform import
# ❌ 不要直接编辑
vi terraform.tfstate # 危险!
6. 定期刷新状态
# 同步真实状态
terraform refresh
# 或在 plan/apply 时自动刷新(默认行为)
terraform apply
7. 状态文件不要提交到 Git
# .gitignore
.terraform/
*.tfstate
*.tfstate.backup
故障排查
状态不一致
# 问题:状态与真实资源不一致
# 解决方案 1:刷新状态
terraform refresh
# 解决方案 2:重新导入
terraform import aws_instance.web i-xxx
# 解决方案 3:手动修复
terraform state rm aws_instance.broken
# 重新创建配置并 apply
状态锁定失败
# 检查锁
aws dynamodb get-item \
--table-name terraform-locks \
--key '{"LockID":{"S":"my-terraform-state/prod/terraform.tfstate"}}'
# 强制解锁
terraform force-unlock <LOCK_ID>
状态文件损坏
# 从 S3 版本恢复
aws s3api list-object-versions \
--bucket my-terraform-state \
--prefix terraform.tfstate
# 下载旧版本
aws s3api get-object \
--bucket my-terraform-state \
--key terraform.tfstate \
--version-id VERSION_ID \
terraform.tfstate.restored
# 替换当前状态
cp terraform.tfstate.restored terraform.tfstate
terraform state list # 验证
总结
Terraform 状态管理要点:
- 状态文件:记录资源映射关系
- 远程状态:团队协作必备
- 状态锁定:防止并发冲突
- 工作空间:管理多环境
- 安全:加密、访问控制、版本控制
- 最佳实践:远程后端、锁定、备份、分离环境
正确管理状态是 Terraform 稳定运行的关键!
下一章:模块化设计