iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0

原簡體中文教程連結: Introduction.《Terraform入門教程》


Terraform 模組是獨立的基礎架構即程式碼片段,抽象化了基礎架構部署的底層複雜性。Terraform 使用者透過使用預置的設定碼加速採用 IaC,並降低了使用門檻。所以,模組的作者應盡量遵循諸如清晰的程式碼結構以及 DRY("Dont't Repeat Yourself")原則的程式碼最佳實踐。

本篇指導討論了模組架構的原則,用以幫助讀者編寫易於組合、易於分享及重用的基礎設施模組。這些架構建議對使用任意版本 Terraform 的企業都有好處,某些諸如「私有模組註冊表(Registry)」的模式僅在Terraform Cloud 以及企業版中才能使用。(本文不對相關內容進行翻譯)

本文是 Terraform 模組文件的補充和擴充。

透過閱讀文本,讀者可以:

  1. 學習有關Terraform 模組創建的典型工作流程和基本原則。
  2. 探索遵循這些原則的範例場景。
  3. 學習如何透過協作改進Terraform 模組
  4. 了解如何建立一套使用模組的工作流程。

1.5.5.1.1. 模組創建的工作流程

要創建一個新模組,第一步是尋找一個早期採納者團隊,收集他們的需求。

與這支早期採納團隊一起工作使我們可以透過使用輸入變數以及輸出值來確保模組足夠靈活,從而打磨模組的功能。此外,還可以用最小的程式碼變更代價吸收其他有類似需求的團隊加入進來。這消除了程式碼重複,並縮短了交付時間。

完成以上任務後,需要謹記兩點:

  1. 將需求範圍劃分成合適的模組。
  2. 創建模組的最小可行產品(Minimum Viable Product, MVP)

1.5.5.1.1.1. 將需求範圍劃分成適當的模組

創建新 Terraform 模組時最具挑戰的方面之一是決定要包含哪些基礎設施資源。

模組設計應該是有主見的,並且被設計成能很好地完成一個目標。如果一個模組的功能或目的很難解釋,那麼這個模組可能太複雜了。在最初確定模組的範圍時,目標應足夠小且簡單,易於開始編寫。

當建構一個模組時,需要考慮以下三個方面:

  • 封裝:一組始終被一起部署的基礎設施資源在模組中包含更多的基礎設施資源簡化了終端用戶部署基礎設施的工作,但會使得模組的目的與需求變得更難理解。
  • 職責:限制模組職責的邊界如果模組中的基礎設施資源由多個群組來負責,使用該模組可能會意外違反職責分離原則。模組中僅包含職責邊界內的一組資源可以提升基礎設施資源的隔離性,並保護我們的基礎設施。
  • 變化頻率:隔離長短生命週期基礎架構資源舉例來說,資料庫基礎架構資源相對來說較為靜態,而團隊可能在一天內多次部署更新應用程式伺服器。在同一個模組中同時管理資料庫與應用程式伺服器使得保存狀態資料的重要基礎架構沒有必要地暴露在資料遺失的風險之中。

1.5.5.1.1.2. 創建模組的最小可行產品

如同所有類型的程式碼一樣,模組的開發永遠不會完成,永遠會有新的模組需求以及變更。擁抱變化,最初的模組版本應致力於滿足最小可行產品(MVP)的標準。以下是在設計最小可行產品時需要謹記的指導清單:

  • 永遠致力於交付至少可以滿足 80% 場景的模組
  • 模組中永遠不要處理邊緣場景。邊緣場景是很少見的。一個模組應該是一組可重複使用的程式碼。
  • 在最小可行產品中避免使用條件表達式。最小可行產品應縮小範圍,不應該同時完成多種任務。
  • 模組應該只將最常被修改的參數公開為輸入變數。一開始時,模組應該只提供最可能需要的輸入變數。
盡可能多輸出

在最小可行產品中輸出盡可能多的信息,即使目前沒有使用者需要這些資訊。這使得那些通常使用多個模組的終端使用者在使用該模組時更加輕鬆,可以使用一個模組的輸出作為下一個模組的輸入。

請記得在模組的 README 文件中記錄輸出值的文件。

1.5.5.1.2. 探索遵循這些原則的一個範例場景

某團隊想要透過 Terraform 創建一套包含 Web 層應用程式、App 層應用程式的基礎架構。

他們想要使用一個專用的 VPC,並遵循傳統的三層架構設計。他們的 Web 層應用程式需要一個自動伸縮群組(AutoScaling Group)。他們的 App 層服務需要一個自動伸縮組,一個 S3 儲存桶以及一個資料庫。下面的架構圖描述了期望的結果:

