Terraform 语法 module模块 简介/封装/实践

之前所有的基础设施的代码都放在一个目录下面来进行管理的,后面写代码的方式通过模块来组合。

随着基础设施是不断在变更的,在一个目录下面管理全部的资源会随着基础设施的扩展管理起来的复杂度就越来越高了,这样就需要将资源抽象成模块,然后引入模块。

将所有的资源封装为模块,然后实现资源的复用。

这里的模块和编程语言当中的模块是一样的概念。之前写代码是在整个文件里面写我们的函数和方法,甚至一些逻辑都写到文件里面去了。

但是后期发现一个文件管理起来是十分复杂的,这个时候就可以创建包和模块,然后单独的去维护那块功能就行了,后面就直接导入这个模块就行了。

在tf里面也是一样的。

在模块里面定义的还是基础设施及代码,后期使用哪些资源导入这个模块就行了,不需要重复去定义这些资源了。

如果全部放在一起,那么创建3个ecs就需要复制三行代码,这是不推荐的做法。有了模块就可以直接使用模块,减少代码编写同时代码可以复用。

resource "alicloud_instance" "instance" {
  # cn-beijing
  availability_zone = "${var.region}-b"
  security_groups   = alicloud_security_group.group.*.id

  # series III
  instance_type           = "ecs.n1.tiny"
  system_disk_category    = "cloud_efficiency"
  system_disk_name        = "test_foo_system_disk_name"
  system_disk_description = "test_foo_system_disk_description"
  // image_id                   = "ubuntu_18_04_64_20G_alibase_20190624.vhd"
  image_id                   = data.alicloud_images.images_ds.images[0].id
  instance_name              = local.instance_name
  vswitch_id                 = alicloud_vswitch.vsw.id
  internet_max_bandwidth_out = 10
  internet_charge_type       = "PayByTraffic"
  password                   = "root@123"
}

module 简介


