模块化需落实输出定义、AZ分散部署、Git版本化source及state按故障域隔离,否则无法保障高可用。

模块化不是加个 module 块就完事
直接在根配置里写 module "vpc" { source = "./modules/vpc" } 很容易跑通,但高可用架构真正卡住的地方是模块间依赖和状态耦合。比如 module "eks" 依赖 module "vpc" 的子网 ID,但若子网没打标签或没导出 private_subnets 输出,EKS 节点组就会创建失败,报错类似 Error: Invalid index —— 这类问题不会在 terraform plan 阶段暴露,要等 apply 才炸。
- 每个模块必须显式定义
output,且输出名要语义清晰(比如不用ids,改用private_subnet_ids) - 避免跨模块读取
data资源,比如在 EKS 模块里用data.aws_subnets查 VPC 下的子网——这绕过了模块边界,导致状态不一致 - 模块内部尽量不硬编码 AZ 列表,而是接收
availability_zones变量,由上层统一传入(例如["us-east-1a", "us-east-1b", "us-east-1c"])
高可用关键:AZ 分散 + 失败容忍不能靠运气
Terraform 默认不校验资源是否真跨了多个 AZ,它只按你写的参数去调 API。如果你给 aws_instance 的 availability_zone 传了个固定值 "us-east-1a",哪怕模块声明支持多 AZ,实际也是单点。
- 对需要高可用的资源(如 RDS、ALB、EKS Node Group),必须用
count或for_each遍历多个子网,而不是只部署一份 - ALB 的
subnets字段必须填至少两个不同 AZ 的子网 ID;RDS 的multi_az = true仅控制备库位置,主实例仍需手动指定跨 AZ 的子网组 - 检查模块是否在
variables.tf中把min_required_azs = 3设为校验逻辑,例如用validation块限制length(var.availability_zones) >= 3
source 地址别用本地路径,版本控制才是高可用的前提
写 source = "../modules/network" 看似方便,但 CI/CD 流水线拉取代码时路径可能错乱;更糟的是,多人协作时 A 改了本地模块,B apply 却用了旧版逻辑,高可用配置悄悄退化成单 AZ。
- 用 Git URL + 版本号:
source = "git::https://github.com/your-org/terraform-aws-vpc.git?ref=v2.4.0" - 禁止用
ref=main或ref=master,分支随时可能变,等同于没版本 - 模块仓库自身要发 semantic version tag,且每次变更输出字段或必需变量时升 MINOR 或 MAJOR 版,避免下游
plan不报错但行为已变
State 拆分比模块拆分更关键
一个模块对应一个 state 文件,才能隔离变更影响。比如更新数据库密码不该触发 VPC 重建。但很多人只拆了模块,却把所有 module 声明塞进同一个 main.tf,结果 terraform apply -target=module.rds 仍会锁整个 state。
- 按生命周期边界切 state:网络层(VPC、子网)、计算层(EKS、EC2)、数据层(RDS、ElastiCache)各用独立工作目录 + 独立 backend 配置
- 用
terraform init -backend-config="key=prod/network/terraform.tfstate"显式指定每个模块的 state 路径 - 跨 state 引用必须通过
terraform_remote_state数据源,且该数据源要锁定 provider 版本(避免新版 provider 解析老 state 出错)
模块化本身不提升高可用性,它只是让“正确实现高可用”这件事变得可维护、可审计、可回滚。最容易被忽略的是:模块输出是否真实反映基础设施拓扑,以及 state 是否真的按故障域隔离——这两点不落实,再多层 module 嵌套也只是镜花水月。

















