为什么目录结构很重要
用 Terraform 管理云资源时,初期一个 main.tf 就能跑通。但随着资源增多、环境从 1 个变成 3 个,单文件会迅速变成“改一处、崩三处”的泥潭。合理的目录结构能让环境隔离、代码复用和团队协作变得可控。
下面按演进顺序,介绍三种典型结构,你可以根据团队规模直接选用或逐步迁移。
阶段一:单文件/单目录(适合个人项目或原型)
project/
├── main.tf # 所有资源定义
├── variables.tf # 输入变量
├── outputs.tf # 输出值
└── terraform.tfstate
优点:上手快,terraform apply 即用。
缺点:无法区分环境,改测试环境可能误删生产资源;state 文件容易冲突。
何时该升级:当你需要第二套环境,或多人同时修改时。
阶段二:按环境拆分目录(最常用的起点)
project/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── terraform.tfvars
│ ├── staging/
│ └── prod/
└── modules/
└── network/
├── main.tf
├── variables.tf
└── outputs.tf
做法:
- 每个环境独立目录,独立 state(建议配置远程后端,如 S3+DynamoDB 或 Terraform Cloud)。
- 公共逻辑抽成
modules/,环境目录通过module块引用,传入不同变量。
关键点:
- 环境之间不共享 state,避免误操作跨环境传播。
- 模块只定义“能力”,不硬编码环境差异(如实例规格、数量通过变量传入)。
命令:进入 environments/dev 后执行 terraform init、plan、apply。
阶段三:模块化 + 环境组合(适合多团队、多区域)
project/
├── modules/
│ ├── network/
│ ├── compute/
│ └── database/
├── environments/
│ ├── dev/
│ │ ├── main.tf # 组合调用各模块
│ │ ├── backend.tf # 远程 state 配置
│ │ └── terraform.tfvars
│ ├── staging/
│ └── prod/
└── global/ # 跨环境共享资源(如 IAM、DNS)
进阶实践:
- 用 Terragrunt 或 Terraform Workspaces 减少重复代码(但 Workspaces 不适合强隔离的生产环境)。
- 模块版本化:将模块放在独立 Git 仓库,通过
source = "git::...?ref=v1.0.0"引用,避免环境间耦合。 - 状态文件按环境+组件拆分(如
network.tfstate、app.tfstate),缩小爆炸半径。
迁移建议
- 不要一次性重构。先为现有单文件配置远程后端,再逐步把资源抽到模块。
- 使用
terraform state mv移动资源,而不是删除重建。 - 每次迁移后运行
terraform plan,确认无变更再提交。 - 环境命名保持一致,如
dev、staging、prod,方便脚本和 CI 识别。
总结
目录结构的演进本质是隔离与复用的平衡:单文件适合起步,按环境拆分解决隔离,模块化解决复用。没有绝对最优,只有适合当前团队规模的那一档。建议从阶段二开始,等模块超过 5 个或团队超过 3 人时再考虑阶段三。