停止脚本,开始架构:OOP 方法在 Terraform 中
Source: Dev.to
TL;DR
问题: Terraform 代码库常常出现 蔓延——复制粘贴的资源、紧耦合以及泄漏的抽象,使得扩展变得痛苦。
解决方案: 将 Terraform 模块视为 类,模块实例视为 对象。
关键映射
| OOP 概念 | Terraform 等价 |
|---|---|
| 类 | 子模块(例如 ./modules/web_server/) |
| 对象 | module 块(实例化) |
| 接口 | variables.tf(输入)和 outputs.tf(获取器) |
| 私有状态 | locals 和内部资源 |
| 最佳实践 | 更倾向于 组合(由其他模块构建模块)而非继承。通过传递资源 ID(例如 vpc_id)实现 依赖注入,而不是在内部使用 data 源进行查找。 |
面向对象的方式将混乱、重复的 HCL 转变为可扩展的基础设施架构。通过将 OOP 原则——封装、抽象、组合和多态——映射到 Terraform 模块,我们能够构建出像应用代码一样可维护、可测试的基础设施。
问题:单块 Terraform 文件
在项目的早期阶段,单个 main.tf 文件使用起来很方便。随着基础设施的增长,这种 “脚本化” 思维导致脆弱性:硬编码的值在不同环境中重复出现,安全组与实例内联定义,且完全缺乏可复用性。
当 Terraform 仅被当作配置脚本来使用时,我们会错失软件工程设计模式带来的结构化优势。我们需要从 编写脚本 转向 构建对象。
核心类比:模块即类
| OOP 概念 | Terraform 实现 |
|---|---|
| 类定义 | ./modules/web_server/ – 定义 如何 构建某物的蓝图,而不是 构建什么。 |
| 构造函数 | variables.tf – 定义实例化类所需的输入。 |
| 公共方法 / 属性 | outputs.tf – 定义显式向调用方暴露的数据。 |
| 私有成员 | locals、resource – 对父作用域隐藏的内部逻辑和状态。 |
| 对象实例 | module "web_prod" { … } – 对蓝图的具体实现。 |
可视化:模块接口
classDiagram
class Module {
>
+variables.tf
+outputs.tf
-locals
-resources
}
class ChildModule {
+module "instance" {}
}
Module B[networking]
A --> C[compute]
B --> D[VPC & Subnets]
C --> E[EC2 Instances]
代码示例
# /modules/app_stack/main.tf
module "networking" {
source = "../networking"
cidr = var.cidr
}
module "compute" {
source = "../compute"
subnet_id = module.networking.private_subnet_id # Wiring components together
vpc_id = module.networking.vpc_id
}
app_stack 模块充当外观,协调网络层和计算层之间的交互,同时保持每个子模块专注于单一职责。
4. 抽象与复用:“接口”行为
(源文档中此部分内容被截断,原则仍然是通过 variables.tf 与 outputs.tf 定义清晰的输入/输出合约,以实现可复用、可互换的模块。)