如何使用 Terraform 和 Git 分支有效管理多環(huán)境?

作者|Sumeet Ninawe
翻譯|Seal軟件
鏈接|https://spacelift.io/blog/terraform-environments
?
通常我們使用 Terraform 將我們的基礎(chǔ)設(shè)施定義為代碼,然后用 Terraform CLI 在我們選擇的云平臺(tái)中創(chuàng)建制定的基礎(chǔ)設(shè)施組件。從表面上看,整個(gè)過(guò)程看起來(lái)似乎不需要花費(fèi)太多精力。然而當(dāng)我們深入研究將其用于真實(shí)場(chǎng)景時(shí),很快就會(huì)遇到有關(guān)管理子生產(chǎn)和生產(chǎn)環(huán)境的問(wèn)題。
?
在這篇文章中,我們將重點(diǎn)關(guān)注如何使用 Terraform Workspace 和 Git 分支來(lái)有效管理多個(gè)環(huán)境。
?
多環(huán)境基礎(chǔ)設(shè)施
下面列出了使用 IaC 管理多個(gè)環(huán)境的基礎(chǔ)設(shè)施的期望和要求:
可以使用相同的 IaC 配置來(lái)管理生產(chǎn)和非生產(chǎn)環(huán)境。
某些非生產(chǎn)環(huán)境,如開(kāi)發(fā)、QA、測(cè)試版或 UAT 應(yīng)該與生產(chǎn)的相同和縮小版本并永久存在。
團(tuán)隊(duì)成員能夠創(chuàng)建、管理和銷(xiāo)毀與生產(chǎn)相同的臨時(shí)環(huán)境。
所有環(huán)境并不是在同一個(gè)云帳戶(hù)或訂閱中創(chuàng)建的。
?
這里的關(guān)鍵點(diǎn)在于對(duì)所有環(huán)境中的基礎(chǔ)設(shè)施使用相同的 Terraform 配置模板。
?
Terraform Workspace
Terraform 提供了一個(gè)工作區(qū)功能,使用該功能可以使用相同的配置創(chuàng)建和管理多個(gè)相同的、按比例縮小的環(huán)境。以這種方式創(chuàng)建的多個(gè)環(huán)境是完全隔離的,不會(huì)以任何方式相互干擾。這是我們需要的關(guān)鍵功能。一起看看如何利用這個(gè)功能來(lái)滿(mǎn)足我們的要求。
?
Terraform Workspace 不同于 Terraform Cloud Workspace。在 Terraform Cloud 中,工作空間類(lèi)似于一個(gè)“項(xiàng)目”,對(duì)應(yīng)于 Terraform 配置存儲(chǔ)庫(kù)。除了存儲(chǔ)和管理狀態(tài)信息外,還管理變量、憑證、歷史跟蹤等,以支持端到端的 Terraform Cloud CI/CD 工作流。
?
用于處理工作區(qū)的 Terraform CLI 命令
下表表示 Terraform 工作區(qū)命令的基本用法。每個(gè)命令都遵循如下簡(jiǎn)單格式:terraform workspace <command>
?

