第 2 層建構 - AWS 規範指南

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

第 2 層建構

AWS CDK 開源存儲庫主要是通過使用TypeScript編程語言編寫的,並且由許多軟件包和模塊組成。所謂aws-cdk-lib的主包庫大致分為每個 AWS 服務一個包,儘管情況並非總是如此。如前所述,L1 結構是在構建過程中自動生成的,那麼當您查看存儲庫時,您看到的代碼是什麼? 這些是 L2 結構,它們是 L1 結構的抽象。

這些套件也包含 TypeScript 類型、枚舉和介面的集合,以及可新增更多功能的輔助程式類別,但這些項目都提供 L2 建構。實例化時,所有 L2 構造都在構造函數中調用其相應的 L1 構造,並且創建的生成 L1 構造可以從第 2 層訪問,如下所示:

const role = new Bucket(this, "my-bucket", {/*...BucketProps*/}); const cfnBucket = role.node.defaultChild;

L2 構造採用默認屬性,方便方法和其他語法糖,並將它們應用於 L1 構造。這消除了直接在中佈建資源所需的大部分重複和冗長。 CloudFormation

所有 L2 構造在引擎蓋下構建相應的 L1 構造。但是,L2 結構實際上並不會擴展 L1 結構。L1 和 L2 結構都繼承了一個名為構造的特殊類。在版本 1 中, AWS CDK 該Construct類內置到開發工具包中,但在版本 2 中,它是一個單獨的獨立軟件包。這就是其他軟件包,例如 Terraform 的 Cloud Development Kit(CDKTF)可以將其包含為依賴項。繼承類別的任何Construct類別都是 L1、L2 或 L3 建構。L2 建構會直接擴充這個類別,而 L1 建構會延伸名為的類別CfnResource,如下表所示。

L1 繼承樹

L2 繼承樹

一级建筑

→ 班級 CfnResource

→→ 抽象類 CfnRefElement

→→→ 抽象類 CfnElement

→→→→ 班級構建

二层建筑

→ 類構造

如果 L1 和 L2 結構都繼承了Construct類,為什麼 L2 構造只是擴展 L1? 好吧,類別和圖層 1 之間的Construct類別會將 L1 建構鎖定為 CloudFormation 資源的鏡像影像。它們包含抽象方法(下游類必須包含的方法)_toCloudFormation,這會強制構造直接輸出 CloudFormation語法。L2 構造跳過這些類並直接擴展Construct類。這使他們可以靈活地通過在構造函數中單獨構建它們來抽象 L1 構造所需的大部分代碼。

上一節的特色是 CloudFormation範本中的 S3 儲存貯體與轉譯為 L1 建構的相同 S3 儲存貯體的 side-by-side 比較。這項比較顯示,屬性和語法幾乎相同,與建構相比,L1 建構只會儲存三或四行。 CloudFormation 現在讓我們將 L1 構造與相同 S3 存儲桶的 L2 構造進行比較:

適用於 S3 儲存貯體的 L1 建構

適用於 S3 儲存貯體的 L2 建構

new CfnBucket(this, "myS3Bucket", { bucketName: "my-s3-bucket", bucketEncryption: { serverSideEncryptionConfiguration: [ { serverSideEncryptionByDefault: { sseAlgorithm: "AES256" } } ] }, metricsConfigurations: [ { id: "myConfig" } ], ownershipControls: { rules: [ { objectOwnership: "BucketOwnerPreferred" } ] }, publicAccessBlockConfiguration: { blockPublicAcls: true, blockPublicPolicy: true, ignorePublicAcls: true, restrictPublicBuckets: true }, versioningConfiguration: { status: "Enabled" } });
new Bucket(this, "myS3Bucket", { bucketName: "my-s3-bucket", encryption: BucketEncryption.S3_MANAGED, metrics: [ { id: "myConfig" }, ], objectOwnership: ObjectOwnership.BUCKET_OWNER_PREFERRED, blockPublicAccess: BlockPublicAccess.BLOCK_ALL, versioned: true });

