Terraform资源定义梳理

  • 2019 年 12 月 26 日
  • 筆記

一.资源介绍

资源是Terraform体系中最重要的组成元素, 每个资源块用来定义一个或多个基础产品实例, 它或者定义一个VPC,或者定义一个CVM或者一条DNS记录.

二.资源定义语法

1.定义

每个资源可能有多个特性支持, 则需要十几或者几十个字段, 但创建此资源我们只提供最少的子集即可.如腾讯云的CVM全量需要30+个字段,但我们只需要5个字段就可以定义出一个CVM.

resource "tencentcloud_redis_instance" "test" {       availability_zone = "ap-guangzhou-3"                    type = "master_slave_redis"                password = "test12345789"                mem_size = 8192  }

一个资源块的定义需要四部分组成:关键字, 类型, 本地名字,资源配置

  • 关键字:固定为resource, terraform官方定义
  • 类型: 每个云厂商给资源的定义标识, 云厂商来定义,如我们定义mysql实例叫tencentcloud_mysql_instance, kubernetes集群叫tencentcloud_kubernetes_cluster
  • 本地名字:资源实例在本地存储的名字,用于资源之间的互相引用, 使用者来定义,这部分必须以字母或者下划线开头,并且只能包含数字,字母,下划线或者中划线
  • 资源配置:是这个资源的特性字段嵌套map,如cvm的内存大小硬盘大小等,参数必须位于{}中间,

类型和本地名字唯一确定一个资源, 所以可以认为是全局唯一(其实是模块内唯一,模块可以认为是资源定义的namespace)

上面我们定义一个redis的实例,位于"广州三区", 内存大小为8G,密码为test12345789,实例类型为主从redis,这个是定义一个redis的最小集合,其他的参数都采用默认参数

如port为6379, 名字让服务器端随机生成.

2.类型和资源参数

云上的每个产品功能都有一个或者多个资源进行定义,在编写这些资源排版代码时会决定这个资源管理云上的那个产品的那个功能,如定义

  • tencentcloud_mysql_instance来管理mysql产品的mysql实例
  • tencentcloud_mysql_readonly_instance管理mysql产品的只读实例
  • tencentcloud_mysql_account管理mysql产品的mysql帐号

每一个资源类型都属于一个特定的provider,此provider需要云厂商来开发, 以rpc插件的形式提供给terrform,terraform-provider-tencentcloud是我们提供的插件,提供大部分腾讯云产品资源封装

当选择好类型后,资源配置也随着确定,资源配置字段包含三个方面

A.排版资源需要的特性字段

如上例子中的password,mem_size.这些是能创建云上产品必须的一些字段

B.terraform支持的表达式

terraform支持部分表达式语法,如条件表达式condition ? true_val : false_val, 循环表达式for s in var.list : upper(s)等

C.terraform支持的元参数,本章后面会详细介绍

3.文档

terraform要求接入云厂商提供规范化文档方便客户使用,当我们熟悉terraform后大部分时间都画在研究这些文档上.我们腾讯云提供的文档在 here

左列是云产品分类和资源类型,右列是这个此资源类型如何配置使用,全英文环境.

1577349941_33_w1436_h602.png

4.资源行为

写好的资源块只存在于本地的配置文件,是我们创建云上资源的意图, 并没有在云平台上进行实施.

执行apply操作,此配置就会开始在云上进行产品的创建.执行destroy操作,云上的产品就开始销毁.

当我们执行完apply操作时, 新的配置块会将云上创建新的产品并保存在本地的state文件中,以便我们后序修改和删除.

对于那些旧的配置块,会和本地的state文件进行对比,如有修改则调用腾讯云的openapi进行修改,如有删除同样调用openapi进行删除.

5.资源依赖