一个目录下就是一个模块,只不过它叫做根模块。在生产环境代码下都是根模块下面有很多子模块,甚至子模块里面还有子模块。(之前所有文件在一起,其实就已经是一个模块了,之前是大模块,里面没有子模块,现在需要做的就是添加子模块

建议就是根模块和子模块,不易套太深,套太深那么可读性和维护性都会变的很差了,到时候不方便去管理。

一个目录下所有.tf文件组成的就是一个模块。这些模块写的资源的语法都是一致的。

如果你是json语法,那么会有很多.json结尾的文件组成模块。

模块结构如上所示:有个主文件,main.tf,主程序的入口,有些模块的输出变量outputs.tf(主要适用于模块之间的调用),模块的说明,最后模块的变量。

上面是常见模块里面所包含的内容。

语法


随意创建一个目录,然后在这个目录下存放所有的资源模块,然后在模块下创建ecs,那么下面全部存放的都是ecs资源。

上面就是常见模块的组成。这个主配置文件里面还是写我们的ecs。如果后面要创建VPC 安全组,那么在模块下再创建目录。

我们将这些资源以模块方式加进去(其实就是将资源改为模块的方式),最后导入进来。

在创建ecs的时候,肯定会使用到vpc的属性的。

vswitch_id                 = alicloud_vswitch.vsw.id

 那么就需要在vpc模块outputs.tf里面使用output输出变量了。

output "vswitch_id" {
  value = alicloud_vswitch.vsw.id
}

vpc是一个模块,ecs是一个模块,ecs肯定是要使用到vpc模块当中vswitch_id参数的。

 那么现在就可以在ecs当中改了

  vswitch_id                 = var.vswitch_id

带宽先不需要关心,后面可能会创建eip的资源,然后挂上去就行了,当你指定完带宽之后,它会给你自动的分配公网的IP。

然后使用locals定义局部变量,locals非常适合在模块当中定义变量。

因为ecs涉及到vpc,上面创建好了vpc,还差安全组,现在搞安全组。

这里需要关联vpc,那么将其变为变量。因为安全组和安全组规则在同一个模块,所以可以通过资源名称引用。因为ecs和安全组不是一个模块,那么安全组需要在outputs加一个输出,这个输出其实就是安全组。

output "security_group_id" {
  value = alicloud_security_group.group.*.id
}

因为返回的是list类型,那么在ecs里面定义的便利也是list类型

variable "security_group_id" {
  type = list(string)
}

最后同理dns也一样,引用了模块ecs的public ip,那么ecs的要使用output

output "instance_public_ip" {
  value = alicloud_instance.instance.public_ip
}

整体结构如上面。模块定义好了,资源定义好了,要发布了,就要去引用资源了。

现在在根目录下创建一个目录env,再创建一个文件夹dev。dev环境肯定需要引入这些资源。

versions里面其实就是版本,main是项目的主配置文件。

上面就是按照步骤,创建好了各个资源,相当于将资源都分开了,之前是在一个目录下,现在是按照每个资源一个模块目录的方式去划分的。

现在比如要创建dev环境,那么肯定要引入这里的资源,接下来就是模块的引用。

引用的话也是按照语句块这种格式去写的,用的关键字module, 然后是模块的名称,这个是可以自己去定义的。

后面就是source源,就是模块在哪,可以是在git上面,也可能在你的本地,也可能在其他的HTTP服务器上面。

这个在实际环境当中,用的最多的就是git了,在git上面存储我们的模块,加载的时候从gitlab上面去拿,更新就是更新到gitlab上面去。

现在是使用本地的方式去加载,唯一的区别就是保存到本地了,还有一个就是上传到版本控制系统远端的,source里面地址会发生变化。

version是版本,这个可以按照自己的定义去写。

最后就是变量和参数了。调用模块的时候需要传递什么样的参数。

在调用模块的时候,里面的形参数是模块里面通过variable定义的参数,实参可以来自于local。而调用其他模块当中参数,就在模块当中使用output参数即可。

比如ecs模块需要用的vpc模块当中的输出变量,module.哪个模块的名称.output输出变量

vswitch_id = module.terraform_vpc.vswitch_id

1.如果全部资源清单文件在一起,那么可以直接通过data数据源和资源resource拿到想要的值,如果是不同模块,那么要通过variables声明变量,然后通过模块当中的参数

value = alicloud_security_group.group.*.id

*.id这就代表是list类型

variable "security_group_id" {
  type = list(string)
}

或者这里类型不限制

 最后整个结构如下,env是环境,下面有test测试环境,dev生产环境,现在我们去dev环境操作

两个目录,一个目录是环境(开发,测试环境)一个目录模块环境(放各个资源模块) 

这里注意在引用模块的时候,相对路径不要写错了。

上面就是模块的语法,非常的简单,重点还是source这块,将路径写对,下面就是一些参数了。

模块source可以写远程的模块,也可以写本地的模块。

所有的远程的模块都是放在git存储库上面,那么在执行init或者get的时候,他就会将其下载到.terraform目录下面,这里多了modules这个目录。

{
    "Modules": [
        {
            "Key": "terraform_dns",
            "Source": "../../modules/dns",
            "Dir": "../../modules/dns"
        },
        {
            "Key": "terraform_ecs",
            "Source": "../../modules/ecs",
            "Dir": "../../modules/ecs"
        },
        {
            "Key": "terraform_security_group",
            "Source": "../../modules/security_group",
            "Dir": "../../modules/security_group"
        },
        {
            "Key": "terraform_vpc",
            "Source": "../../modules/vpc",
            "Dir": "../../modules/vpc"
        },
        {
            "Key": "",
            "Source": "",
            "Dir": "."
        }
    ]
}

key是模块的名称,模块的位置在哪,然后目录。在执行init的时候就会在terraform目录下创建。

但是本地模块就比较简单,不会把模块复制到这里面去,而是直接做了引用,相当于软链接的引用,你改了本地模块之后理论上是不需要重新init的。

init是包含get下载模块这个步骤,另外还加了初始化后端存储,就是terraform.tfstate这个文件。

所以init功能比get功能要多一些,而get是专门去下载模块的。当模块较多的时候,可以通过graph图表的方式查看模块。

root下面有很多模块,和我们现在结构一样。

模块调用,就是source这块,可以是本地的目录,也可以是一个公共的terraform regestri上面的公共的模块,现在模块比较成熟,可以直接拿来使用。

GitHub上面支持HTTP ssh,无非就改一下链接。

s3 bucket里面也可以去拿。

还有通用的一些git。

上面都是模块的调用方式。

ecs这里其实引用了vpc模块里面的交换机的id,那么这个id我们这定义了一个变量,通过variable里面定义了输入的变量。

在创建好交换机之后,是要将变量输出一下,将交换机的id放在output里面了。

主模块在调用创建ecs的时候交换机的id通过模块输出拿到。 

这里的输入输出就是对应我们在:

输入变量:variables里面定义的是输入变量,这些变量放在了模块定义里面。

输出变量就放在output里面。

模块实例化就是引入这个模块,练习就是单独的模块,上面就是一个模块的多次实例化。 

记住一点dev相当于是root模块,在里面有stat文件,那么dev环境变更的时候都要进入到这个目录下面,在其他目录下执行,就加载不到这个状态文件,最后有可能就失败的。

所以生产环境还是多个目录去区分不同的环境。

 如果再创建一台ecs,那么引入ecs的模块就行了,再去填它的配置就行了。

猜你喜欢

转载自blog.csdn.net/qq_34556414/article/details/127615713