如您所見,L2 建構小於 L1 建構大小的一半。L2 結構使用許多技術來完成此整合。其中一些技術適用於單個 L2 構造,但其他技術可以跨多個構造重複使用,以便將它們分為自己的類以實現可重用性。L2 會以數種方式建構合併 CloudFormation語法,如下列各節所述。

預設屬性

整合用於佈建資源的程式碼最簡單的方法是將最常見的內容設定轉換為預設值。 AWS CDK 有權訪問強大的編程語言,而 CloudFormation 不是,所以這些默認值通常在本質上是有條件的。有時候可以從 AWS CDK 程式碼中消除數行 CloudFormation 組態,因為這些設定可以從傳遞至建構的其他屬性值中推斷出來。

結構、類型和介面

雖然 AWS CDK 有數種程式設計語言可供使用,但它是以原生方式撰寫的 TypeScript,因此語言的型別系統可用來定義組成 L2 建構的型別。深入了解該類型系統已超出本指南的範圍;如需詳細資訊,請參閱TypeScript文件。總而言之, TypeScript type描述了一個特定變量持有什麼樣的數據。這可能是基本資料,例如string,或更複雜的資料,例如object. A TypeScript interface 是表達 TypeScript對象類型的另一種方式,a struct 是接口的另一個名稱。

TypeScript 不使用術語結,但是如果您查看 AWS CDK API 參考,您會看到結構實際上只是代碼中的另一個 TypeScript 接口。API 參考也將某些接口稱為接口。如果結構和接口是一樣的,為什麼 AWS CDK 文檔在它們之間有區別?

所 AWS CDK 指的結構是代表 L2 結構使用的任何對象的接口。這包括在實例化期間傳遞給 L2 建構的屬性引數的物件類型,例如BucketProps針對 S3 儲存貯體建構和 TableProps DynamoDB 表建構,以及在中使用的其他 TypeScript 介面。 AWS CDK簡而言之,如果它是中的 TypeScript 接口, AWS CDK 並且它的名稱不是前綴字母I,則將其 AWS CDK 稱為結構體

相反, AWS CDK 使用術語接來表示普通對象需要被視為特定構造或輔助類的正確表示的基本元素。也就是說,接口描述了 L2 構造的公共屬性必須是什麼。所有 AWS CDK 接口名稱都是字母前綴的現有構造或輔助類的名稱。I所有 L2 結構都擴展了Construct類,但它們也實現了相應的接口。所以 L2 構造Bucket實現了接IBucket口。

靜態方法

L2 構造的每個實例也是其相應接口的一個實例,但反過來並非如此。這在查看結構體以查看需要哪些數據類型時非常重要。如果結構具有名為bucket的屬性 (需要資料類型)IBucket,您可以傳遞包含IBucket介面中所列屬性的物件或 L Bucket 2 的執行個體。任何一個都可以工作。不過,如果該bucket屬性呼叫 L2Bucket,您只能傳遞該欄位中的Bucket執行個體。

當您將預先存在的資源導入到堆棧中時,此區別變得非常重要。您可以為堆棧本機的任何資源創建一個 L2 構造,但是如果您需要引用在堆棧之外創建的資源,則必須使用該 L2 構造的接口。這是因為創建一個 L2 構造創建一個新的資源,如果該堆棧中不存在一個新的資源。對現有資源的引用必須是符合該 L2 構造的接口的普通對象。

為了使這在實踐中更容易,大多數 L2 構造都有一組與它們相關聯的靜態方法,它們返回該 L2 構造的接口。這些靜態方法通常以單詞開頭from。傳遞給這些方法的前兩個引id數是相同的scope,標準 L2 建構所需的引數。props但是,第三個參數不僅僅是定義接口的屬性的一小部分(或有時只是一個屬性)。因此,當您傳遞 L2 建構時,在大多數情況下,只需要介面的元素。這樣您也可以在可能的情況下使用導入的資源。

// Example of referencing an external S3 bucket const preExistingBucket = Bucket.fromBucketName(this, "external-bucket", "name-of-bucket-that-already-exists");

但是,您不應該嚴重依賴接口。您應該只在絕對必要時才直接匯入資源並使用介面,因為介面不會提供許多屬性 (例如說明器方法),這會讓 L2 建構變得如此強大。

