AWS CDK 堆栈 - AWS Cloud Development Kit (AWS CDK) v2

这是 AWS CDK v2 开发者指南。较旧的 CDK v1 于 2022 年 6 月 1 日进入维护阶段,并于 2023 年 6 月 1 日终止支持。

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

AWS CDK 堆栈

AWS Cloud Development Kit (AWS CDK) 堆栈是一个或多个结构的集合,用于定义 AWS 资源。每个CDK堆栈代表您的CDK应用程序中的一个 AWS CloudFormation 堆栈。部署时,堆栈中的构造将作为一个单元(称为堆栈)进行 AWS CloudFormation 配置。要了解有关 AWS CloudFormation 堆栈的更多信息,请参阅《AWS CloudFormation 用户指南》中的使用堆栈

由于CDK堆栈是通过 AWS CloudFormation 堆栈实现的,因此适用 AWS CloudFormation 配额和限制。要了解更多信息,请参阅AWS CloudFormation 配额

如何定义堆栈

堆栈是在应用程序的上下文中定义的。您可以使用构造库中的Stack构造来定义堆栈。 AWS 可以通过以下任何一种方式定义堆栈:

  • 直接在应用程序的范围内。

  • 由树中的任何构造间接获得。

以下示例定义了一个包含两个堆栈的CDK应用程序:

TypeScript
const app = new App(); new MyFirstStack(app, 'stack1'); new MySecondStack(app, 'stack2'); app.synth();
JavaScript
const app = new App(); new MyFirstStack(app, 'stack1'); new MySecondStack(app, 'stack2'); app.synth();
Python
app = App() MyFirstStack(app, 'stack1') MySecondStack(app, 'stack2') app.synth()
Java
App app = new App(); new MyFirstStack(app, "stack1"); new MySecondStack(app, "stack2"); app.synth();
C#
var app = new App(); new MyFirstStack(app, "stack1"); new MySecondStack(app, "stack2"); app.Synth();

以下示例是在单独文件上定义堆栈的常用模式。在这里,我们扩展或继承Stack类并定义一个接受scopeid、和的构造函数props。然后,我们使用super接收到的、和scopeid调用基Stack类构造函数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) # ...
Java
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); // ... } }
C#
public class HelloCdkStack : Stack { public HelloCdkStack(Construct scope, string id, IStackProps props=null) : base(scope, id, props) { //... } }
Go
func HelloCdkStack(scope constructs.Construct, id string, props *HelloCdkStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } stack := awscdk.NewStack(scope, &id, &sprops) return stack }

以下示例声明了一个名为的堆栈类MyFirstStack,其中包含一个 Amazon S3 存储桶。

TypeScript
class MyFirstStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); new s3.Bucket(this, 'MyFirstBucket'); } }
JavaScript
class MyFirstStack extends Stack { constructor(scope, id, props) { super(scope, id, props); new s3.Bucket(this, 'MyFirstBucket'); } }
Python
class MyFirstStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) s3.Bucket(self, "MyFirstBucket")
Java
public class MyFirstStack extends Stack { public MyFirstStack(final Construct scope, final String id) { this(scope, id, null); } public MyFirstStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); new Bucket(this, "MyFirstBucket"); } }
C#
public class MyFirstStack : Stack { public MyFirstStack(Stack scope, string id, StackProps props = null) : base(scope, id, props) { new Bucket(this, "MyFirstBucket"); } }
Go
func MyFirstStack(scope constructs.Construct, id string, props *MyFirstStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } stack := awscdk.NewStack(scope, &id, &sprops) s3.NewBucket(stack, jsii.String("MyFirstBucket"), &s3.BucketProps{}) return stack }

但是,此代码仅声明了一个堆栈。要将堆栈实际合成 AWS CloudFormation 模板并进行部署,必须对其进行实例化。而且,像所有CDK构造一样,它必须在某些上下文中实例化。App就是那个背景。

如果您使用的是标准 AWS CDK 开发模板,则您的堆栈将在您实例化对象的同一个文件中进行实例化。App

TypeScript

项目bin文件夹中以您的项目(例如hello-cdk.ts)命名的文件。

JavaScript

项目bin文件夹中以您的项目(例如hello-cdk.js)命名的文件。

Python

项目主目录app.py中的文件。

Java

例如 ProjectNameApp.javaHelloCdkApp.java,名为的文件嵌套在src/main目录的深处。

C#

例如src\ProjectName,名为Program.cs下的文件src\HelloCdk\Program.cs

堆栈 API

