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進行回饋.

Tags: