【翻譯】Terraform 最佳實踐:模塊組合

原文:https://www.terraform.io/language/modules/develop/composition

在只有一個根模塊的簡單 Terraform 配置中亿笤,我們創(chuàng)建一組資源并使用 Terraform 的表達式語法來描述這些資源之間的關(guān)系:

resource "aws_vpc" "example" {
  cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "example" {
  vpc_id = aws_vpc.example.id

  availability_zone = "us-west-2b"
  cidr_block        = cidrsubnet(aws_vpc.example.cidr_block, 4, 1)
}

當我們引入模塊時,我們的配置開始變得分層而不是扁平化:每個模塊都包含自己的一組資源栋猖,可能還有自己的子模塊净薛,這可能會創(chuàng)建一個深層、復雜的資源配置樹蒲拉。

但是肃拜,在大多數(shù)情況下,我們強烈建議保持模塊樹扁平化:只有一層子模塊雌团,并使用類似于上述的技術(shù)燃领,使用表達式來描述模塊之間的關(guān)系:

module "network" {
  source = "./modules/aws-network"

  base_cidr_block = "10.0.0.0/8"
}

module "consul_cluster" {
  source = "./modules/aws-consul-cluster"

  vpc_id     = module.network.vpc_id
  subnet_ids = module.network.subnet_ids
}

我們將這種扁平化的模塊使用方式稱為模塊組合,因為它需要多個可組合的構(gòu)建塊模塊并將它們組裝在一起以產(chǎn)生更大的系統(tǒng)锦援。

模塊不是嵌入其依賴項猛蔽,創(chuàng)建和管理自己的副本,而是從根模塊接收其依賴項灵寺,因此可以以不同的方式連接相同的模塊以產(chǎn)生不同的結(jié)果曼库。

依賴倒置

在上面的示例中,我們看到了一個名為 consul_cluster 的模塊替久,它可能描述了在 AWS VPC 網(wǎng)絡(luò)中運行的 HashiCorp Consul 服務(wù)器集群凉泄,因此它需要 VPC 和該 VPC 內(nèi)的子網(wǎng)標識符作為參數(shù)。

另一種設(shè)計是讓 consul_cluster 模塊描述它自己的網(wǎng)絡(luò)資源蚯根。如果我們這樣做后众,那么 Consul 集群將很難與同一網(wǎng)絡(luò)中的其它基礎(chǔ)設(shè)施共存,所以我們希望盡可能保持模塊相對小颅拦,并傳遞它們的依賴項蒂誉。

這種依賴倒置方法還提高了未來重構(gòu)的靈活性,因為 consul_cluster 模塊不知道也不關(guān)心調(diào)用模塊如何獲取這些標識符距帅。 未來的重構(gòu)可能會將網(wǎng)絡(luò)創(chuàng)建分離到自己的配置中右锨,因此我們可以將這些值從數(shù)據(jù)源傳遞到模塊中:

data "aws_vpc" "main" {
  tags = {
    Environment = "production"
  }
}

data "aws_subnet_ids" "main" {
  vpc_id = data.aws_vpc.main.id
}

module "consul_cluster" {
  source = "./modules/aws-consul-cluster"

  vpc_id     = data.aws_vpc.main.id
  subnet_ids = data.aws_subnet_ids.main.ids
}

有條件的創(chuàng)建對象

在跨多個環(huán)境使用同一個模塊的情況下,通常會看到一些必要的對象已經(jīng)存在于某些環(huán)境中碌秸,但在其他環(huán)境中還需要創(chuàng)建绍移。

例如悄窃,這可能出現(xiàn)在開發(fā)環(huán)境場景中:出于成本原因,某些基礎(chǔ)架構(gòu)可能會在多個開發(fā)環(huán)境中共享蹂窖,而在生產(chǎn)環(huán)境中轧抗,基礎(chǔ)架構(gòu)是唯一的,并由生產(chǎn)配置直接管理瞬测。

我們建議采用依賴倒置的方式:讓模塊通過輸入變量接受它需要的對象作為參數(shù)横媚,而不是嘗試編寫一個檢測其存在并創(chuàng)建它的模塊。

例如月趟,考慮一個 Terraform 模塊基于磁盤映像部署計算實例的情況灯蝴,并且在某些環(huán)境中有一個專用磁盤映像可用,而其他環(huán)境共享一個公共基礎(chǔ)磁盤映像孝宗。與其讓模塊本身處理這兩種情況穷躁,不如為表示磁盤映像的對象聲明一個輸入變量。以 AWS EC2 為例碳褒,我們可以聲明 aws_ami 資源類型和數(shù)據(jù)源模式的公共子類型:

variable "ami" {
  type = object({
    # 僅使用模塊所需的屬性子集聲明對象折砸。 
    # Terraform 將允許任何至少具有這些屬性的對象看疗。
    id           = string
    architecture = string
  })
}

該模塊的調(diào)用者現(xiàn)在可以自己直接表示這是要內(nèi)聯(lián)創(chuàng)建的 AMI 還是要從其他地方檢索的 AMI:

# 這種情形下我們將自己管理 AMI

resource "aws_ami_copy" "example" {
  name              = "local-copy-of-ami"
  source_ami_id     = "ami-abc123"
  source_ami_region = "eu-west-1"
}

module "example" {
  source = "./modules/example"

  ami = aws_ami_copy.example
}

# 或者沙峻,AMI 已經(jīng)在某處存在了

data "aws_ami" "example" {
  owner = "9999933333"

  tags = {
    application = "example-app"
    environment = "dev"
  }
}

module "example" {
  source = "./modules/example"

  ami = data.aws_ami.example
}

這與 Terraform 的聲明式風格一致:我們并不構(gòu)建條件分支復雜的模塊,而是直接描述應(yīng)該存在的內(nèi)容以及希望 Terraform 管理的內(nèi)容两芳。

通過遵循這種風格摔寨,我們可以確定在哪些情況下應(yīng)該 AMI 存在,哪些情況下不應(yīng)該存在怖辆。維護配置的人以后可以了解這些配置的意圖是复,而無需檢查云上的狀態(tài)。

在上面的示例中竖螃,要創(chuàng)建或讀取的對象非常簡單淑廊,可以作為單個資源內(nèi)聯(lián)提供,但是在依賴項本身足夠復雜以從中受益的情況下特咆,我們也可以將多個模塊組合在一起季惩,如本頁其他地方所述的一樣。

多云(Multi-cloud)抽象

Terraform 本身不會嘗試抽象不同供應(yīng)商提供的類似服務(wù)腻格,因為我們希望在每個產(chǎn)品中開放全部功能画拾,但在單個接口后面統(tǒng)一多個產(chǎn)品往往需要“最小公分母”方法。

但是菜职,通過 Terraform 模塊的組合青抛,可以通過自己權(quán)衡哪些平臺功能對您很重要來創(chuàng)建自己的輕量級多云抽象。

在多個供應(yīng)商實現(xiàn)相同概念酬核、協(xié)議或開放標準的任何情況下蜜另,都會出現(xiàn)這種抽象的機會适室。例如,域名系統(tǒng)的基本功能在所有供應(yīng)商中都是通用的举瑰,盡管一些供應(yīng)商通過地理定位和智能負載平衡等獨特功能來區(qū)分自己亭病,但您可能會得出結(jié)論,在您的用例中您愿意避開這些功能作為對創(chuàng)建模塊的回報嘶居,這些模塊將多個供應(yīng)商的通用 DNS 概念抽象化:

module "webserver" {
  source = "./modules/webserver"
}

locals {
  fixed_recordsets = [
    {
      name = "www"
      type = "CNAME"
      ttl  = 3600
      records = [
        "webserver01",
        "webserver02",
        "webserver03",
      ]
    },
  ]
  server_recordsets = [
    for i, addr in module.webserver.public_ip_addrs : {
      name    = format("webserver%02d", i)
      type    = "A"
      records = [addr]
    }
  ]
}

module "dns_records" {
  source = "./modules/route53-dns-records"

  route53_zone_id = var.route53_zone_id
  recordsets      = concat(local.fixed_recordsets, local.server_recordsets)
}

在上面的示例中罪帖,我們以“記錄集”的形式創(chuàng)建了一個輕量級的抽象。這個抽象包含描述應(yīng)該可映射到任何 DNS 供應(yīng)商的 DNS 記錄的一般概念的屬性邮屁。

然后整袁,我們將該抽象實例化為一個模塊。在本例中將記錄集部署到 AWS 的 Route53 服務(wù)上佑吝。

如果你想以后切換到不同的 DNS 供應(yīng)商坐昙,只需將 dns_records 模塊中的內(nèi)容替換為新供應(yīng)商的實現(xiàn),從而使記錄集中定義的所有記錄配置保持不變芋忿。

你可以在 Terraform 通過定義代表所涉及概念的對象炸客,然后將這些對象類型用于模塊輸入變量來創(chuàng)建像這樣的輕量級抽象。 在這種情況下戈钢,所有的“DNS 記錄”實現(xiàn)都將聲明以下變量:

variable "recordsets" {
  type = list(object({
    name    = string
    type    = string
    ttl     = number
    records = list(string)
  }))
}

DNS 只是一個簡單的示例痹仙,但仍有更多機會利用供應(yīng)商之間的通用元素。 一個更復雜的例子是部署 Kubernetes 集群殉了,現(xiàn)在有許多不同的供應(yīng)商提供托管的 Kubernetes 集群服務(wù)开仰,甚至還有更多運行 Kubernetes 的方法。

如果所有這些實現(xiàn)中的通用功能足以滿足您的需求薪铜,您可以選擇實現(xiàn)一組不同的模塊來描述特定的 Kubernetes 集群實現(xiàn)众弓,并且都具有將集群的主機名導出為輸出值的共同特征:

output "hostname" {
  value = azurerm_kubernetes_cluster.main.fqdn
}

然后,您可以編寫僅期望 Kubernetes 集群主機名作為輸入的其他模塊隔箍,并將它們與您的任何 Kubernetes 集群模塊互換使用:

module "k8s_cluster" {
  source = "modules/azurerm-k8s-cluster"

  # (Azure-specific configuration arguments)
}

module "monitoring_tools" {
  source = "modules/monitoring_tools"

  cluster_hostname = module.k8s_cluster.hostname
}

只讀模塊

大多數(shù)模塊都包含 resource 部分谓娃,它描述了要創(chuàng)建和管理的基礎(chǔ)設(shè)施。有時編寫根本不描述任何新基礎(chǔ)設(shè)施蜒滩,而只用來檢索有關(guān)使用data sources在其他地方創(chuàng)建的基礎(chǔ)設(shè)施信息也是一種常見的方式滨达。

作為模塊的使用約定,我們建議僅在模塊以某種方式提高抽象級別時才用這種用法帮掉。在這種情況下會通過精確封裝的數(shù)據(jù)的檢索方式弦悉。

這種技術(shù)的一個常見用途是當一個系統(tǒng)被分解為幾個子系統(tǒng)配置,但某些基礎(chǔ)設(shè)施在各子子系統(tǒng)之間共享的時候蟆炊。例如一個公共 IP 網(wǎng)絡(luò)稽莉。 在這種情況下,我們可能會編寫一個名為 join-network-aws 的共享模塊涩搓,當部署在 AWS 中時污秆,任何需要共享網(wǎng)絡(luò)信息的配置都可以調(diào)用該模塊:


module "network" {
  source = "./modules/join-network-aws"

  environment = "production"
}

module "k8s_cluster" {
  source = "./modules/aws-k8s-cluster"

  subnet_ids = module.network.aws_subnet_ids
}

網(wǎng)絡(luò)模塊本身可以通過多種不同的方式檢索這些數(shù)據(jù):它可以使用 aws_vpcaws_subnet_ids 數(shù)據(jù)源直接查詢 AWS API劈猪,或者它可以使用 consul_keys 從 Consul 集群中讀取保存的信息,或者它可以直接從 使用 terraform_remote_state 管理網(wǎng)絡(luò)的配置狀態(tài)良拼。

這種方法的主要好處是战得,此信息的來源可以隨時間變化,而無需更新依賴它的每個配置庸推。 此外常侦,如果您將純數(shù)據(jù)模塊設(shè)計為具有與相應(yīng)管理模塊相似的一組輸出,則在重構(gòu)時可以相對輕松地在兩者之間進行切換贬媒。

(完)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末聋亡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子际乘,更是在濱河造成了極大的恐慌坡倔,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脖含,死亡現(xiàn)場離奇詭異罪塔,居然都是意外死亡,警方通過查閱死者的電腦和手機养葵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門征堪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人港柜,你說我怎么就攤上這事请契】劝瘢” “怎么了夏醉?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涌韩。 經(jīng)常有香客問我畔柔,道長,這世上最難降的妖魔是什么臣樱? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任靶擦,我火速辦了婚禮,結(jié)果婚禮上雇毫,老公的妹妹穿的比我還像新娘玄捕。我一直安慰自己,他們只是感情好棚放,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布枚粘。 她就那樣靜靜地躺著,像睡著了一般飘蚯。 火紅的嫁衣襯著肌膚如雪馍迄。 梳的紋絲不亂的頭發(fā)上福也,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機與錄音攀圈,去河邊找鬼暴凑。 笑死,一個胖子當著我的面吹牛赘来,可吹牛的內(nèi)容都是我干的现喳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼犬辰,長吁一口氣:“原來是場噩夢啊……” “哼拿穴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起忧风,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤默色,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后狮腿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腿宰,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年缘厢,在試婚紗的時候發(fā)現(xiàn)自己被綠了吃度。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡贴硫,死狀恐怖椿每,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情英遭,我是刑警寧澤间护,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站挖诸,受9級特大地震影響汁尺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜多律,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一痴突、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧狼荞,春花似錦辽装、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春殷勘,著一層夾襖步出監(jiān)牢的瞬間此再,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工玲销, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留输拇,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓贤斜,卻偏偏與公主長得像策吠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瘩绒,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容