View a markdown version of this page

程式碼基礎結構和組織的最佳實務 - AWS 方案指引

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

程式碼基礎結構和組織的最佳實務

隨著 Terraform 用量在大型團隊和企業中成長,適當的程式碼基礎結構和組織至關重要。架構良好的程式碼基礎可大規模協作,同時增強可維護性。

本節提供有關支援品質和一致性的 Terraform 模組化、命名慣例、文件和編碼標準的建議。

指引包括依環境和元件將組態分解為可重複使用的模組、使用字首和尾碼建立命名慣例、記錄模組並清楚解釋輸入和輸出,以及使用自動化樣式檢查套用一致的格式規則。

其他最佳實務涵蓋在結構化階層中邏輯組織模組和資源、在文件中編目公有和私有模組,以及在模組中抽象化不必要的實作詳細資訊,以簡化使用。

透過實作有關模組化、文件、標準和邏輯組織的程式碼基礎結構指導方針,您可以支援跨團隊的廣泛協作,同時隨著整個組織的用量分散,保持 Terraform 的可維護性。透過強制執行慣例和標準,您可以避免分段程式碼基礎的複雜性。

實作標準儲存庫結構

我們建議您實作下列儲存庫配置。跨模組標準化這些一致性實務可改善可探索性、透明度、組織和可靠性,同時可在許多 Terraform 組態間重複使用。

  • 根模組或目錄:這應該是 Terraform 模組和可重複使用模組的主要進入點,並且預期是唯一的。如果您有更複雜的架構,您可以使用巢狀模組來建立輕量型抽象。這可協助您在架構方面描述基礎設施,而不是直接在實體物件方面描述基礎設施。

  • README:根模組和任何巢狀模組都應具有 README 檔案。此檔案必須命名為 README.md。它應該包含模組及其用途的描述。如果您想要包含將此模組與其他 資源搭配使用的範例,請將其放入 examples目錄中。考慮包含圖表,其中描述模組可能建立的基礎設施資源及其關係。使用 terraform-docs 自動產生模組的輸入或輸出。

  • main.tf:這是主要進入點。對於簡單的模組,可能會在此檔案中建立所有資源。對於複雜的模組,資源建立可能會分散到多個檔案,但任何巢狀模組呼叫都應在main.tf檔案中。

  • variables.tfoutputs.tf:這些檔案包含變數和輸出的宣告。所有變數和輸出都應有解釋其目的的一聲或兩聲描述。這些描述用於文件。如需詳細資訊,請參閱 HashiCorp 文件以取得變數組態輸出組態

    • 所有變數都必須有已定義的類型。

    • 變數宣告也可以包含預設引數。如果宣告包含預設引數,則變數會被視為選用,而且如果您在呼叫模組或執行 Terraform 時未設定值,則會使用預設值。預設引數需要常值,且無法在組態中參考其他物件。若要讓變數成為必要,請省略變數宣告中的預設值,並考慮設定是否nullable = false合理。

    • 對於具有環境獨立值 (例如 disk_size) 的變數,請提供預設值。

    • 對於具有環境特定值 (例如 project_id) 的變數,請勿提供預設值。在此情況下,呼叫模組必須提供有意義的值。

    • 針對空白字串等變數使用空白預設值,或僅在將變數保留空白時才列出,這是基礎 APIs 不會拒絕的有效偏好設定。

    • 謹慎使用變數。只有當每個執行個體或環境的值必須不同時,才能參數化值。當您決定是否公開變數時,請確保您有變更該變數的具體使用案例。如果只有很小的機率可能需要變數,請不要公開它。

      • 新增具有預設值的變數可回溯相容。

      • 移除變數與向後不相容。

      • 如果常值在多個位置重複使用,您應該使用本機值,而不將其公開為變數。

    • 請勿直接透過輸入變數傳遞輸出,因為這樣做可防止輸出正確新增至相依性圖表。為了確保已建立隱含相依性,請務必從 資源輸出參考屬性。傳遞 屬性,而不是直接參考執行個體的輸入變數。

  • locals.tf:此檔案包含將名稱指派給表達式的本機值,因此可以在模組內多次使用名稱,而不是重複表達式。本機值就像函數的暫時本機變數。本機值中的表達式不限於常值;它們也可以參考模組中的其他值,包括變數、資源屬性或其他本機值,以合併它們。

  • providers.tf://:此檔案包含 terraform 區塊提供者區塊provider 區塊只能由 模組的取用者在根模組中宣告。

    如果您使用的是 HCP Terraform,也請新增空白雲端區塊cloud 區塊應完全透過環境變數環境變數登入資料進行設定,做為 CI/CD 管道的一部分。

  • versions.tf:此檔案包含 required_providers 區塊。所有 Terraform 模組都必須宣告其需要哪些提供者,以便 Terraform 可以安裝和使用這些提供者。

  • data.tf://:如需簡單的組態,請將資料來源放在參考資料來源的資源旁。例如,如果您要擷取要在啟動執行個體時使用的映像,請將其放在執行個體旁邊,而不是在自己的檔案中收集資料資源。如果資料來源數量變得太大,請考慮將其移至專用data.tf檔案。

  • .tfvars 檔案:對於根模組,您可以使用 .tfvars 檔案提供不敏感的變數。為了一致性,請將變數檔案命名為 terraform.tfvars。將常用值放在儲存庫的根目錄,以及envs/資料夾中的環境特定值。

  • 巢狀模組:巢狀模組應存在於 modules/子目錄下。具有 的任何巢狀模組README.md都視為可由外部使用者使用。如果 README.md 不存在,則會考慮將該模組僅供內部使用。巢狀模組應該用來將複雜行為分割成多個小型模組,使用者可以仔細挑選和選擇。

    如果根模組包含對巢狀模組的呼叫,這些呼叫應該使用相對路徑,例如 ,./modules/sample-module以便 Terraform 將它們視為相同儲存庫或套件的一部分,而不是單獨下載它們。

    如果儲存庫或套件包含多個巢狀模組,最好是由發起人撰寫,而不是直接呼叫彼此並建立模組的深度巢狀樹狀目錄。

  • 範例:使用可重複使用模組的範例應存在於儲存庫根目錄的 examples/ 子目錄下。對於每個範例,您可以新增 README 來說明範例的目標和用量。子模組的範例也應該放在根examples/目錄中。

    由於範例通常會複製到其他儲存庫以進行自訂,因此模組區塊的來源應設為外部發起人將使用的地址,而非相對路徑。

  • 服務命名檔案:使用者通常想要依服務區分多個檔案中的 Terraform 資源。此實務應盡可能不鼓勵,而資源應main.tf改為在 中定義。不過,如果資源集合 (例如 IAM 角色和政策) 超過 150 行,則可以將其分成自己的檔案,例如 iam.tf。否則,所有資源程式碼都應在 中定義main.tf

  • 自訂指令碼:僅在需要時使用指令碼。Terraform 不會考慮或管理透過指令碼建立的資源狀態。只有在 Terraform 資源不支援所需的行為時,才能使用自訂指令碼。將 Terraform 呼叫的自訂指令碼放在scripts/目錄中。

  • 協助程式指令碼:整理helpers/目錄中 Terraform 未呼叫的協助程式指令碼。在 README.md 檔案中記錄協助程式指令碼,其中包含說明和範例調用。如果協助程式指令碼接受引數,請提供引數檢查和--help輸出。

  • 靜態檔案:Terraform 參考但未執行的靜態檔案 (例如載入 EC2 執行個體的啟動指令碼) 必須組織到files/目錄中。將冗長的文件放在外部檔案中,與其 HCL 分開。使用 file() 函數來參考它們。

  • 範本:對於 Terraform templatefile 函數讀取的檔案,請使用副檔名 .tftpl。範本必須放置在 templates/目錄中。

根模組結構

Terraform 一律會在單一根模組的內容中執行。完整的 Terraform 組態包含根模組和子模組的樹狀目錄 (包括根模組呼叫的模組、這些模組呼叫的任何模組等)。

Terraform 根模組配置基本範例:

. ├── data.tf ├── envs │ ├── dev │ │ └── terraform.tfvars │ ├── prod │ │ └── terraform.tfvars │ └── test │ └── terraform.tfvars ├── locals.tf ├── main.tf ├── outputs.tf ├── providers.tf ├── README.md ├── terraform.tfvars ├── variables.tf └── versions.tf

可重複使用的模組結構

可重複使用的模組遵循與根模組相同的概念。若要定義模組,請為其建立新的目錄,並將.tf檔案放在其中,就像定義根模組一樣。Terraform 可以從本機相對路徑或從遠端儲存庫載入模組。如果您預期許多組態會重複使用模組,請將它放在自己的版本控制儲存庫中。請務必將模組樹保持相對平坦,以便以不同的組合重複使用模組。

Terraform 可重複使用的模組配置基本範例:

. ├── data.tf ├── examples │ ├── multi-az-new-vpc │ │ ├── data.tf │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── README.md │ │ ├── terraform.tfvars │ │ ├── variables.tf │ │ ├── versions.tf │ │ └── vpc.tf │ └── single-az-existing-vpc │ │ ├── data.tf │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── README.md │ │ ├── terraform.tfvars │ │ ├── variables.tf │ │ └── versions.tf ├── iam.tf ├── locals.tf ├── main.tf ├── outputs.tf ├── README.md ├── variables.tf └── versions.tf

