程式碼基底結構與組織的最佳做法 - AWS 方案指引

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

程式碼基底結構與組織的最佳做法

隨著 Terraform 在大型團隊和企業中的使用量增加,正確的代碼庫結構和組織至關重要。架構良好的程式碼基底能夠大規模協同合作,同時提升可維護性。

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

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

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

藉由實作有關模組化、文件、標準和邏輯組織的程式碼基底結構準則,您可以支援跨團隊的廣泛協同作業,同時讓 Terraform 在整個組織中的使用量分散時保持可維護。透過強制執行慣例和標準,您可以避免分散程式碼基底的複雜性。

實作標準儲存庫結構

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

  • 根模塊或目錄:這應該是 Terraform 模塊和可重複使用模塊的主要入口點,並且預計是唯一的。如果你有一個更複雜的架構,你可以使用嵌套模塊來創建輕量級抽象。這可協助您描述基礎結構的架構,而不是直接,就實體物件而言。

  • 自述文件:根模塊和任何嵌套模塊應該具有自述文件。此檔案必須命名README.md。它應該包含模塊的描述以及它應該用於什麼。如果要包含將此模塊與其他資源一起使用的示例,請將其放在examples目錄中。請考慮加入描述模組可能建立的基礎結構資源及其關係的圖表。使用地形文檔來自動生成模塊的輸入或輸出。

  • main.tf:這是主要的入口點。對於一個簡單的模塊,所有的資源可以在這個文件中創建。對於複雜的模塊,資源創建可能會分散在多個文件中,但任何嵌套模塊調用都應該在文main.tf件中。

  • 變量 .tf 和輸出 .tf:這些文件包含變量和輸出的聲明。所有變量和輸出都應該有一句或兩句話的描述來解釋它們的目的。這些描述用於文件。如需詳細資訊,請參閱變數組態輸出組態的 HashiCorp 文件。

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

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

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

    • 對於具有環境特定值 (例如project_id) 的變數,請勿提供預設值。在這種情況下,調用模塊必須提供有意義的值。

    • 僅當變量保持空白是基礎 API 不拒絕的有效偏好設置時,才對空字符串或列表等變量使用空默認值。

    • 明智地使用變量。僅當每個例證或環境都必須有所不同時,才能將其參數化。當您決定是否公開變數時,請確定您有變更該變數的具體使用案例。如果只有一個很小的可能性可能需要一個變量,不要公開它。

      • 使用默認值添加變量是向後兼容的。

      • 移除變數是向後不相容的。

      • 在多個位置重複使用常值的情況下,您應該使用局部值而不將其公開為變數。

    • 不要直接通過輸入變量傳遞輸出,因為這樣做可以防止它們正確添加到依賴關係圖中。若要確保建立隱含相依性,請確定輸出參考資源中的屬性。而不是直接引用實例的輸入變量,而是傳遞屬性。

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

  • 供應商 .tf:此文件包含地形和提供程序塊。 provider塊必須僅在根模塊中由模塊的消費者聲明。

    如果您使用的是 HCP 地形,還要添加一個空的雲塊。cloud區塊應完全透過環境變數和環境變數認證來設定,做為 CI/CD 管線的一部分。

  • 版本 .tf:此文件包含所需的提供程序塊。所有 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/

  • 協助程式指令碼:組織目錄中未由 Terraform 呼叫的輔助程式指令碼。helpers/在文件中記錄幫助程序腳本,其README.md中包含解釋和示例調用。如果輔助腳本接受參數,請提供參數檢查和--help輸出。

  • 靜態文件:Terraform 引用但不運行的靜態文件(例如,加載到 EC2 實例的啟動腳本)必須組織到一個files/目錄中。將冗長的文件放置在外部檔案中,與其 HCL 分開。使用文件()函數引用它們。

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

根模塊結構

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 可以從本地相對路徑或遠程存儲庫加載模塊。如果您希望模塊被許多配置重複使用,請將其放置在其自己的版本控制存儲庫中。重要的是要保持模塊樹相對平坦,以便更容易以不同的組合重複使用模塊。

地形可重複使用的模塊佈局基本示例:

. ├── 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 提供程序,請使用悲觀約束運算符(~>),該運算符只允許最右側的版本組件增加:

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

在此範例中,~> 4.0允許安裝4.57.1和 (4.67.0但不允許) 5.0.0。如需詳細資訊,請參閱文件中的版本條 HashiCorp 件約束語法

遵循命名慣例

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

遵循資源命名的準則

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

  • 若要簡化對資源類型中唯一資源的參照 (例如,整個模組的單一負載平衡器),請為資源命名mainthis為清楚起見。

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

  • 使用單數,而不是複數名稱。

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

遵循變數命名的準則

  • 將單位新增至輸入名稱、區域變數和代表數值的輸出,例如磁碟大小或 RAM 大小 (例如,ram_size_gbRAM 大小以 GB 為單位)。此做法可讓設定維護者清楚預期的輸入單元。

  • 儲存區大小使用二進位單位 (例如 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 }

使用預設標籤

將標籤指派給所有可接受標籤的資源。地形表單 AWS 提供者有一個 aws_default_tags 資料來源,您應該在根模組內使用它。

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

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

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

  • AppRole: 資源的技術功能; 例如,「網絡服務器」或「數據庫」

  • AppPurpose:資源的業務目的;例如,「前端 UI」或「付款處理者」

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

  • 專案:使用資源的專案

  • CostCenter:向誰開立資源使用量帳單

符合地形登錄要求

模塊存儲庫必須滿足以下所有要求,以便可以將其發佈到 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) 作為前綴。登錄會忽略看起來不像版本號碼的標記。如需有關發行模組的詳細資訊,請參閱 T erraform 文件。

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

使用建議的模組來源

Terraform 使用模塊中的source參數來查找和下載子模塊的源代碼。

我們建議您針對密切相關的模組使用本機路徑,這些模組的主要目的是分解重複的程式碼元素,並針對多個組態共用的模組使用原生 Terraform 模組登錄或 VCS 提供程式。

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

登錄檔

地形註冊表:

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

通過固定提交哈希值,您可以避免從容易受到供應鏈攻擊的公共註冊表中漂移。

醫護機構地形:

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

地形企業:

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 fmt 指令來格式化所有地形.tf檔案 (檔案),以符合樣式標準。 HashiCorp

  • 使用地形驗證命令來驗證配置的語法和結構。

  • 使用 T Flint 靜態分析程式碼品質。此絨毛器會檢查 Terraform 的最佳實踐,而不僅僅是格式化,並在遇到錯誤時失敗構建。

配置提交前掛鉤

在允許認可之前,先設定執行terraform fmttflintcheckov、和其他程式碼掃描和樣式檢查的用戶端提交前掛接。此做法可協助您在開發人員工作流程中早期驗證標準一致性。

使用預先提交的框架(例如預先提交)將 Terraform 線條,格式化和代碼掃描添加為本地計算機上的掛鉤。鉤子在每個 Git 提交上運行,如果檢查未通過,則提交失敗。

將樣式和品質檢查移至本機預先提交掛鉤可在變更之前向開發人員提供快速回饋。標準成為編碼工作流程的一部分。