?
下面的 CLI 輸出顯示了管理工作區(qū)的示例。簡(jiǎn)而言之,我們檢查當(dāng)前選擇的工作空間——默認(rèn),然后創(chuàng)建一個(gè)名為 beta 的新工作空間,列出所有工作空間,并刪除 beta 工作空間。
?
% terraform workspace show
default
% terraform workspace new beta?
Created and switched to workspace "beta"!?
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.?
% terraform workspace list
?default
* beta?
% terraform workspace select default
Switched to workspace "default".?
% terraform workspace delete beta?
Releasing state lock.?
This may take a few moments...?
Deleted workspace "beta"!?
% terraform workspace list?
* default
?
工作區(qū)插值
要使用相同的配置管理多個(gè)按比例縮小的環(huán)境,需要有一種方法讓 Terraform 知道我們正在使用哪個(gè)工作區(qū),這有助于正確設(shè)置配置。例如,我們可能希望為特定工作區(qū)管理的環(huán)境提供更多的 EC2 實(shí)例,為其他環(huán)境提供更少的實(shí)例。
?
Terraform 工作空間插值序列為我們提供了一種實(shí)現(xiàn)這種動(dòng)態(tài)變化的方法。通過(guò)訪(fǎng)問(wèn)所選工作區(qū)的值,我們可以使用多個(gè)構(gòu)造和運(yùn)算符來(lái)創(chuàng)建具有所需規(guī)模和其他自定義屬性的環(huán)境。
?
考慮下面的例子。在這里,Terraform 配置旨在在 AWS 中創(chuàng)建 EC2 實(shí)例。但是,基于所選工作區(qū)的計(jì)數(shù)屬性定義了要?jiǎng)?chuàng)建的實(shí)例數(shù)。這里,“terraform.workspace”插值序列用于訪(fǎng)問(wèn)它。
?
resource "aws_instance" "my_vm" {
?count = terraform.workspace == "default" ? 3 : 1
?ami = var.ami //Ubuntu AMI
instance_type = var.instance_type?
tags = {
? Name = format("%s_%s_%s", var.name_tag, terraform.workspace, count.index)
?}?
}
?
如果選擇“默認(rèn)”工作區(qū),則將創(chuàng)建三個(gè) EC2 實(shí)例,否則只創(chuàng)建一個(gè)。當(dāng)然,這只是一個(gè)例子。我們可以使用更復(fù)雜的變量和運(yùn)算符來(lái)管理更多的環(huán)境。
?
基礎(chǔ)設(shè)施和應(yīng)用程序開(kāi)發(fā)
端到端產(chǎn)品開(kāi)發(fā)涉及幾個(gè)方面——基礎(chǔ)設(shè)施和要部署在基礎(chǔ)設(shè)施上的應(yīng)用程序。通常,會(huì)有對(duì)應(yīng)的個(gè)人團(tuán)隊(duì)來(lái)負(fù)責(zé)相應(yīng)的任務(wù)。在微服務(wù)中,由于依賴(lài)性和資源限制,在本地機(jī)器上測(cè)試和開(kāi)發(fā)應(yīng)用程序可能并不總是可行的。應(yīng)用程序團(tuán)隊(duì)成員可能需要調(diào)整臨時(shí)環(huán)境來(lái)運(yùn)行他們的測(cè)試用例,甚至在將更改部署到“永久”開(kāi)發(fā)環(huán)境之前。
?
在這種情況下,無(wú)需擔(dān)心 Terraform 源代碼,因?yàn)樗麄兛梢院?jiǎn)單地克隆存儲(chǔ)庫(kù),然后使用工作區(qū)功能創(chuàng)建自己的臨時(shí)環(huán)境。這種能力對(duì)于應(yīng)用程序開(kāi)發(fā)團(tuán)隊(duì)來(lái)說(shuō)非常有用,可以在將更改合并到 dev 并將它們向前推進(jìn)之前單獨(dú)運(yùn)行他們的測(cè)試用例。
?
帳戶(hù)和憑據(jù)
多個(gè)環(huán)境通常使用多個(gè)云帳戶(hù)或訂閱進(jìn)行管理。云平臺(tái)還實(shí)施了“組織”概念,以從單個(gè)根帳戶(hù)管理多個(gè)帳戶(hù)。此根帳戶(hù)負(fù)責(zé)所有管理活動(dòng),如計(jì)費(fèi)、訪(fǎng)問(wèn)配置等。當(dāng) Terraform 配置被“應(yīng)用”時(shí),更改將根據(jù)其提供者配置為目標(biāo)帳戶(hù)進(jìn)行驗(yàn)證和執(zhí)行。
?
provider "aws" {
? shared_config_files = ["/path/to/.aws/conf"]
? shared_credentials_files = ["/path/to/.aws/creds"]
? profile = "profile_name"
}
?
在這里,我們對(duì)配置文件名稱(chēng)進(jìn)行了硬編碼,以便 Terraform 為目標(biāo)帳戶(hù)使用適當(dāng)?shù)膽{據(jù)。在這里,我們還可以利用工作區(qū)插值序列從共享憑證文件中動(dòng)態(tài)選擇配置文件名稱(chēng)。
?
Terraform Workspace 小結(jié)
在使用 Terraform 管理多個(gè)環(huán)境時(shí),Terraform 中的狀態(tài)管理可能是一個(gè)敏感話(huà)題。不過(guò) Terraform 提供的工作區(qū)管理可以通過(guò)在當(dāng)前設(shè)置的后端中創(chuàng)建子目錄來(lái)在后臺(tái)處理此問(wèn)題。狀態(tài)管理也可能是一個(gè)限制因素,因?yàn)樗袪顟B(tài)文件都存儲(chǔ)在同一個(gè)后端目錄中。這意味著所有用于 terraform 配置的插件也會(huì)在每個(gè)工作區(qū)中被復(fù)制。
?
Terraform 工作區(qū)提供了一種創(chuàng)建瞬態(tài)環(huán)境的好方法,只需學(xué)習(xí)一些命令即可測(cè)試基礎(chǔ)設(shè)施的變化。依靠?jī)?nèi)部布線(xiàn)——使用插值序列。如果代碼已經(jīng)構(gòu)建,引入工作空間插值依賴(lài)性可能需要一些努力。
?
Git 分支
在本節(jié)中,我們將探索使用 Git 分支管理多個(gè)環(huán)境的可能性,并了解為什么它可能不是最佳策略。下圖旨在滿(mǎn)足本博文介紹中所述的要求。
?