模組化的結構

原則上,您可以將任何資源和其他建構模組結合到模組中,但過度使用巢狀和可重複使用的模組可能會使您的整體 Terraform 組態更難理解和維護,因此請適度使用這些模組。

當合理時,請將您的組態分成可重複使用的模組,透過在架構中描述以資源類型建構的新概念來提高抽象層級。

當您將基礎設施模組化為可重複使用的定義時,請瞄準邏輯資源集,而不是個別元件或過於複雜的集合。

不要包裝單一資源

您不應在其他單一資源類型周圍建立薄包裝函式的模組。如果您在尋找與其中主要資源類型名稱不同的模組名稱時遇到問題,則您的模組可能未建立新的抽象 ― 會增加不必要的複雜性。請改為直接在呼叫模組中使用 資源類型。

封裝邏輯關係

相關資源的群組集,例如聯網基礎、資料層、安全控制和應用程式。可重複使用的模組應封裝可一起運作以啟用 功能的基礎設施組件。

保持水平繼承

當您在子目錄中巢狀模組時,請避免進入超過一個或兩個層級的深度。深度巢狀繼承結構使組態和故障診斷更為複雜。模組應建置在其他模組上,而不是建置通道。

透過將模組集中在代表架構模式的邏輯資源群組上,團隊可以快速設定可靠的基礎設施基礎。在不過度工程或過度簡化的情況下平衡抽象。

參考輸出中的資源

對於可重複使用模組中定義的每個資源,至少包含一個參考資源的輸出。變數和輸出可讓您在模組和資源之間推斷相依性。如果沒有任何輸出,使用者就無法正確訂購與其 Terraform 組態相關的模組。

結構良好的模組提供環境一致性、目標驅動的分組和匯出的資源參考,可大規模實現整個組織的 Terraform 協作。團隊可以從可重複使用的建置區塊組合基礎設施。

請勿設定提供者

雖然共用模組會繼承提供者呼叫模組,但模組不應自行設定提供者設定。避免在模組中指定提供者組態區塊。此組態應該只全域宣告一次。

宣告必要的供應商

雖然提供者組態是在模組之間共用,共用模組也必須宣告自己的提供者需求。此實務可讓 Terraform 確保單一版本的提供者與組態中的所有模組相容,並指定做為提供者全域 (模組無關) 識別符的來源地址。不過,模組特定的提供者需求不會指定任何組態設定,以決定提供者將存取哪些遠端端點,例如 AWS 區域。

透過宣告版本需求並避免硬式編碼提供者組態,模組可使用共用提供者跨 Terraform 組態提供可攜性和可重複使用性。

對於共用模組,請在 的 required_providers 區塊中定義最低必要提供者版本versions.tf

若要宣告模組需要特定版本的 AWS 提供者,請在required_providers區塊內使用terraform區塊:

terraform { required_version = ">= 1.0.0" required_providers { aws = { source = "hashicorp/aws" version = ">= 4.0.0" } } }

