1972 字
10 分钟
Terraform 状态文件

本文行文:state 里到底存了什么 → 本地存储的局限 → 远程后端与状态锁的完整配置 → 多环境与仓库布局 → 冲突与漂移的处理

Terraform State 是什么?#

terraform.tfstate 是 Terraform 的核心状态文件,使用 JSON 格式记录:

  • Terraform 管理的资源与真实基础设施的映射
  • 资源 ID、属性、依赖关系
  • 元数据、版本、输出值

Terraform 通过 state 对比“期望状态”和“真实状态”,生成 plan,再执行 apply。

代码里只写”我想要什么”,state 里才记着”实际建出来的是什么”,两者靠资源地址对应:

main.tf
# 只声明期望状态;真实资源 ID 与属性记在 state 里
resource "alicloud_vpc" "this" {
vpc_name = "prod-vpc"
cidr_block = "10.0.0.0/16"
}
resource "alicloud_vswitch" "this" {
vpc_id = alicloud_vpc.this.id # 引用即隐式依赖
cidr_block = "10.0.1.0/24"
zone_id = "cn-hangzhou-i"
}
resource "alicloud_instance" "web" {
instance_type = "ecs.c6.large"
image_id = "centos_7_9_x64_20G_alibase_20240115.vhd"
vswitch_id = alicloud_vswitch.this.id
}

apply 之后,state 里就有了这份对应关系的节选(真实文件比这长得多):

terraform.tfstate(节选)
{
"version": 4,
"terraform_version": "1.9.0",
"serial": 12,
"lineage": "6f2a1c3e-1f4b-4d2a-9c3e-7b8f0d5a1e42",
"outputs": {
"web_public_ip": { "value": "47.98.12.34", "type": "string" }
},
"resources": [
{
"mode": "managed",
"type": "alicloud_instance",
"name": "web",
"provider": "provider[\"registry.terraform.io/aliyun/alicloud\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"id": "i-bp1xxxxxxxxxxxx",
"instance_type": "ecs.c6.large",
"vswitch_id": "vsw-bp1yyyyyyyy",
"private_ip": "10.0.1.23"
},
"dependencies": [
"alicloud_vpc.this",
"alicloud_vswitch.this"
]
}
]
}
]
}

几个字段值得留意:

字段含义
lineage这份 state 的”血统”标识,用来判断两份 state 是否同源(迁移、合并时很关键)
serial每次写入自增,能看出写入次序;两个 serial 相同的副本说明发生了并发写
instances[].attributes.id真实资源 ID,丢了它就等于失去了对资源的跟踪
dependencies资源依赖关系,决定 destroy 的顺序
outputsterraform output 的数据来源,也是 terraform_remote_state 对外暴露的内容

注意:

  • 默认存在本地(terraform.tfstate 与备份 terraform.tfstate.backup),不适合多人协作。
  • 明文存储,包含敏感信息,不能提交到 Git。
  • sensitive = true 只是隐藏命令行与日志里的输出,state 中依然是明文,别把它当脱敏手段。
  • 删除或损坏 state 会导致 Terraform 失去对资源的跟踪,destroy 也就无从谈起。

多人协作#

核心:远程后端 + 状态锁 + CI/CD + 权限控制。

多人协作规范:

  1. 远程后端:所有人读写同一份远程 state。
  2. 状态锁:apply 时自动加锁,防止并发写坏 state;CI 里加 -lock-timeout=5m 让并发排队而不是直接失败。
  3. CI/CD 执行:通过流水线跑 terraform plan/apply,人工审批后 apply。
  4. 环境隔离:用 workspace 或独立目录隔离 dev/staging/prod。
  5. 权限控制:RAM 权限、OSS 加密、操作审计。
  6. 代码审查:Terraform 代码走 Git MR/PR。
  7. 禁止手动改 state:必须用 terraform import、state mv、state rm、apply -replace 等命令。
  8. 定期备份 state:OSS 版本控制 + 生命周期。

配置示例#

阿里云常用:

  • OSS 作为远程后端 存 state
  • Tablestore 作为状态锁
  • 配置示例:
backend.tf
terraform {
required_version = ">= 1.6"
backend "oss" {
bucket = "tf-state-prod"
key = "env/prod/terraform.tfstate"
region = "cn-hangzhou"
# 私有读写 + 服务端加密
acl = "private"
encrypt = true
# 状态锁:OSS 后端依赖 Tablestore 实现
tablestore_endpoint = "https://tf-lock.cn-hangzhou.ots.aliyuncs.com"
tablestore_table = "tf_lock"
}
}

多环境要复用同一份代码,就得把后端参数外置(partial configuration),在 init 时注入:

envs/prod/backend.tf
# 只声明"用哪个后端",具体参数由 -backend-config 提供
terraform {
backend "oss" {}
}
envs/prod/backend.tfbackend
# 真正的连接参数,按环境各存一份
bucket = "tf-state-prod"
key = "env/prod/terraform.tfstate"
region = "cn-hangzhou"
encrypt = true
acl = "private"
tablestore_endpoint = "https://tf-lock.cn-hangzhou.ots.aliyuncs.com"
tablestore_table = "tf_lock"
Terminal window
# 首次初始化:下载 provider 并连上远程后端
terraform -chdir=envs/prod init -backend-config=backend.tfbackend
# 已经有本地 state(或换后端)时,把已有 state 迁移过去
terraform -chdir=envs/prod init -migrate-state -backend-config=backend.tfbackend