?
開(kāi)發(fā)的兩個(gè)方面——基礎(chǔ)設(shè)施和應(yīng)用程序,分別以綠色和藍(lán)色顯示。此處表示的分支策略是將 Terraform 配置用于各種目的的應(yīng)用。我們將深入探討 Git 分支的各個(gè)部分。
?
Git 的目的
簡(jiǎn)而言之,Git 旨在協(xié)調(diào)整個(gè)團(tuán)隊(duì)的開(kāi)發(fā)工作。它為部署維護(hù)各種版本的源代碼和包版本。主分支通常包含經(jīng)過(guò)充分測(cè)試的功能,適用于任何給定軟件的一般用途。
?
執(zhí)行開(kāi)發(fā)活動(dòng)或以錯(cuò)誤修復(fù)、功能或增強(qiáng)的形式引入任何更改——?jiǎng)?chuàng)建主分支的副本,在其上執(zhí)行修改、重建、部署到子生產(chǎn)環(huán)境,并在合并之前進(jìn)行徹底測(cè)試對(duì)主要分支的更改。
?
環(huán)境的 Git 分支
考慮到這一點(diǎn),使用 Git 分支來(lái)管理多個(gè)環(huán)境也是可以的,每個(gè)環(huán)境一個(gè)分支是非常理想的。在給定的圖表中,infra-dev 團(tuán)隊(duì)在三個(gè)分支上工作:
Main?– 用于管理生產(chǎn)基礎(chǔ)設(shè)施設(shè)置。
QA?– 用于管理 QA 基礎(chǔ)設(shè)施設(shè)置,合格用戶(hù)在其中執(zhí)行 UAT 測(cè)試。
Dev?– 用于管理開(kāi)發(fā)基礎(chǔ)架構(gòu)設(shè)置,其中首先發(fā)布功能并進(jìn)行單元測(cè)試。
?
在高層次上,從主分支分出并創(chuàng)建相同配置的副本以創(chuàng)建 QA 和開(kāi)發(fā)環(huán)境是有意義的。
?
存在的問(wèn)題
然而,當(dāng)我們將 Terraform 的更深層次視為 IaC 時(shí),我們需要關(guān)注一些點(diǎn):
狀態(tài)文件管理和關(guān)聯(lián)的遠(yuǎn)程后端。
縮放轉(zhuǎn)化為環(huán)境特定屬性方面。
多個(gè)帳戶(hù)的憑據(jù)。
?
這里考慮的環(huán)境是單獨(dú)的基礎(chǔ)設(shè)施部署。這些環(huán)境都有自有的狀態(tài)信息,需要對(duì)其進(jìn)行遠(yuǎn)程安全管理。遠(yuǎn)程后端在 terraform 資源塊中定義。
?
下面的示例使用 AWS S3 后端。
?
terraform {
?required_providers {
? ?aws = {
? ? ?source = "hashicorp/aws"
? ? ?version = "~> 4.18.0"
? ?}
?} ?backend "s3" {
? ?bucket = "tf-state-bucket"
? ?key = "terraform.tfstate"
? ?region = "eu-central-1"
? ?dynamodb_table = "tf_state_lock"
?}
}
?
假設(shè)這是生產(chǎn)環(huán)境使用的配置,即主分支。當(dāng)我們從主分支分出來(lái)時(shí),后端配置也會(huì)被復(fù)制。所有 Terraform CLI 命令都假定此后端對(duì)于所有其他副本(分支)都相同,但這是不可取的,而且可能是非常危險(xiǎn)的。事實(shí)上,運(yùn)行任何 Terraform 命令(如 plan、apply、destroy)將引用生產(chǎn)狀態(tài)文件,甚至對(duì)生產(chǎn)執(zhí)行操作。
?
如果我們手動(dòng)修改后端配置,為 QA 和開(kāi)發(fā)環(huán)境使用不同的后端,那么它就違背了 Git 的初衷。Git 合并會(huì)產(chǎn)生沖突,并要求開(kāi)發(fā)人員通過(guò)選擇后端來(lái)解決這些沖突。
?
這個(gè)情況也適用于.tfvars 文件中定義的特定于環(huán)境的屬性值。各種環(huán)境的擴(kuò)展方面是通過(guò)變量管理的——更具體地說(shuō),是 .tfvars 文件?,F(xiàn)代 Git 工作流通常需要在任何分支上進(jìn)行推送和拉取。在這種方法中可能是不可行的。
?
提供商配置可能包含多個(gè)別名以表示多個(gè)云提供商帳戶(hù)和區(qū)域中的部署。這也被 Git 的優(yōu)點(diǎn)所覆蓋。
?
CI/CD 流水線(xiàn)
大多數(shù)遠(yuǎn)程 Git 存儲(chǔ)庫(kù)都提供了以 CI/CD 流水線(xiàn)的形式引入自動(dòng)化的能力。值得注意的是 GitHub Actions 和 Gitlab CI/CD。就源代碼版本控制而言,使用遠(yuǎn)程 Git 存儲(chǔ)庫(kù)并定義處理憑據(jù)的自動(dòng)化流水線(xiàn)是有意義的。在我們的示例中,如果我們?cè)谔囟ǚ种线M(jìn)行提交或批準(zhǔn)拉取請(qǐng)求,則可以運(yùn)行特定于分支的管道,該管道使用特定于環(huán)境的憑證將更改應(yīng)用到正確的目標(biāo)環(huán)境。
?
然而,即使這解決了憑據(jù)問(wèn)題,特定于環(huán)境的提供程序配置和屬性仍然是 Git 工作流程的一部分。這與 Terraform 期望這些配置用于我們想要的更改方式不一致。此外,CI/CD 流水線(xiàn)功能是任何其他 Terraform 工作流都可以利用的功能。所以這不會(huì)增加依賴(lài) Git 分支的任何特定優(yōu)勢(shì)。
?
應(yīng)用開(kāi)發(fā)
現(xiàn)代應(yīng)用程序開(kāi)發(fā)基于微服務(wù)、容器和函數(shù)。通常,本地開(kāi)發(fā)環(huán)境是開(kāi)發(fā)團(tuán)隊(duì)遇到的一個(gè)問(wèn)題,具體取決于各種因素。一個(gè)簡(jiǎn)單的例子——當(dāng)運(yùn)行一組相互依賴(lài)和其他因素的容器時(shí),可能會(huì)發(fā)現(xiàn)開(kāi)發(fā)機(jī)器上可用的資源不夠用。
?
使用 Terraform 作為 IaC 確實(shí)有助于旋轉(zhuǎn)臨時(shí)和隔離的環(huán)境來(lái)為開(kāi)發(fā)人員執(zhí)行單元測(cè)試。還可以從所需的源分支(主分支、QA 分支或開(kāi)發(fā)分支)創(chuàng)建一個(gè)臨時(shí) Git 分支,并創(chuàng)建一個(gè)隔離的縮小環(huán)境——如上圖中的“Temp2”部署所示。
?
此外,假設(shè)任何應(yīng)用程序功能都依賴(lài)于仍在開(kāi)發(fā)中的特定基礎(chǔ)結(jié)構(gòu)組件。在這種情況下,應(yīng)用程序團(tuán)隊(duì)可以選擇從基礎(chǔ)架構(gòu)開(kāi)發(fā)的“dev”分支分支出來(lái),其中包含預(yù)期的更改。它由圖中的“Temp1”部署表示。
?
應(yīng)該注意的是,使用 Git 分支策略管理環(huán)境使用了一個(gè)總體上的假設(shè),即擁有正確的分支策略。例如,應(yīng)用程序開(kāi)發(fā)團(tuán)隊(duì)創(chuàng)建的分支不能合并到任何基礎(chǔ)設(shè)施開(kāi)發(fā)團(tuán)隊(duì)的分支中。如果 Terraform 有辦法知道當(dāng)前檢出哪個(gè)分支,那么采用 Git 分支策略會(huì)更有意義。在使用 Terraform 工作空間時(shí),工作空間插值序列提供了這個(gè)確切的功能。