大部分资源都不会对其他资源产生依赖,terraform可以并发的对资源进行创建,修改和删除.当时总有一些时候,资源之间会有依赖,或者是因为这些资源就是这样工作的,如mysql的帐号类型必须是在mysql实例创建完成会才能创建,或者是因为依赖其他资源的生成的一些信息.如dns要依赖lb生成的外网ip

一般情形下依赖关系可以自主处理,terraform处理的方式是根据资源块之间的字段依赖来形成有向图,遍历有向图形成排序关系,当资源创建,修改或者销毁时,会根据排序关系依次创建.

大多是资源对其他资源的依赖都是字段的依赖,因此通常不必指定资源之间的依赖性.

1577350573_55_w671_h453.png

然而总有一些依赖是没法通过字段来引用的, 这时候就需要元参数depends_on来解决这种隐藏的依赖,如使用cdn产品依赖域名备案, 但是cdn产品字段不会引用域名备案的任何字段

6.元参数

现在的terraform定义下下面6个元参数来影响资源行为,分别是

  • depends_on 用户指定隐藏的依赖
  • count 创建资源的数量
  • provider 用户选择非默认的其他provider
  • for_each 通过map或者string数组来创建一批资源 lifecycle 定制资源的生命周期细节
  • provisioner&connection 创建资源后的初始化操作

A.depends_on 指定隐藏的依赖

处理terraform不能自动推断出来的依赖关系, 主要处理的是依赖其他资源但是不依赖这些资源的任何字段

因为有的产品还没有封装进terraform-provider-tencentcloud, 在现有的支持产品情形下没有找到比较好的例子,所以例子先空缺,等以后产品丰富了在进行补充.

B.count 创建资源的数量

默认的情况下,资源块配置的是一个产品实例,在实际项目中我们可能要创建一批同配置的产品实例,这时候就可以用count来定义,如创建3个同样配置的redis可用

resource "tencentcloud_redis_instance" "test" {        availability_zone = "ap-guangzhou-3"                     type = "master_slave_redis"                 password = "test12345789"                  mem_size = 8192                      count=3                      name="redis_${count.index}"  }

当我们在资源块配置中使用count后,count.index表达式就会起效,标识当前的产品实例在资源块配置是的顺序编码,从0开始.

上面会创建3个redis,官网控制台可查看名字分别为redis_0,redis_1和redis_2

如果引用某个产品实例,普通的可以通过<TYPE>.<NAME>进行引用,而带有count的则要通用<TYPE>.<NAME><INDEX>的形式进行引用,如我们要引用第2个实例服务端分配的ip,可以通过

tencentcloud_redis_instance.test1.ip的形式

C.for_each 通过map或者string数组来创建一批资源

count要求各个实例的配置是一样的(除了可以通过count.index稍微达到差异),而for_each可以提供更个性配置方式

有此场景我们准备创建一个命名为orange的redis内存为1024,另一个命名为banana内存为8192,除此之外其他的配置都一样, 则可以用下面的形式

resource "tencentcloud_redis_instance" "test" {             for_each = {                  orange = 1024                  banana = 8192            }          availability_zone = "ap-guangzhou-3"                      type = "master_slave_redis"                  password = "test12345789"                       name=each.key                  mem_size = each.value  }

当我们在资源块配置中使用for_each后,each.key和each.value两个表达式就会起效

each.key表示for_each遍历的map或者set的key

each.value表示for_each遍历的map或者set的value,在set情形下each.key==each.value

用for_each创建的资源进行引用需要通过<TYPE>.<NAME><KEY>的形式进行,如我们需要名称为banana的redis实例的ip,则需要通过tencentcloud_redis_instance.test"banana".ip的形式

D.provider 用户选择非默认的其他provider

一般情形下一个provider配置一个region+aksk,如果我们想创建多个地区的资源就可以用provider元参数,它可以定义我们创建此资源使用另一个provider.

备选provider需要指定别名, 资源引用此provider采用<PROVIDER>.<ALIAS>的形式.

备选provider的配置会覆盖默认provider的配置

