1.9.2.1. 依赖反转
Terraform 编排的基础设施对象彼此之间可能互相存在依赖关系,有时我们在编写一些旨在重用的模块时,模块内定义的资源可能本身需要依赖其他一些资源,这些资源可能已经存在,也可能有待创建。
举一个例子,假设我们编写了一个模块,定义了在 UCloud 上同一个 VPC 中的两台服务器;第一台服务器部署了一个 Web 应用,它被分配在一个 DMZ 子网里;第二台服务器部署了一个数据库,它被分配在一个内网子网里。现在的问题是,在我们编写模块时,我们并没有关于 VPC 和子网的任何信息,我们甚至连服务器应该部署在哪个可用区都不知道。VPC 和子网可能已经存在,也可以有待创建。
我们可以定义这样的一个模块代码:
terraform {
required_providers {
ucloud = {
source = "ucloud/ucloud"
version = "~>1.22.0"
}
}
}
variable "network_config" {
type = object({
vpc_id = string
web_app_config = object({
az = string
subnet_id = string
})
db_config = object({
az = string
subnet_id = string
})
})
}
data "ucloud_images" "web_app" {
name_regex = "^WebApp"
}
data "ucloud_images" "mysql" {
name_regex = "^MySql 5.7"
}
resource "ucloud_instance" "web_app" {
availability_zone = var.network_config.web_app_config.az
image_id = data.ucloud_images.web_app.images[0].id
instance_type = "n-basic-2"
vpc_id = var.network_config.vpc_id
subnet_id = var.network_config.web_app_config.subnet_id
}
resource "ucloud_instance" "mysql" {
availability_zone = var.network_config.db_config.az
image_id = data.ucloud_images.mysql.images[0].id
instance_type = "n-basic-2"
vpc_id = var.network_config.vpc_id
subnet_id = var.network_config.db_config.subnet_id
}
在代码中我们把依赖的网络参数定义为一个复杂类型,一个强类型对象结构。这样的话模块代码就不用再关注网络层究竟是查询而来的还是创建的,模块中只定义了抽象的网络层定义,其具体实现由调用者从外部注入,从而实现了依赖反转。
如果调用者需要创建网络层,那么代码可以是这样的(假设我们把前面编写的模块保存在 ./machine
目录下而成为一个内嵌模块):
resource "ucloud_vpc" "vpc" {
cidr_blocks = [
"192.168.0.0/16"]
}
resource "ucloud_subnet" "dmz" {
cidr_block = "192.168.0.0/24"
vpc_id = ucloud_vpc.vpc.id
}
resource "ucloud_subnet" "db" {
cidr_block = "192.168.1.0/24"
vpc_id = ucloud_vpc.vpc.id
}
module "machine" {
source = "./machine"
network_config = {
vpc_id = ucloud_vpc.vpc.id
web_app_config = {
az = "cn-bj2-02"
subnet_id = ucloud_subnet.dmz.id
}
db_config = {
az = "cn-bj2-02"
subnet_id = ucloud_subnet.db.id
}
}
}
或者我们想使用现存的网络来托管服务器:
data "ucloud_vpcs" "vpc" {
name_regex = "^AVeryImportantVpc"
}
data "ucloud_subnets" dmz_subnet {
vpc_id = data.ucloud_vpcs.vpc.vpcs[0].id
name_regex = "^DMZ"
}
data "ucloud_subnets" "db_subnet" {
vpc_id = data.ucloud_vpcs.vpc.vpcs[0].id
name_regex = "^DataBase"
}
module "machine" {
source = "./machine"
network_config = {
vpc_id = data.ucloud_vpcs.vpc.vpcs[0].id
web_app_config = {
az = "cn-bj2-02"
subnet_id = data.ucloud_subnets.dmz_subnet.subnets[0].id
}
db_config = {
az = "cn-bj2-02"
subnet_id = data.ucloud_subnets.db_subnet.subnets[0].id
}
}
}
由于模块代码中对网络层的定义是抽象的,并没有指定必须是 resource
或是 data
,所以使得模块的调用者可以自己决定如何构造模块的依赖层,作为参数注入模块。