provider、变量与资源分成几个文件,是 Terraform 里最常见的组织方式:

envs/prod/providers.tf
terraform {
required_providers {
alicloud = {
source = "aliyun/alicloud"
version = "~> 1.230"
}
}
}
provider "alicloud" {
region = "cn-hangzhou"
}
envs/prod/variables.tf
variable "env" {
type = string
description = "环境名,用于资源命名"
}
variable "instance_type" {
type = string
default = "ecs.c6.large"
}
envs/prod/terraform.tfvars
# 随环境变化的取值(含敏感值时不入库,见 .gitignore)
env = "prod"
instance_type = "ecs.c7.xlarge"

仓库布局#

Terraform 仓库布局
  • envs
    • dev
      • backend.tf
      • backend.tfbackend
      • providers.tf
      • main.tf
      • variables.tf
      • terraform.tfvars
    • prod
      • backend.tf
      • backend.tfbackend
      • providers.tf
      • main.tf
      • variables.tf
      • terraform.tfvars
  • modules
    • network
      • main.tf
      • variables.tf
      • outputs.tf
    • ecs
      • main.tf
      • variables.tf
      • outputs.tf
  • .gitignore

.gitignore 至少要挡住三样东西:

.gitignore
# 本地 state 与崩溃日志:绝不入库
*.tfstate
*.tfstate.*
crash.log
# 变量文件可能含明文密钥
*.tfvars
!*.tfvars.example
# provider 插件与本地缓存
.terraform/

CI/CD 流水线#

流水线里最小可用的 plan 配置:

.github/workflows/terraform.yml
name: terraform
on:
pull_request:
paths: ["envs/**", "modules/**"]
jobs:
plan:
runs-on: ubuntu-latest
defaults:
run:
working-directory: envs/prod
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform fmt -check -recursive
- run: terraform init -backend-config=backend.tfbackend
- run: terraform plan -lock-timeout=5m -out=tfplan

冲突处理#

冲突分几种:

A. 锁冲突

  • 现象:Error acquiring the state lock
  • 处理:确认没有正在运行的 apply,等待锁释放;若锁残留,从报错信息里取出 LOCK_ID,确认安全后用 terraform force-unlock <LOCK_ID>,谨慎操作。

B. 状态漂移

  • 现象:plan 显示资源要重建或修改,但真实环境已被人手动改过。
  • 处理:先用只读的 terraform plan -refresh-only 看清真实环境与 state 的差异;确认接受现状就 terraform apply -refresh-only 把真实属性写回 state,确认要收敛就正常 apply 让代码覆盖真实环境。能纳管就 import,不能纳管就 state rm 后重新创建。
Terminal window
# 只比对、不改资源(推荐用它替代已废弃的 terraform refresh)
terraform plan -refresh-only
# 确认"以真实环境为准"后,把属性写回 state
terraform apply -refresh-only

C. 并发修改

  • 远程后端 + 锁基本可避免。若两人同时改代码,先拉最新代码,解决 Git 冲突,再重新 plan。
  • 不要手动编辑 .tfstate,除非紧急且先备份。

D. 资源已存在

  • 用 terraform import 把已有 ECS、SLB、VPC 等导入 state;Terraform 1.5+ 还可以把导入写成声明式代码:
import.tf
# 声明式导入,apply 时生效
import {
to = alicloud_instance.web
id = "i-bp1xxxxxxxxxxxx"
}
Terminal window
# 命令行方式(等价)
terraform import alicloud_instance.web i-bp1xxxxxxxxxxxx
  • 导入只按 ID 读取真实资源并写入 state,不会生成配置,导完必须 terraform plan 检查属性是否与代码一致;不想手写配置可以用 terraform plan -generate-config-out=imported.tf 生成骨架。
  • 或 terraform state rm 移除后重新创建。

常用命令速查#

以下命令默认在环境目录中执行(如 envs/prod),在其他位置可以用全局选项 -chdir=envs/prod 指定。

命令用途
terraform init -backend-config=...初始化后端、下载 provider
terraform init -migrate-state切换后端并迁移已有 state
terraform plan -refresh-only只比对真实环境与 state 的差异,不改资源
terraform apply -refresh-only把真实属性写回 state
terraform state list / show <地址>查看纳管的资源 / 单个资源的记档
terraform state mv <源> <目标>资源改名或移动(重构代码后常用)
terraform state rm <地址>从 state 移除(不会删除真实资源)
terraform import <地址> <ID>纳管已存在的资源
terraform apply -replace=<地址>强制重建单个资源(替代已废弃的 taint)
terraform force-unlock <LOCK_ID>释放残留锁
terraform output -json读取输出值,供其他环境或脚本消费
Terraform 状态文件
https://blog-l7wd3.pages.dev/posts/iac/terraform-state/
作者
L7WD3-Xiao
发布于
2026-10-05
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取