provider "tencentcloud" {    secret_id  = "AKIDPT**********nOxvJdjbR3"    secret_key = "XoqmT7WlkU******oJZaMzZ4ThfC2h6v"    region     = "ap-guangzhou"  }  provider "tencentcloud" {    alias  = "shanghai"    region = "ap-shanghai"  }    resource "tencentcloud_redis_instance" "test" {    type     = "master_slave_redis"    password = "test12345789"    mem_size = 8192  }    resource "tencentcloud_redis_instance" "test" {    provider = tencentcloud.shanghai    type     = "master_slave_redis"    password = "test12345789"    mem_size = 8192  }

我们在上海和广州各创建一个redis(因为redis az必须指定,此例子不能正常执行,大家可用cvm代替)

E.lifecycle 定制资源的生命周期细节

资源的生命周期可以参考上文的资源行为, lifecycle作用是定义资源行为中的一些小细节

resource "tencentcloud_redis_instance" "test" {    availability_zone = "ap-guangzhou-3"    type              = "master_slave_redis"    password          = "test12345789"    mem_size          = 8192    lifecycle {      create_before_destroy = true    }  }
  • create_before_destroy:大量资源存在不能修改的字段,如cdn的域名,cos的桶名字.修改这些字段需要对云上产品实例进行删除+创建新的产品实例. 默认情况下是先调用api删除云上产品实例,然后在创建新的实例.而create_before_destroy可以改变这种行为,使创建新的产品实例作为第一步,然后再销毁旧的云上产品实例. 但是云上资源一般都有自己的唯一性约束,如cos需要bucket名字唯一,as需要名字全局唯一等等.在使用 create_before_destroy前应该确认是否会出现唯一性约束的问题. prevent_destroy:只要此元参数在资源块配置中,terraform就会阻止此资源的删除.如果要删除此资源,需要将资源块配置中此字段去掉后即可.resource "tencentcloud_redis_instance" "test" { availability_zone = "ap-guangzhou-3" type = "master_slave_redis" password = "test12345789" mem_size = 8192 name = "redis" lifecycle { ignore_changes = [ name, ] } }resource "tencentcloud_key_pair" "foo" { key_name = "examplekey" public_key = file("~/.ssh/id_rsa.pub") } resource "tencentcloud_instance" "my_awesome_app" { key_name = aws_key_pair.example.key_name # … connection { type = "ssh" user = "root" private_key = file("~/.ssh/id_rsa") host = self.ip } provisioner "remote-exec" { inline = [ "sudo yum -y install nginx", "sudo systemctl start nginx" ] } }
  • ignore_changes: 一般情况下,如果terraform发现云上资源的字段和本地资源块中的不一致,会调用更新或者重建来进行同步.这种不一致一般是用户通过控制台操作或者自己调用api修改,这种情况尽量避免.如果发生后而且极少情形下,我们可以忽略这些属性不进行更新或者重建, 我们可以用ignore_changes.
  • provisioner&connection 创建资源后的初始化操作 有些资源创建后不能马上使用,需要执行一些特殊的操作后才能投入使用.如CVM可能需要一些个性化的配置或者初始化操作,可以通过provisioner&connection进行.官方不建议使用此功能,有其他的初始化工具如HashiCorp Packer替代

三.操作超时

有的资源编写的时候支持可以设置资源行为时间,如下边的资源可以设置create/update/delete的超时时间,并设置默认时间分别为40/80/40分钟

1577350516_66_w547_h284.png

同时我们也可以在资源配置块中指定行为的超时时间,3s表示3秒, 4m表示4分钟,5h表示5小时

resource "tencentcloud_example_instance" "example" {    # ...    timeouts {      create = "60m"      delete = "2h"    }  }

注意的是大部分资源不支持可配置超时,我们腾讯云的没有一个资源支持可配置超时.


以上就是terraform的基本使用方式.有什么疑问可以找我们组或者点击

here进行反馈.