輔助方法

L2 構造是一個程序化類而不是一個簡單的對象,因此它可以公開類方法,允許您在實例化發生後操作資源配置。這方面的一個很好的例子是 AWS Identity and Access Management (IAM)L2 角色構造。下列程式碼片段顯示兩種使用 L2 建Role構建立相同 IAM 角色的方法。

沒有輔助方法:

const role = new Role(this, "my-iam-role", { assumedBy: new FederatedPrincipal('my-identity-provider.com'), managedPolicies: [ ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess") ], inlinePolicies: { lambdaPolicy: new PolicyDocument({ statements: [ new PolicyStatement({ effect: Effect.ALLOW, actions: [ 'lambda:UpdateFunctionCode' ], resources: [ 'arn:aws:lambda:us-east-1:123456789012:function:my-function' ] }) ] }) } });

用一個輔助方法:

const role = new Role(this, "my-iam-role", { assumedBy: new FederatedPrincipal('my-identity-provider.com') }); role.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")); role.attachInlinePolicy(new Policy(this, "lambda-policy", { policyName: "lambdaPolicy", statements: [ new PolicyStatement({ effect: Effect.ALLOW, actions: [ 'lambda:UpdateFunctionCode' ], resources: [ 'arn:aws:lambda:us-east-1:123456789012:function:my-function' ] }) ] }));

在實例化之後使用實例方法操作資源配置的能力使 L2 構造比前一層具有很多額外的靈活性。L1 構造也繼承了一些資源方法(例如addPropertyOverride),但直到第二層才能獲得專門為該資源及其屬性設計的方法。

列舉

CloudFormation 語法通常需要您指定許多細節才能正確佈建資源。但是,大多數用例通常只有少數配置涵蓋。使用一系列枚舉值來表示這些配置可以大大減少所需的代碼量。

例如,在本節前面的 S3 儲存貯體 L2 程式碼範例中,您必須使用 CloudFormation 範本的bucketEncryption屬性來提供所有詳細資料,包括要使用的加密演算法的名稱。取而代之的是 AWS CDK 提供BucketEncryption枚舉,它採用了五種最常見的存儲桶加密形式,並允許您使用單個變量名稱來表達每種形式。

枚舉未涵蓋的邊緣情況呢? L2 建構的其中一個目標是簡化佈建第 1 層資源的工作,因此第 2 層可能不支援較少使用的某些邊緣案例。若要支援這些邊緣案例, AWS CDK 可讓您直接使用addPropertyOverride方法操作基礎 CloudFormation 資源屬性。如需屬性覆寫的詳細資訊,請參閱本指南的最佳作法一節以及文件中的抽象與逸出剖面 AWS CDK 線一節。

輔助類

有時,枚舉無法完成為給定用例配置資源所需的編程邏輯。在這些情況下, AWS CDK 通常會提供輔助類。枚舉是一個簡單的對象,它提供了一系列鍵值對,而輔助類提供了一個 TypeScript 類的完整功能。輔助類仍然可以通過公開靜態屬性來像枚舉一樣工作,但是這些屬性可以在幫助器類構造函數或幫助器方法中使用條件邏輯在內部設置它們的值。

因此,儘管BucketEncryption枚舉可以減少在 S3 存儲桶上設置加密算法所需的代碼量,但相同的策略不適用於設置時間持續時間,因為有太多可能的值可供選擇。為每個值創建一個枚舉將比它的價值更麻煩。基於這個原因,S3 儲存貯體的預設 S3 物件鎖定組態設定會使用輔助程式類別,如ObjectLockRetention類別所代表。 ObjectLockRetention包含兩種靜態方法:一種用於合規性保留,另一種用於治理保留。這兩種方法都採用持續時間輔助類的實例作為參數來表示鎖應配置的時間量。

另一個例子是 AWS Lambda 輔助類運行時。乍一看,可能看起來與此類關聯的靜態屬性可以由枚舉處理。但是,在引擎蓋下,每個屬性值代表Runtime類本身的一個實例,因此在類的構造函數中執行的邏輯無法在枚舉中實現。