《Terraform 101 從入門到實踐》 第三章 Modules模組化

2023-02-10 12:00:49

《Terraform 101 從入門到實踐》這本小冊在南瓜慢說官方網站GitHub兩個地方同步更新,書中的範例程式碼也是放在GitHub上,方便大家參考檢視。


模組的概念

模組化是Terraform實現程式碼重用的方式。模組可以理解為一個包含多個資源的容器模板。封裝好之後,可以給大家使用。也可以理解為程式碼中的函數或方法,它接收入參,經過一些宣告式的呼叫後,輸出一些結果變數。

從Terraform的程式碼層面來看,模組其實就是一個包含多個.tf或.tf.json檔案的目錄。任何一個Terraform專案,都是一個目錄,所以也都是一個模組,我們把它稱為根模組(Root Module)。而在它目錄下的其它模組,都是子模組。我們可以呼叫多個模組,也可以多次呼叫同一個子模組。在子模組中,也可以呼叫其它模組。這些特點,與函數無異。

呼叫模組有兩種方式,一種是在當前專案定義一個模組,另一種是引入外部的模組。而外部模組的方式也很多種,如Git的倉庫、壓縮檔案等。

定義並使用模組

我們先來使用第一種方式,參照當前專案中的模組。

子模組的功能很簡單,建立一個檔案,檔名有隨機字串,以避免衝突。寫入檔案的內容可以通過引數指定。

子模組:

定義入參:建立一個檔案叫variables.tf,專門用來定義入參:

variable "prefix" {
  type        = string
  default     = "pkslow"
  description = "File name prefix"
}

variable "content" {
  type        = string
  default     = "www.pkslow.com"
  description = "File content"
}

這裡輸入有兩個變數,都是字串型別,分別是檔名字首prefix和檔案內容context。

定義模組功能,主要設定這個模組用管理的資源,一般會放在main.tf檔案中,內容如下:

resource "random_string" "random" {
  length  = 6
  lower   = true
  special = false
}

resource "local_file" "file" {
  content  = var.content
  filename = "${path.root}/${var.prefix}.${random_string.random.result}.txt"
}

這裡定義了兩個resource,第一個是生成6位的隨機字串。第二個是生成一個檔案,第二個resource使用了輸入引數,還使用了第一個資源生成的結果。所以第二個resource是依賴於第一個的。輸入的變數參照方式為var.xxx

定義返回值:

可以不需要返回值,也可以定義一個或多個返回值。建立一個outputs.tf檔案,內容如下:

output "file_name" {
  value = local_file.file.filename
}

它返回的是前面第二個resource中的值。

現在,模組random-file已經定義完成了。現在我們在根模組呼叫這個子模組。程式碼如下:

module "local-file" {
  source  = "./random-file"
  prefix  = "pkslow"
  content = "Hi guys, this is www.pkslow.com\nBest wishes!"
}

這個source是被呼叫模組的地址。prefixcontent都是入參,之前已經定義了。

在根模組也可以定義輸出變數:

output "fileName" {
  value = module.local-file.file_name
}

這裡直接輸出子模組的檔名,也就是子模組的返回變數file_name。

apply後通過terraform output檢視輸出:

$ terraform output
fileName = "./pkslow.B2UwmR.txt"

多個block呼叫同一個module

我們說過模組是為了實現程式碼複用,Terraform允許一個模組被多次呼叫。我們修改根模組的呼叫程式碼:

module "pkslow-file" {
  source  = "./random-file"
  prefix  = "pkslow"
  content = "Hi guys, this is www.pkslow.com\nBest wishes!"
}

module "larry-file" {
  source  = "./random-file"
  prefix  = "larrydpk"
  content = "Hi guys, this is Larry Deng!"
}

這裡兩個呼叫的source都是一樣的,都呼叫了random-file這個模組,只是入參不同。

根模組的輸出也修改一下:

output "pkslowPileName" {
  value = module.pkslow-file.file_name
}

output "larryFileName" {
  value = module.larry-file.file_name
}

執行apply後output輸出結果為:

$ terraform output
larryFileName = "./larrydpk.txoV34.txt"
pkslowPileName = "./pkslow.WnJVMm.txt"

迴圈呼叫一個module

count方式

多次呼叫一個模組還有另一種方式就是迴圈呼叫,通過count來實現,具體如下:

module "pkslow-file" {
  count   = 6
  source  = "./random-file"
  prefix  = "pkslow-${count.index}"
  content = "Hi guys, this is www.pkslow.com\nBest wishes!"
}

這裡會呼叫6次子模組random-file,下標索引為count.index,它是從0開始的索引。

因此,執行後,會生成以下6個檔案:

pkslow-0.JBDuhH.txt
pkslow-1.Z6QmPV.txt
pkslow-2.PlCK5u.txt
pkslow-3.a70sWN.txt
pkslow-4.UnxYue.txt
pkslow-5.8bSNxg.txt

這裡根模組的輸出就需要修改了,它成了一個List,通過*參照所有元素:

output "pkslowPileNameList" {
  value = module.pkslow-file.*.file_name
}

for each方式

通過for_each也可以實現迴圈呼叫:

Map的情況:

resource "azurerm_resource_group" "rg" {
  for_each = {
    a_group = "eastus"
    another_group = "westus2"
  }
  name     = each.key
  location = each.value
}

Set的情況:

resource "aws_iam_user" "the-accounts" {
  for_each = toset( ["Todd", "James", "Alice", "Dottie"] )
  name     = each.key
}

參照外部模組

除了在本專案中定義並參照模組之外,還可以參照外部的模組。在官方的倉庫中已經有非常多的可重用的模組了,可以到上面查詢:https://registry.terraform.io/browse/modules

比如我參照了( https://registry.terraform.io/modules/matti/resource/shell/latest )這個模組:

module "echo-larry-result" {
  source  = "matti/resource/shell"
  version = "1.5.0"
  command = "cat ${module.larry-file.file_name}"
}

執行terraform get會從倉庫下載模組:

$ terraform get
Downloading matti/resource/shell 1.5.0 for echo-larry-result...
- echo-larry-result in .terraform/modules/echo-larry-result
- larry-file in random-file
- pkslow-file in random-file

.modules目錄下可以檢視模組內容。

這個模組可以執行shell命令,並返回結果。我這裡執行的命令是讀取之前生成檔案的內容。輸出呼叫結果:

output "larryFileResult" {
  value = module.echo-larry-result.stdout
}

執行結果如下:

larryFileName = "./.result/larrydpk.GfgMyh.txt"
larryFileResult = "Hi guys, this is Larry Deng!"

模組來源

引入模組的來源很多:

  • 本地目錄
  • Terraform官方倉庫
  • GitHub或其它Git倉庫
  • Bitbucket
  • HTTP URLs
  • S3 Buckets
  • GCS Bucket

非常方便。我們已經介紹過比較常用的前兩種了,其它更多細節可以參考:https://www.terraform.io/docs/language/modules/sources.html