在這個場景中,一個負責從零開始撰寫 Terraform 程式碼的團隊,負責編寫一組用於配置基礎架構及應用的模組。負責應用程式的團隊成員將使用這些模組來配置他們需要的基礎架構。

請注意,雖然此範例使用了AWS 命名,但所描述的模式適用於所有雲端平台。

經過對應用程式團隊的需求進行審核,模組團隊將此應用程式基礎架構分割成如下模組:網路、Web、App、資料庫、路由,以及安全性。

當 Terraform 模組團隊完成模組開發後,他們應該將模組匯入到私人模組註冊表中,並且向對應的團隊成員宣傳模組的使用方法。舉例來說,負責網路的團隊成員將會使用開發的網路模組來部署和配置相應的應用程式網路。

1.5.5.1.2.1. 網路模組

網路模組負責網路基礎設施。它包含了網路存取控制清單(ACL)以及 NAT 閘道。它還可以包含應用程式所需的 VPC、子網路、對等連接以及 Direct Connect 等。

此模組包含這些資源是因為它們需要特定權限並且變化頻率較低

  1. 只有應用程式團隊中有權建立或修改網路資源的成員可以使用該模組。
  2. 該模組的資源不會經常變更。將它們組合在單獨的模組中可以保護它們免於暴露在沒有必要的資料遺失的風險之中。

網路模組傳回一組其他工作區(Workspace)以及模組可以使用的輸出值。如果VPC 的創建過程是由多個方面組成的,我們可能最終會需要將該模組進一步切割成具有不同功能的不同模組。

1.5.5.1.2.2. 應用程式模組

本場景中有兩個應用程式模組- 一個是 Web 層模組,另一個是 App 層模組。

Terraform 模組團隊完成這兩個模組的開發後,它們應分發給對應的團隊成員來部署他們的應用程式。隨著應用程式團隊的成員變得越來越熟悉 Terraform 程式碼,它們可以提出基礎架構方面的增強建議,或透過 Pull Request 配合他們自己的應用程式碼發布提交對基礎架構的變更請求。

Web 模組

Web 模組可建立和管理執行 Web 應用程式所需的基礎架構。它包含了負載平衡器和自動伸縮群組,同時也可以包含應用程式中使用的 EC2 虛擬機器執行個體、S3 儲存桶、安全性群組,以及日誌系統。此模組接收一個透過 Packer 預先建置的包含最新 Web 層應用程式發布版本代碼的虛擬機器映像的 AMI ID 作為輸入。

此模組包含這些資源是因為它們是高度封裝的,並且它們變化頻率較高

  1. 此模組中的資源高度內聚,並且與 Web 應用程式緊密相關(例如,此模組需要一個包含最新 Web 層應用程式程式碼版本的 AMI)。結果就是它們被編制進同一個模組,這樣 Web 應用團隊的成員就可以輕鬆地部署它們。
  2. 此模組的資源變更頻率較高(每次發布更新版本都需要更新對應基礎架構資源)。透過將它們組合在單獨的模組中,我們降低了將其他模組的資源暴露在沒有必要的資料遺失的風險中的可能性。
App 模組

App 模組建立和管理運行 App 層應用程式所需的基礎架構。它包含了負載平衡器和自動伸縮群組,同時也包含了應用程式中使用的 EC2 虛擬機器實例、S3 儲存桶、安全性群組,以及日誌系統。此模組接收一個透過 Packer 預先建置的包含最新 App 層應用程式發布版本代碼的虛擬機器映像的 AMI ID 作為輸入。

此模組包含這些資源是因為它們是高度封裝的,並且它們變化頻率較高

  1. 此模組中的資源高度內聚,並且與 App 應用程式緊密相關。結果就是它們被編制進同一個模組,這樣 App 層應用程式團隊的成員就可以輕鬆地部署它們。
  2. 此模組的資源變更頻率較高(每次發布更新版本都需要更新對應基礎架構資源)。透過將它們組合在單獨的模組中,我們降低了將其他模組的資源暴露在沒有必要的資料遺失的風險中的可能性。
資料庫模組

資料庫模組創建並管理了運行資料庫所需的基礎設施資源。它包含了應用程式所需的RDS 實例,也包含了所有關聯的儲存、備份以及日誌資源。

此模組包含這些資源是因為它們需要特定權限並且變化頻率較低

  1. 只有應用程式團隊中有權建立或修改資料庫資源的成員可以使用該模組。
  2. 該模組的資源不會經常變更。將它們組合在單獨的模組中可以保護它們免於暴露在沒有必要的資料遺失的風險之中。
路由模組

路由模組建立並管理網路路由所需的基礎設施資源。它包含了公共託管區域(Hosted Zone)、Route 53 以及路由表,也可以包含私人託管區域。