Stack 对象提供了丰富的内容API,包括以下内容:

  • Stack.of(construct)— 一种静态方法,它返回在其中定义构造的堆栈。如果您需要在可重复使用的构造中与堆栈进行交互,这将非常有用。如果在作用域中找不到堆栈,则调用将失败。

  • stack.stackName(Python:stack_name)-返回堆栈的物理名称。如前所述,所有 AWS CDK 堆栈都有一个物理名称, AWS CDK 可以在合成过程中解析。

  • stack.regionstack.account — 分别返回此堆栈将部署到的 AWS 区域和账户。这些属性返回以下内容之一:

    • 定义堆栈时明确指定的账户或区域

    • 字符串编码的令牌,可解析为账户和区域的 AWS CloudFormation 伪参数,以表明此堆栈与环境无关

    有关如何确定堆栈环境的信息,请参阅的环境 AWS CDK

  • stack.addDependency(stack)(Python: stack.add_dependency(stack) — 可用于明确定义两个堆栈之间的依赖顺序。同时部署多个堆栈时,cdk deploy命令会遵守此顺序。

  • stack.tags— 返回TagManager可用于添加或移除堆栈级别标签的。此标签管理器会标记堆栈中的所有资源,并在通过创建堆栈时对堆栈本身进行标记 AWS CloudFormation。

  • stack.partitionstack.urlSuffix (Python:url_suffix)、stack.stackId (Python:notification_arn) 和 stack.notificationArn (Python:) — 返回解析为相应 AWS CloudFormation 伪参数的标记,例如{ "Ref": "AWS::Partition" }stack_id这些令牌与特定的堆栈对象相关联,因此 AWS CDK 框架可以识别跨堆栈引用。

  • stack.availabilityZones(Python:availability_zones)-返回部署此堆栈的环境中可用的一组可用区。对于与环境无关的堆栈,这始终会返回一个包含两个可用区的数组。对于特定于环境的堆栈,会 AWS CDK 查询环境并返回您指定的区域中可用的确切可用区集。

  • stack.parseArn(arn)stack.formatArn(comps) (Python:parse_arn,format_arn) — 可用于处理亚马逊资源名称 (ARNs)。

  • stack.toJsonString(obj)(Python:to_json_string)-可用于将任意对象格式化为可以嵌入到 AWS CloudFormation 模板中的JSON字符串。该对象可以包含标记、属性和引用,这些标记、属性和引用只能在部署期间解析。

  • stack.templateOptions(Python:template_options)-用于为堆栈指定 AWS CloudFormation 模板选项,例如转换、描述和元数据。

使用 堆栈

堆栈作为 AWS CloudFormation 堆栈的一部分部署到 AWS 环境中。环境涵盖了特定的 AWS 账户 和 AWS 区域.

当您为具有多个堆栈的应用程序运行cdk synth命令时,云程序集会为每个堆栈实例包含一个单独的模板。即使这两个堆栈是同一个类的实例,它们也会将它们作为两个单独 AWS CDK 的模板发出。

您可以通过在cdk synth命令中指定堆栈名称来合成每个模板。以下示例合成了 st ack1 的模板。

$ cdk synth stack1

这种方法在概念上与通常使用 AWS CloudFormation 模板的方式不同,在模板中,模板可以多次部署并通过参数进行参数化。AWS CloudFormation尽管可以在中定义 AWS CloudFormation 参数 AWS CDK,但通常不鼓励使用这些参数,因为 AWS CloudFormation 参数只能在部署期间解析。这意味着您无法在代码中确定它们的值。

例如,要根据参数值有条件地将资源包含在应用程序中,您必须设置AWS CloudFormation 条件并使用该条件标记该资源。 AWS CDK 采用的方法是在合成时解析混凝土模板。因此,您可以使用 i f 语句来检查该值,以确定是应定义资源还是应应用某些行为。

注意

在合成期间 AWS CDK 提供尽可能多的分辨率,以实现编程语言的惯用和自然使用。

像任何其他构造一样,堆栈可以组合成组。以下代码显示了由三个堆栈组成的服务示例:控制平面、数据平面和监控堆栈。服务结构定义了两次:一次用于测试环境,另一次用于生产环境。

TypeScript
import { App, Stack } from 'aws-cdk-lib'; import { Construct } from 'constructs'; interface EnvProps { prod: boolean; } // imagine these stacks declare a bunch of related resources class ControlPlane extends Stack {} class DataPlane extends Stack {} class Monitoring extends Stack {} class MyService extends Construct { constructor(scope: Construct, id: string, props?: EnvProps) { super(scope, id); // we might use the prod argument to change how the service is configured new ControlPlane(this, "cp"); new DataPlane(this, "data"); new Monitoring(this, "mon"); } } const app = new App(); new MyService(app, "beta"); new MyService(app, "prod", { prod: true }); app.synth();
JavaScript
const { App, Stack } = require('aws-cdk-lib'); const { Construct } = require('constructs'); // imagine these stacks declare a bunch of related resources class ControlPlane extends Stack {} class DataPlane extends Stack {} class Monitoring extends Stack {} class MyService extends Construct { constructor(scope, id, props) { super(scope, id); // we might use the prod argument to change how the service is configured new ControlPlane(this, "cp"); new DataPlane(this, "data"); new Monitoring(this, "mon"); } } const app = new App(); new MyService(app, "beta"); new MyService(app, "prod", { prod: true }); app.synth();
Python
from aws_cdk import App, Stack from constructs import Construct # imagine these stacks declare a bunch of related resources class ControlPlane(Stack): pass class DataPlane(Stack): pass class Monitoring(Stack): pass class MyService(Construct): def __init__(self, scope: Construct, id: str, *, prod=False): super().__init__(scope, id) # we might use the prod argument to change how the service is configured ControlPlane(self, "cp") DataPlane(self, "data") Monitoring(self, "mon") app = App(); MyService(app, "beta") MyService(app, "prod", prod=True) app.synth()
Java
package com.myorg; import software.amazon.awscdk.App; import software.amazon.awscdk.Stack; import software.constructs.Construct; public class MyApp { // imagine these stacks declare a bunch of related resources static class ControlPlane extends Stack { ControlPlane(Construct scope, String id) { super(scope, id); } } static class DataPlane extends Stack { DataPlane(Construct scope, String id) { super(scope, id); } } static class Monitoring extends Stack { Monitoring(Construct scope, String id) { super(scope, id); } } static class MyService extends Construct { MyService(Construct scope, String id) { this(scope, id, false); } MyService(Construct scope, String id, boolean prod) { super(scope, id); // we might use the prod argument to change how the service is configured new ControlPlane(this, "cp"); new DataPlane(this, "data"); new Monitoring(this, "mon"); } } public static void main(final String argv[]) { App app = new App(); new MyService(app, "beta"); new MyService(app, "prod", true); app.synth(); } }
C#
using Amazon.CDK; using Constructs; // imagine these stacks declare a bunch of related resources public class ControlPlane : Stack { public ControlPlane(Construct scope, string id=null) : base(scope, id) { } } public class DataPlane : Stack { public DataPlane(Construct scope, string id=null) : base(scope, id) { } } public class Monitoring : Stack { public Monitoring(Construct scope, string id=null) : base(scope, id) { } } public class MyService : Construct { public MyService(Construct scope, string id, Boolean prod=false) : base(scope, id) { // we might use the prod argument to change how the service is configured new ControlPlane(this, "cp"); new DataPlane(this, "data"); new Monitoring(this, "mon"); } } class Program { static void Main(string[] args) { var app = new App(); new MyService(app, "beta"); new MyService(app, "prod", prod: true); app.Synth(); } }

该 AWS CDK 应用程序最终由六个堆栈组成,每个环境三个堆栈:

$ cdk ls betacpDA8372D3 betadataE23DB2BA betamon632BD457 prodcp187264CE proddataF7378CE5 prodmon631A1083

AWS CloudFormation 堆栈的物理名称由 AWS CDK 基于堆栈在树中的构造路径自动确定。默认情况下,堆栈的名称源自Stack对象的构造 ID。但是,您可以使用 stackName prop(在 Python 中stack_name)指定显式名称,如下所示。

TypeScript
new MyStack(this, 'not:a:stack:name', { stackName: 'this-is-stack-name' });
JavaScript
new MyStack(this, 'not:a:stack:name', { stackName: 'this-is-stack-name' });
Python
MyStack(self, "not:a:stack:name", stack_name="this-is-stack-name")
Java
new MyStack(this, "not:a:stack:name", StackProps.builder() .StackName("this-is-stack-name").build());
C#
new MyStack(this, "not:a:stack:name", new StackProps { StackName = "this-is-stack-name" });

嵌套堆栈

NestedStack构造为规避堆栈的 AWS CloudFormation 500 个资源限制提供了一种方法。嵌套堆栈仅算作包含它的堆栈中的一个资源。但是,它最多可以包含 500 个资源,包括额外的嵌套堆栈。

嵌套堆栈的作用域必须是StackNestedStack构造。嵌套堆栈不需要在其父堆栈中按词法声明。实例化嵌套堆栈时,只需要将父堆栈作为第一个参数 (scope) 传递。除了这个限制之外,在嵌套堆栈中定义构造的工作原理与在普通堆栈中定义构造完全相同。

在合成时,嵌套堆栈会合成到自己的 AWS CloudFormation 模板中,并在部署时将其上传到 AWS CDK 暂存桶。嵌套堆栈绑定到其父堆栈,不会被视为独立的部署工件。它们不是由列出的cdk list,也不能由其部署cdk deploy

父堆栈和嵌套堆栈之间的引用会自动转换为生成的 AWS CloudFormation 模板中的堆栈参数和输出,就像任何堆栈引用一样。

警告

在部署嵌套堆栈之前,不会显示安全状态的变化。此信息仅针对顶级堆栈显示。