这是AWS CDK v2 开发者指南。较旧的 CDK v1 于 2022 年 6 月 1 日进入维护阶段,现在将仅收到关键错误修复和安全补丁。将专门为 CDK v2 开发新功能。对 CDK v1 的Support 将于 2023 年 6 月 1 日完全终止。
本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
构造
构造是AWS CDK应用程序的基本构建模块。构造代表 "云组件",它封装了创建该组件AWS CloudFormation所需的一切。
注意
构造是构造编程模型 (CPM) 的一部分。其他工具也使用它们,例如适用于 Terraform 的 CDK (cdkTF)、适用于 Kubernetes 的 CDK (cdk8s) 和 Projen。
Amazon SimpleAWS Storage Service (Amazon S3)。构造也可以是由多个相关AWS资源组成的更高级别的抽象。此类组件的示例包括具有相关计算容量的工作队列,或带有监控资源和仪表板的计划作业。
AWS CDK包括一组名为 Construct Library 的AWS构造,其中包含每AWS项服务的构造。Constr@@
uct Hub
重要
在AWS CDK v1 中,
Construct
基类位于 CDK
core
模块中。在 CDK v2 中,有一个名为的单独模块包含
constructs
该类。
AWS构造库
AWS CDK包括 AWS构造库 ,其中包含代表AWS资源的构造。
该库包含代表上所有可用资源的结构AWS。例如,该
s3.Bucket
类代表一个 Amazon S3 存储桶,该
dynamodb.Table
类代表 Amazon DynamoDB 表。
L1 构造
这个库中有三个不同级别的结构,首先是低级结构,我们称之为 CFN Res ources(或 L1 ,"第 1 层" 的缩写)。这些结构直接代表了中所有可用的资源AWS CloudFormation。CFN 资源定期根据 AWS CloudFormation资源规范 生成。它们被命名为 Cfn Xyz ,其中 Xyz 是资源的名称。例如, CfnBucket 表示 AWS::S3::Bucket AWS CloudFormation资源。使用 Cfn 资源时,必须明确配置所有资源属性。这需要对基础AWS CloudFormation资源模型的细节有全面的了解。
L2 构造
下一级结构 L2 也表示AWS资源,但使用更高级别的、基于意图的 API。它们提供类似的功能,但包含了默认值、样板和你自己用 CFN Resource 构造编写的粘合逻辑。 AWS构造提供了方便的默认值,减少了了解它们所代表的AWS资源的所有细节的需求。它们还提供了便捷的方法,使资源的使用变得更加简单。例如, s3.bucket 类表示具有其他属性和方法的 Amazon S3 存储桶,例如 存储桶。 addLifeCycleRule () ,它向存储桶添加生命周期规则。
L3 构造
最后,AWS构造库包含 L3 构造,我们称之为 模式 。这些结构旨在帮助您完成中的常见任务AWS,通常涉及多种资源。例如, aws-ecs-patterns. ApplicationLoadBalancedFargateService 构造代表一种架构,该架构包括使用ApplicAWS ation Load Balancer 的 Fargate 容器集群。a ws-apigateway。 LambdaRestApi 构造表示由AWS Lambda函数支持的Amazon API Gateway API。
有关如何浏览库和发现可以帮助您构建应用程序的结构的更多信息,请参阅 API 参考 。
组合 是通过构造定义更高层次抽象的关键模式。高级结构可以由任意数量的低级结构组成。反过来,它们可以由更低级别的结构组成,这些结构最终由AWS资源组成。
从自下而上的角度来看,您可以使用结构来组织要部署的单个AWS资源。您可以根据自己的目的使用任何方便的抽象,并根据需要添加任意数量的图层。
Composition 允许您定义可重复使用的组件,并像任何其他代码一样共享它们。例如,团队可以定义一种结构,以实现公司对具有备份、全局复制、自动扩展和监控功能的 DynamoDB 表的最佳实践。团队可以与组织中的其他团队共享构造,也可以公开共享。
团队可以在自己的首选编程语言中使用此结构,就像使用任何其他库包来定义表并遵守最佳实践一样。库更新后,开发人员将可以通过他们已经拥有的其他类型代码的工作流程来访问新版本的错误修复和改进。
构造是在扩展
Construct
基类的类中实现的。您可以通过实例化类来定义构造。所有构造在初始化时都采用三个参数:
s@@
cop
e — 构造的父项或所有者,无论是堆栈还是其他构造,它决定了它在
构造树
中的位置。通常,您应该为作用域传递
this
(或
self
在 Python 中传递),它代表当前对象。
id — 在此范围内必须是唯一的 标识符 。标识符用作当前构造中定义的所有内容的命名空间。它用于生成唯一标识符,例如 资源名称 和AWS CloudFormation逻辑 ID。
props — 一组属性或关键字参数,视语言而定,用于定义构造的初始配置。在大多数情况下,构造提供了合理的默认值,如果所有 props 元素都是可选的,则可以完全省略 props 参数。
标识符只需要在作用域内是唯一的。这使您可以实例化和重用构造,而不必担心它们可能包含的构造和标识符,并且可以将构造组合成更高级别的抽象。此外,作用域可以同时引用所有构造组。示例包括 标记 或指定构造的部署位置。
应用程序和堆栈
我们将您的 CDK 应用程序称为 应用程序 ,它由 AppAWS CDK 类表示。以下示例定义了一个具有单个堆栈的应用程序,该堆栈包含一个启用了版本控制的 Amazon S3 存储桶:
import { App, Stack, StackProps } from 'aws-cdk-lib'; import * as s3 from 'aws-cdk-lib/aws-s3'; class HelloCdkStack extends Stack { constructor(scope: App, id: string, props?: StackProps) { super(scope, id, props); new s3.Bucket(this, 'MyFirstBucket', { versioned: true const app = new App(); new HelloCdkStack(app, "HelloCdkStack");
const { App , Stack } = require('aws-cdk-lib'); const s3 = require('aws-cdk-lib/aws-s3'); class HelloCdkStack extends Stack { constructor(scope, id, props) { super(scope, id, props); new s3.Bucket(this, 'MyFirstBucket', { versioned: true const app = new App(); new HelloCdkStack(app, "HelloCdkStack");
from aws_cdk import App, Stack import aws_cdk.aws_s3 as s3 from constructs import Construct class HelloCdkStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) s3.Bucket(self, "MyFirstBucket", versioned=True) app = App() HelloCdkStack(app, "HelloCdkStack")
import software.amazon.awscdk.*; import software.amazon.awscdk.services.s3.*; public class HelloCdkStack extends Stack { public HelloCdkStack(final Construct scope, final String id) { this(scope, id, null); public HelloCdkStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); Bucket.Builder.create(this, "MyFirstBucket") .versioned(true).build();using Amazon.CDK; using Amazon.CDK.AWS.S3; namespace HelloCdkApp internal static class Program public static void Main(string[] args) var app = new App(); new HelloCdkStack(app, "HelloCdkStack"); app.Synth(); public class HelloCdkStack : Stack public HelloCdkStack(Construct scope, string id, IStackProps props=null) : base(scope, id, props) new Bucket(this, "MyFirstBucket", new BucketProps { Versioned = true });如您所见,您需要一个范围来定义您的存储桶。资源最终需要作为AWS CloudFormation堆栈的一部分部署到AWS环境中。该环境涵盖特定的AWS账户和AWS区域。 AWS构造(例如
s3.Bucket)必须在堆栈的范围内定义。AWS CDK应用程序中的堆栈扩展了 Stack 基类,如前面的示例所示。以下示例是在您的AWS CDK应用程序中创建堆栈时的常见模式。它显示了如何扩展 Stack 类、定义接受作用域、id 和 props 的构造函数,以及如何
super使用接收到的作用域、id 和 props 调用基类构造函数。TypeScript class HelloCdkStack extends Stack { constructor(scope: App, id: string, props?: StackProps) { super(scope, id, props); //...JavaScript class HelloCdkStack extends Stack { constructor(scope, id, props) { super(scope, id, props); //...Python class HelloCdkStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) # ...public class HelloCdkStack extends Stack { public HelloCdkStack(final Construct scope, final String id) { this(scope, id, null); public HelloCdkStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); // ...public class HelloCdkStack : Stack public HelloCdkStack(Construct scope, string id, IStackProps props=null) : base(scope, id, props) //...const bucket = new s3.CfnBucket(this, "MyBucket", { bucketName: "MyBucket", corsConfiguration: { corsRules: [{ allowedOrigins: ["*"], allowedMethods: ["GET"]JavaScript const bucket = new s3.CfnBucket(this, "MyBucket", { bucketName: "MyBucket", corsConfiguration: { corsRules: [{ allowedOrigins: ["*"], allowedMethods: ["GET"]Python bucket = CfnBucket(self, "MyBucket", bucket_name="MyBucket", cors_configuration=CfnBucket.CorsConfigurationProperty( cors_rules=[CfnBucket.CorsRuleProperty( allowed_origins=["*"], allowed_methods=["GET"]CfnBucket bucket = CfnBucket.Builder.create(this, "MyBucket") .bucketName("MyBucket") .corsConfiguration(new CfnBucket.CorsConfigurationProperty.Builder() .corsRules(Arrays.asList(new CfnBucket.CorsRuleProperty.Builder() .allowedOrigins(Arrays.asList("*")) .allowedMethods(Arrays.asList("GET")) .build())) .build()) .build();var bucket = new CfnBucket(this, "MyBucket", new CfnBucketProps BucketName = "MyBucket", CorsConfiguration = new CfnBucket.CorsConfigurationProperty CorsRules = new object[] { new CfnBucket.CorsRuleProperty AllowedOrigins = new string[] { "*" }, AllowedMethods = new string[] { "GET" }, public class HelloCdkStack extends Stack { public HelloCdkStack(final Construct scope, final String id) { this(scope, id, null); public HelloCdkStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); Bucket.Builder.create(this, "MyFirstBucket") .versioned(true).build();using Amazon.CDK.AWS.S3; // "this" is HelloCdkStack new Bucket(this, "MyFirstBucket", new BucketProps Versioned = truenew s3.Bucket(this, 'MyEncryptedBucket', { encryption: s3.BucketEncryption.KMS, websiteIndexDocument: 'index.html'JavaScript new s3.Bucket(this, 'MyEncryptedBucket', { encryption: s3.BucketEncryption.KMS, websiteIndexDocument: 'index.html'Python s3.Bucket(self, "MyEncryptedBucket", encryption=s3.BucketEncryption.KMS, website_index_document="index.html")Bucket.Builder.create(this, "MyEncryptedBucket") .encryption(BucketEncryption.KMS_MANAGED) .websiteIndexDocument("index.html").build();new Bucket(this, "MyEncryptedBucket", new BucketProps Encryption = BucketEncryption.KMS_MANAGED, WebsiteIndexDocument = "index.html"与构造交互
构造是扩展基本构造类的类。实例化构造后,构造对象会公开一组方法和属性,这些方法和属性允许您与构造交互并将其作为对系统其他部分的引用传递。
该AWS CDK框架对构造的 API 没有任何限制。作者可以定义他们想要的任何 API。但是,AWS构造库中包含的AWS构造(例如
s3.Bucket)遵循指导方针和常见模式。这为所有AWS资源提供了一致的体验。大多数AWS构造都有一组授予方法,您可以使用这些方法向委托人授予AWS Identity and Access Management (IAM) 对该构造的权限。以下示例授予 IAM 组从 Amazon S3 存储桶读取的
data-science权限raw-data。TypeScript const rawData = new s3.Bucket(this, 'raw-data'); const dataScience = new iam.Group(this, 'data-science'); rawData.grantRead(dataScience);JavaScript const rawData = new s3.Bucket(this, 'raw-data'); const dataScience = new iam.Group(this, 'data-science'); rawData.grantRead(dataScience);Python raw_data = s3.Bucket(self, 'raw-data') data_science = iam.Group(self, 'data-science') raw_data.grant_read(data_science)Bucket rawData = new Bucket(this, "raw-data"); Group dataScience = new Group(this, "data-science"); rawData.grantRead(dataScience);var rawData = new Bucket(this, "raw-data"); var dataScience = new Group(this, "data-science"); rawData.GrantRead(dataScience);TypeScript const jobsQueue = new sqs.Queue(this, 'jobs'); const createJobLambda = new lambda.Function(this, 'create-job', { runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.Code.fromAsset('./create-job-lambda-code'), environment: { QUEUE_URL: jobsQueue.queueUrlJavaScript const jobsQueue = new sqs.Queue(this, 'jobs'); const createJobLambda = new lambda.Function(this, 'create-job', { runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.Code.fromAsset('./create-job-lambda-code'), environment: { QUEUE_URL: jobsQueue.queueUrlPython jobs_queue = sqs.Queue(self, "jobs") create_job_lambda = lambda_.Function(self, "create-job", runtime=lambda_.Runtime.NODEJS_14_X, handler="index.handler", code=lambda_.Code.from_asset("./create-job-lambda-code"), environment=dict( QUEUE_URL=jobs_queue.queue_urlfinal Queue jobsQueue = new Queue(this, "jobs"); Function createJobLambda = Function.Builder.create(this, "create-job") .handler("index.handler") .code(Code.fromAsset("./create-job-lambda-code")) .environment(java.util.Map.of( // Map.of is Java 9 or later "QUEUE_URL", jobsQueue.getQueueUrl()) .build();var jobsQueue = new Queue(this, "jobs"); var createJobLambda = new Function(this, "create-job", new FunctionProps Runtime = Runtime.NODEJS_14_X, Handler = "index.handler", Code = Code.FromAsset(@".\create-job-lambda-code"), Environment = new Dictionary<string, string> ["QUEUE_URL"] = jobsQueue.QueueUrlConstruct
除了使用现有的结构
s3.Bucket,比如说,你还可以编写自己的构造,然后任何人都可以在他们的应用程序中使用它们。中的所有结构都是相等的AWS CDK。AWS CDK构造(例如s3.Bucket或sns.Topic)的行为与通过 NPM、Maven 或 PyPI 发布的第三方库中的构造相同。发布到贵公司内部软件包存储库的结构也以相同的方式运行。要声明新的构造,请在
constructs包中创建一个扩展 Construct 基类的类,然后遵循初始化器参数的模式。Amazon S3。Amazon Simple Notification Service (Amazon SNS)。
TypeScript export interface NotifyingBucketProps { prefix?: string; export class NotifyingBucket extends Construct { constructor(scope: Construct, id: string, props: NotifyingBucketProps = {}) { super(scope, id); const bucket = new s3.Bucket(this, 'bucket'); const topic = new sns.Topic(this, 'topic'); bucket.addObjectCreatedNotification(new s3notify.SnsDestination(topic), { prefix: props.prefix });JavaScript class NotifyingBucket extends Construct { constructor(scope, id, props = {}) { super(scope, id); const bucket = new s3.Bucket(this, 'bucket'); const topic = new sns.Topic(this, 'topic'); bucket.addObjectCreatedNotification(new s3notify.SnsDestination(topic), { prefix: props.prefix }); module.exports = { NotifyingBucket }Python class NotifyingBucket(Construct): def __init__(self, scope: Construct, id: str, *, prefix=None): super().__init__(scope, id) bucket = s3.Bucket(self, "bucket") topic = sns.Topic(self, "topic") bucket.add_object_created_notification(s3notify.SnsDestination(topic), s3.NotificationKeyFilter(prefix=prefix))public class NotifyingBucket extends Construct { public NotifyingBucket(final Construct scope, final String id) { this(scope, id, null, null); public NotifyingBucket(final Construct scope, final String id, final BucketProps props) { this(scope, id, props, null); public NotifyingBucket(final Construct scope, final String id, final String prefix) { this(scope, id, null, prefix); public NotifyingBucket(final Construct scope, final String id, final BucketProps props, final String prefix) { super(scope, id); Bucket bucket = new Bucket(this, "bucket"); Topic topic = new Topic(this, "topic"); if (prefix != null) bucket.addObjectCreatedNotification(new SnsDestination(topic), NotificationKeyFilter.builder().prefix(prefix).build());public class NotifyingBucketProps : BucketProps public string Prefix { get; set; } public class NotifyingBucket : Construct public NotifyingBucket(Construct scope, string id, NotifyingBucketProps props = null) : base(scope, id) var bucket = new Bucket(this, "bucket"); var topic = new Topic(this, "topic"); bucket.AddObjectCreatedNotification(new SnsDestination(topic), new NotificationKeyFilter Prefix = props?.Prefix注意
我们的
NotifyingBucket构造不是继承的Bucket,而是继承的Construct。我们使用组合而不是继承来将 Amazon S3 存储桶和 Amazon SNS 主题捆绑在一起。通常,在开发AWS CDK构造时,组合比继承更受青睐。
NotifyingBucket构造函数具有典型的构造签名:scopeid、和props。最后一个参数是可选的(获取默认值{}),因为所有道具都是可选的。props(基Construct类不接受props参数。) 例如,你可以在你的应用程序中定义这个结构的实例props,而不必要:TypeScript new NotifyingBucket(this, 'MyNotifyingBucket');JavaScript new NotifyingBucket(this, 'MyNotifyingBucket');Python NotifyingBucket(self, "MyNotifyingBucket")new NotifyingBucket(this, "MyNotifyingBucket");new NotifyingBucket(this, "MyNotifyingBucket");new NotifyingBucket(this, "MyNotifyingBucket", new NotifyingBucketProps Prefix = "/images"TypeScript export class NotifyingBucket extends Construct { public readonly topic: sns.Topic; constructor(scope: Construct, id: string, props: NotifyingBucketProps) { super(scope, id); const bucket = new s3.Bucket(this, 'bucket'); this.topic = new sns.Topic(this, 'topic'); bucket.addObjectCreatedNotification(new s3notify.SnsDestination(this.topic), { prefix: props.prefix });JavaScript class NotifyingBucket extends Construct { constructor(scope, id, props) { super(scope, id); const bucket = new s3.Bucket(this, 'bucket'); this.topic = new sns.Topic(this, 'topic'); bucket.addObjectCreatedNotification(new s3notify.SnsDestination(this.topic), { prefix: props.prefix }); module.exports = { NotifyingBucket };Python class NotifyingBucket(Construct): def __init__(self, scope: Construct, id: str, *, prefix=None, **kwargs): super().__init__(scope, id) bucket = s3.Bucket(self, "bucket") self.topic = sns.Topic(self, "topic") bucket.add_object_created_notification(s3notify.SnsDestination(self.topic), s3.NotificationKeyFilter(prefix=prefix))public class NotifyingBucket extends Construct { public Topic topic = null; public NotifyingBucket(final Construct scope, final String id) { this(scope, id, null, null); public NotifyingBucket(final Construct scope, final String id, final BucketProps props) { this(scope, id, props, null); public NotifyingBucket(final Construct scope, final String id, final String prefix) { this(scope, id, null, prefix); public NotifyingBucket(final Construct scope, final String id, final BucketProps props, final String prefix) { super(scope, id); Bucket bucket = new Bucket(this, "bucket"); topic = new Topic(this, "topic"); if (prefix != null) bucket.addObjectCreatedNotification(new SnsDestination(topic), NotificationKeyFilter.builder().prefix(prefix).build());public class NotifyingBucket : Construct public readonly Topic topic; public NotifyingBucket(Construct scope, string id, NotifyingBucketProps props = null) : base(scope, id) var bucket = new Bucket(this, "bucket"); topic = new Topic(this, "topic"); bucket.AddObjectCreatedNotification(new SnsDestination(topic), new NotificationKeyFilter Prefix = props?.PrefixTypeScript const queue = new sqs.Queue(this, 'NewImagesQueue'); const images = new NotifyingBucket(this, '/images'); images.topic.addSubscription(new sns_sub.SqsSubscription(queue));JavaScript const queue = new sqs.Queue(this, 'NewImagesQueue'); const images = new NotifyingBucket(this, '/images'); images.topic.addSubscription(new sns_sub.SqsSubscription(queue));Python queue = sqs.Queue(self, "NewImagesQueue") images = NotifyingBucket(self, prefix="Images") images.topic.add_subscription(sns_sub.SqsSubscription(queue))NotifyingBucket images = new NotifyingBucket(this, "MyNotifyingBucket", "/images"); images.topic.addSubscription(new SqsSubscription(queue));var queue = new Queue(this, "NewImagesQueue"); var images = new NotifyingBucket(this, "MyNotifyingBucket", new NotifyingBucketProps Prefix = "/images" images.topic.AddSubscription(new SqsSubscription(queue));Construct
正如我们已经看到的,在AWS CDK应用程序中,您可以使用传递给每个构造的
scope参数在 "内部" 定义构造。通过这种方式,AWS CDK应用程序定义了一种称为构造树的构造层次结构。这棵树的根是你的应用程序,即
App类的实例。在应用程序中,您可以实例化一个或多个堆栈。在堆栈中,您可以实例化AWS CloudFormation资源或更高级别的结构,它们本身可以实例化资源或其他结构,依此类推。构造总是在另一个构造的范围内明确定义的,因此构造之间的关系是毫无疑问的。几乎总是应该传递
this(在 Python 中self)作为作用域,这表明新构造是当前构造的子构造。预期的模式是你从中派生你的构造Construct,然后实例化它在构造函数中使用的构造。显式传递作用域允许每个构造将其自身添加到树中,而这种行为完全包含在
Construct基类中。它在支持的每种语言中都以相同的方式工作AWS CDK,不需要反省或其他 "魔法"。重要
从技术上讲,除了实例化构造之外
this,还可以传递一些作用域。你可以在树中的任何地方添加构造,甚至可以在同一个应用程序的另一个堆栈中添加构造。例如,你可以编写一个 mixin 风格的函数,将构造添加到作为参数传入的作用域中。这里的实际困难在于,你无法轻易确保为构造选择的 ID 在别人的范围内是唯一的。这种做法还会使你的代码更难理解、维护和重用。在不滥用scope论点的情况下,找到一种表达意图的方法几乎总是更好的。AWS CDK使用从树根到每个子构造的路径中所有构造的 ID 来生成所需的唯一 IDAWS CloudFormation。这种方法意味着构造 ID 只需要在其范围内是唯一的,而不是像原生那样在整个堆栈中是唯一的AWS CloudFormation。但是,如果您将构造移到不同的作用域,则其生成的堆栈唯一 ID 会发生变化,并且AWS CloudFormation不会将其视为相同的资源。
构造树与您在AWS CDK代码中定义的构造是分开的。但是,它可以通过任何构造的
node属性进行访问,该属性是对代表树中该构造的节点的引用。每个节点都是一个Node实例,其属性提供对树的根以及节点的父作用域和子级的访问权限。