此模組包含這些資源是因為它們需要特定權限並且變化頻率較低

  1. 只有應用程式團隊中有權建立或修改路由資源的成員可以使用該模組。
  2. 該模組的資源不會經常變更。將它們組合在單獨的模組中可以保護它們免於暴露在沒有必要的資料遺失的風險之中。
安全模組

安全模組可建立並管理所有安全所需的基礎設施資源。它包含一組IAM 資源,也可以包含安全群組(Security Group)及多因子認證(MFA)。

此模組包含這些資源是因為它們需要特定權限並且變化頻率較低

  1. 只有應用程式團隊中有權創建或修改 IAM 或是安全資源的成員可以使用該模組。
  2. 該模組的資源不會經常變更。將它們組合在單獨的模組中可以保護它們免於暴露在沒有必要的資料遺失的風險之中。

1.5.5.1.3. 建立模組的提示

除了範圍界定之外,我們在創建模組時還應牢記以下幾點:

1.5.5.1.3.1. 嵌套模組

嵌套模組是指在目前模組中對另一個模組的引用。嵌套模組可以是外部的,也可以是目前工作空間內的。使用嵌套模組是一項強大的功能;然而我們必須謹慎實踐以避免引入錯誤。

對於所有類型的嵌套模組,請考慮以下事項:

  • 嵌套模組可以加速開發速度,但可能會引發未知以及意料之外的結果。請在文件中清楚記錄輸入變數、模組行為以及輸出值。
  • 通常來說,不要讓主模組的嵌套深度超過兩層。常用且簡單的工具模組,例如專門用來定義 Tag 的模組,則不受此限制制約。
  • 嵌套模組必須包含必要的用來建立指定的資源配置的輸入參數以及輸出值。
  • 輸入參數以及輸出值的命名應遵循一致的命名約定,以使得模組可以更容易被分享,以及將一個模組的的輸出值作為另一個模組的輸入參數。
  • 嵌套模組可能會導致程式碼冗餘。必須同時在父模組與嵌套模組中聲明輸入參數和輸出值。
嵌套的外部模組

當我們需要使用那些定義了被多個應用程式堆疊、應用程式和團隊重複使用的標準化資源的通用模組時,嵌套的外部模組會很有用。外部模組通被集中管理和版本化控制,以使得消費者在使用新版本之前可以對其進行驗證。當我們依賴或希望使用位於外部的子模組時,請注意以下幾點:

  • 外部模組必須獨立維護,並可供任何需要呼叫它的模組使用。使用模組註冊表可以確保這一點。
  • 根據模組註冊要求,嵌套模組將擁有自己的版本控製程式碼倉庫,獨立於呼叫模組進行版本控制。
  • 對嵌套模組的變更可能會影響呼叫模組,即使呼叫模組的呼叫程式碼及版本沒有變化,這會破壞呼叫程式碼的信任。
  • 對呼叫模組如何使用外部模組在文件中進行記錄,使得模組行為以及呼叫關係可以被輕鬆理解。
  • 對外部模組的變更應該是向後相容的。如果向後相容是不可能的,則應清楚地記錄需要對任何呼叫模組進行的更改,並將之分發給所有模組使用者以避免意外。
嵌套的嵌入模組

在目前工作空間中嵌入一個模組使得我們能夠清楚地分離模組的邏輯元件,或是建立可在呼叫模組執行期間多次呼叫的可重複使用程式碼區塊。在下面的例子中,ec2-instance 是一個嵌入模組,根模組的 main.tf 引用了這個模組:

root-module-directory
├── README.md
├── main.tf
└── ec2-instances
    └── main.tf

如果我們需要或傾向於使用嵌入模組,需要考慮以下幾點:

  • 在「根模組」中加入嵌入模組意味著子模組與根模組被放在一起進行版本控制。
  • 任何影響兩個模組間相容性的變更都會被快速發現,因為它們必須被一同測試和發布。
  • (嵌入的)子模組不能被程式碼樹之外的其他模組調用,所以可能會增加重複的程式碼。舉例來說,如果嵌入的 ec2-instance 模組是用來創建一台被用在多個地方的標準化的計算實例,該模組無法以這種形式被分享。
標籤化模組名並記錄在文件中

為我們的模組創建並遵循一個命名約定將使得模組易於理解與使用。這將促進模組的採用和貢獻。以下是一個用來提升模組元素一致性的建議清單:

  • 使用一個對人類來說一致且易於理解的模組命名約定。舉例來說:
terraform cloud provider function full name
terraform aws consul cluster terraform-aws-consul_cluser
terraform aws security module terraform-aws-security
terraform azure database terraform-azure-database
  • 使用人類可以理解的輸入變數命名約定。模組是編寫一次並多次使用的程式碼,因此請完整命名所有內容以提升可讀性,並在編寫程式碼時在文件中進行記錄。
  • 對所有模組進行文檔記錄。確保文件中包含有:
    • 必填的輸入變數:這些輸入變數應該是經過深思熟慮後的選擇。如果這些輸入變數值未定義,模組運作將會失敗。只在必要時為這些輸入變數設定預設值。例如 var.vpc_id 永遠不應該有預設值,因為每次使用模組時值都會不同。
    • 可選的輸入變數:這些輸入變數應該有一個合理的,適用於大多數場景的預設值,同時又可以根據需求進行調整。公告輸入變數的預設值。例如 var.elb_idle_timeout 會有一個合理的預設值,但呼叫者也可以根據需求修改它的值。
    • 輸出值:列出模組的所有輸出值,並將重要的輸出和資訊性的輸出包裝在對使用者友善的輸出範本中。
定義並使用一個一致的模組結構

雖然模組結構是一個品味問題,我們應該將模組的結構記錄在文件中,並且在我們的所有模組之間保持統一的結構。為了要維持模組結構的一致:

  • 定義一組模組必須包含的 .tf 文件,定義它們應包含哪些內容
  • 為模組定義一個 .gitignore (或類似作用的)文件
  • 建立供樣例程式碼所使用的輸入變數值的標準方式(例如一個 terraform.tfvars.example 檔案)
  • 使用具有固定子目錄的一致的目錄結構,即使它們可能是空的
  • 所有模組目錄都必須包含一個 README 文件詳細記述目錄存在的目的以及如何使用其中的文件

1.5.5.1.4. 模組的協作

隨著團隊模組的開發工作,簡化我們的協作。

  1. 為每個模組建立路線圖
  2. 從使用者收集需求信息,並按受歡迎程度進行優先排序。
    • 不使用模組的最常見原因是「它不符合我的要求」。收集這些需求並將它們新增至路線圖或對使用者的工作流程提出建議。
    • 檢查每一項需求以確認它所引用的用例是否正確。
    • 公佈和維護需求清單。分享該清單並讓使用者參與清單管理過程。
    • 不要為邊緣用例排期。
  3. 將每一個決策記錄進文件。
  4. 在公司內部採用開源社群原則。有些用戶希望盡可能有效率地使用這些模組,而有些用戶則希望協助創建這些模組。
    • 創建一個社區
    • 維護一份清晰和公開的貢獻指引
    • 最終,我們將允許可信的社區成員獲得某些模組的所有權

1.5.5.1.5. 使用原始碼控制系統追蹤模組

一個 Terraform 模組應遵守所有良好的程式碼實踐:

  • 將模組置於原始碼控制中以管理版本發布、協作、變更的審計追蹤。
  • 為所有 main 分支的發布版本建立版本標籤,記錄文件(最起碼在 CHANGELOGREADME 中記錄)。
  • main 分支的所有變更進行程式碼審查
  • 鼓勵模組的使用者透過版本標籤引用模組
  • 為每一個模組指派一位負責人
  • 一個代碼倉庫只負責一個模組
    • 這對於模組的冪等性和作為函式庫的功能至關重要。
    • 我們應該對模組打上版本標籤或是版本化控制。打上版本標籤或是版本化的模組應該是不可變的。
    • 發佈到私有模組註冊表的模組必須要有版本標籤。

1.5.5.1.6. 開發一套模組消費工作流程

定義並宣傳一套消費者團隊使用模組時應遵循的可重複工作流程。這個工作流程,就像模組本身一樣,應該考慮到使用者的需求。

1.5.5.1.6.1. 闡明團隊應該如何使用模組

  • 分散的安全性:如果每個模組都在自己的儲存庫中進行版本控制,則可以使用儲存庫 RBAC 來管理誰擁有寫入存取權限,從而允許相關團隊管理相關的基礎設施(例如網路團隊擁有對網路模組的寫入存取權限)。
  • 培育程式碼社群:鑑於上述建議,模組開發的最佳實踐是允許對儲存在私人模組儲存庫中的模組的所有模組儲存庫提出 Pull Request。這促進了組織內的程式碼社區,保持模組內容的相關性和最大的靈活性,並有助於保持模組註冊表的長期有效性。

原簡體中文教程連結: Introduction.《Terraform入門教程》


上一篇
Day17-【入門教程】重構
下一篇
Day19-【入門教程】Terraform 命令行及命令列設定檔(.terraformrc或terraform.rc)
系列文
Terraform 繁體中文25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言