如果共用模組僅支援特定版本的 AWS 提供者,請使用 essimistic 限制運算子 (~>),只允許增加最右側的版本元件:

terraform { required_version = ">= 1.0.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } } }

在此範例中, ~> 4.0 允許安裝 4.57.14.67.0,但不允許安裝 5.0.0。如需詳細資訊,請參閱 HashiCorp 文件中的版本限制語法

遵循命名慣例

清晰的描述性名稱可簡化您對模組中資源與組態值用途之間的關係的理解。與樣式準則的一致性可增強模組使用者和維護者的可讀性。

遵循資源命名的指導方針

  • 針對所有資源名稱使用 snake_case (其中小寫詞彙以底線分隔),以符合 Terraform 樣式標準。此實務可確保與資源類型、資料來源類型和其他預先定義值的命名慣例一致。此慣例不適用於名稱引數

  • 若要簡化唯一一種類型的資源參考 (例如,整個模組的單一負載平衡器),請將資源命名為 mainthis 以清楚說明。

  • 使用有意義的名稱來描述資源的目的和內容,並協助區分類似的資源 (例如,primary用於主要資料庫和資料庫的僅供read_replica讀取複本)。

  • 使用單一名稱,而非複數名稱。

  • 請勿在資源名稱中重複資源類型。

遵循變數命名的指導方針

  • 將單位新增至輸入名稱、本機變數和輸出,這些輸出代表數值,例如磁碟大小或 RAM 大小 (例如,ram_size_gb以 GB 為單位的 RAM 大小)。此實務可讓組態維護器的預期輸入單位清除。

  • 儲存大小請使用 MiB 和 GiB 等二進位單位,其他指標則使用 MB 或 GB 等十進位單位。

  • 提供布林值變數正名稱,例如 enable_external_access

使用附件資源

有些資源內嵌虛擬資源做為其中的屬性。如果可能,您應該避免使用這些內嵌資源屬性,並使用唯一資源來連接該虛擬資源。這些資源關係可能會導致對每個資源而言都是唯一的cause-and-effect問題。

使用內嵌屬性 (避免此模式):

resource "aws_security_group" "allow_tls" { ... ingress { description = "TLS from VPC" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [aws_vpc.main.cidr_block] ipv6_cidr_blocks = [aws_vpc.main.ipv6_cidr_block] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } }

使用附件資源 (偏好):

resource "aws_security_group" "allow_tls" { ... } resource "aws_security_group_rule" "example" { type = "ingress" description = "TLS from VPC" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [aws_vpc.main.cidr_block] ipv6_cidr_blocks = [aws_vpc.main.ipv6_cidr_block] security_group_id = aws_security_group.allow_tls.id }

使用預設標籤

將標籤指派給可接受標籤的所有資源。Terraform AWS 提供者具有您應該在根模組內使用的 aws_default_tags 資料來源。

考慮將必要的標籤新增至 Terraform 模組建立的所有資源。以下是可能連接的標籤清單:

  • 名稱:人類可讀取的資源名稱

  • AppId:使用 資源之應用程式的 ID

  • AppRole:資源的技術函數,例如 "webserver" 或 "database"

  • AppPurpose:資源的商務用途,例如「前端 ui」或「付款處理器」

  • 環境:軟體環境,例如開發、測試或生產

  • 專案:使用 資源的專案

  • CostCenter:資源用量的計費對象

符合 Terraform 登錄檔需求

模組儲存庫必須符合下列所有要求,才能發佈至 Terraform 登錄檔。

即使您不打算在短期內將模組發佈至登錄檔,仍應一律遵循這些要求。透過這樣做,您可以稍後將模組發佈到登錄檔,而不必變更儲存庫的組態和結構。

  • 儲存庫名稱:對於模組儲存庫,請使用三部分名稱 terraform-aws-<NAME>,其中 <NAME>反映模組管理的基礎設施類型。<NAME> 區段可以包含額外的連字號 (例如 terraform-aws-iam-terraform-roles)。

  • 標準模組結構:模組必須遵循標準儲存庫結構。這可讓登錄檔檢查您的模組並產生文件、追蹤資源用量等。

    • 建立 Git 儲存庫之後,請將模組檔案複製到儲存庫的根目錄。我們建議您將欲重複使用的每個模組放在其儲存庫的根目錄中,但您也可以參考子目錄中的模組。

    • 如果您使用的是 HCP Terraform,請將要共用的模組發佈到您的組織登錄檔。登錄檔會使用 HCP Terraform API 權杖處理下載和控制存取,因此即使消費者從命令列執行 Terraform,也不需要存取模組的來源儲存庫。

  • 位置和許可:儲存庫必須位於其中一個已設定的版本控制系統 (VCS) 提供者中,且 HCP Terraform VCS 使用者帳戶必須具有儲存庫的管理員存取權。登錄檔需要管理員存取權才能建立 Webhook 以匯入新的模組版本。

  • 發行版本的 x.y.z 標籤:必須至少有一個發行標籤,您才能發佈模組。登錄檔使用發行標籤來識別模組版本。版本標籤名稱必須使用語意版本控制,您可以選擇使用字首加上 v(例如 v1.1.01.1.0)。登錄檔會忽略看起來不像版本編號的標籤。如需發佈模組的詳細資訊,請參閱 Terraform 文件

如需詳細資訊,請參閱 Terraform 文件中的準備模組儲存庫

使用建議的模組來源

Terraform 使用模組區塊中的 source 引數來尋找和下載子模組的原始程式碼。

我們建議您將本機路徑用於密切相關的模組,這些模組主要目的是將重複的程式碼元素納入考量,以及將原生 Terraform 模組登錄檔或 VCS 供應商用於旨在由多個組態共用的模組。

下列範例說明共用模組的最常見和建議來源類型。登錄模組支援版本控制。您應該一律提供特定版本,如下列範例所示。

登錄檔

Terraform 登錄檔:

module "lambda" { source = "github.com/terraform-aws-modules/terraform-aws-lambda.git?ref=e78cdf1f82944897ca6e30d6489f43cf24539374" #--> v4.18.0 ... }

透過鎖定遞交雜湊,您可以避免偏離易遭受供應鏈攻擊的公有登錄檔。

HCP Terraform:

module "eks_karpenter" { source = "app.terraform.io/my-org/eks/aws" version = "1.1.0" ... enable_karpenter = true }

Terraform Enterprise:

module "eks_karpenter" { source = "terraform.mydomain.com/my-org/eks/aws" version = "1.1.0" ... enable_karpenter = true }

VCS 提供者

VCS 提供者支援用於選取特定修訂的ref引數,如下列範例所示。

GitHub (HTTPS):

module "eks_karpenter" { source = "github.com/my-org/terraform-aws-eks.git?ref=v1.1.0" ... enable_karpenter = true }

一般 Git 儲存庫 (HTTPS):

module "eks_karpenter" { source = "git::https://example.com/terraform-aws-eks.git?ref=v1.1.0" ... enable_karpenter = true }

一般 Git 儲存庫 (SSH):

警告

您需要設定登入資料來存取私有儲存庫。

module "eks_karpenter" { source = "git::ssh://username@example.com/terraform-aws-eks.git?ref=v1.1.0" ... enable_karpenter = true }

遵循編碼標準

在所有組態檔案中套用一致的 Terraform 格式規則和樣式。在 CI/CD 管道中使用自動樣式檢查來強制執行標準。當您將編碼最佳實務嵌入團隊工作流程時,組態會隨著用量在組織中廣泛分佈而保持可讀性、可維護性和協作性。

遵循樣式準則

設定預先遞交掛鉤

在允許遞交之前,設定執行 terraform fmtcheckovtflint和其他程式碼掃描和樣式檢查的用戶端預先遞交勾點。此實務可協助您更早在開發人員工作流程中驗證標準一致性。

使用預先遞交架構,例如預先遞交,將 Terraform linting、格式化和程式碼掃描新增為本機電腦上的掛鉤。勾點會在每個 Git 遞交上執行,如果檢查未通過,則遞交會失敗。

在引入變更之前,將樣式和品質檢查移至本機遞交前勾點可為開發人員提供快速意見回饋。標準成為編碼工作流程的一部分。