1.6.1.1. 创建模块
实际上所有包含 Terraform 代码文件的文件夹都是一个 Terraform 模块。我们如果直接在一个文件夹内执行 terraform apply
或者 terraform plan
命令,那么当前所在的文件夹就被称为根模块(root module)。我们也可以在执行 Terraform 命令时通过命令行参数指定根模块的路径。
1.6.1.1.1. 模块结构
旨在被重用的模块与我们编写的根模块使用的是相同的 Terraform 代码和代码风格规范。一般来讲,在一个模块中,会有:
- 一个
README
文件,用来描述模块的用途。文件名可以是README
或者README.md
,后者应采用 Markdown 语法编写。可以考虑在README
中用可视化的图形来描绘创建的基础设施资源以及它们之间的关系。README
中不需要描述模块的输入输出,因为工具会自动收集相关信息。如果在README
中引用了外部文件或图片,请确保使用的是带有特定版本号的绝对 URL 路径以防止未来指向错误的版本 - 一个
LICENSE
描述模块使用的许可协议。如果你想要公开发布一个模块,最好考虑包含一个明确的许可证协议文件,许多组织不会使用没有明确许可证协议的模块 - 一个 examples 文件夹用来给出一个调用样例(可选)
- 一个
variables.tf
文件,包含模块所有的输入变量。输入变量应该有明确的描述说明用途 - 一个
outputs.tf
文件,包含模块所有的输出值。输出值应该有明确的描述说明用途 - 嵌入模块文件夹,出于封装复杂性或是复用代码的目的,我们可以在 modules 子目录下建立一些嵌入模块。所有包含 README 文件的嵌入模块都可以被外部用户使用;不含
README
文件的模块被认为是仅在当前模块内使用的(可选) - 一个
main.tf
,它是模块主要的入口点。对于一个简单的模块来说,可以把所有资源都定义在里面;如果是一个比较复杂的模块,我们可以把创建的资源分布到不同的代码文件中,但引用嵌入模块的代码还是应保留在main.tf
里 - 其他定义了各种基础设施对象的代码文件(可选)
如果模块含有多个嵌入模块,那么应避免它们彼此之间的引用,由根模块负责组合它们。
由于 examples/
的代码经常会被拷贝到其他项目中进行修改,所有在 examples/
代码中引用本模块时使用的引用路径应使用外部调用者可以使用的路径,而非相对路径。
一个最小化模块推荐的结构是这样的:
$ tree minimal-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
一个更完整一些的模块结构可以是这样的:
$ tree complete-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
├── ...
├── modules/
│ ├── nestedA/
│ │ ├── README.md
│ │ ├── variables.tf
│ │ ├── main.tf
│ │ ├── outputs.tf
│ ├── nestedB/
│ ├── .../
├── examples/
│ ├── exampleA/
│ │ ├── main.tf
│ ├── exampleB/
│ ├── .../
1.6.1.1.2. 避免过深的模块结构
我们刚才提到可以在 modules/
子目录下创建嵌入模块。Terraform 倡导"扁平"的模块结构,只应保持一层嵌入模块,防止在嵌入模块中继续创建嵌入模块。应将嵌入模块设计成易于组合的结构,使得在根模块中可以通过组合各个嵌入模块创建复杂的基础设施。