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

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

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

AWS CDK 堆栈简介

AWS CDK 堆栈是最小的单个部署单元。它表示您使用CDK构造定义的 AWS 资源集合。部署CDK应用程序时,堆栈中的资源将作为CDK AWS CloudFormation 堆栈一起部署。要了解有关 AWS CloudFormation 堆栈的更多信息,请参阅《AWS CloudFormation 用户指南》中的使用 AWS CloudFormation 堆栈将AWS资源作为单个单元进行管理

您可以通过从 Stack 构造扩展或继承,以此来定义堆栈。以下示例是在单独的文件(称为CDK堆栈文件)上定义堆栈的常用模式。在本例中,我们扩展或继承 Stack 类并定义一个接受 scopeidprops 的构造函数。然后,我们使用 super 和接收到的 scopeidprops 调用 Stack 基类构造函数:

TypeScript
import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class MyCdkStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // Define your constructs here } }
JavaScript
const { Stack } = require('aws-cdk-lib'); class MyCdkStack extends Stack { constructor(scope, id, props) { super(scope, id, props); // Define your constructs here } } module.exports = { MyCdkStack }
Python
from aws_cdk import ( Stack, ) from constructs import Construct class MyCdkStack(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # Define your constructs here
Java
package com.myorg; import software.constructs.Construct; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; public class MyCdkStack extends Stack { public MyCdkStack(final Construct scope, final String id) { this(scope, id, null); } public MyCdkStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); // Define your constructs here } }
C#
using Amazon.CDK; using Constructs; namespace MyCdk { public class MyCdkStack : Stack { internal MyCdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { // Define your constructs here } } }
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) type CdkDemoAppStackProps struct { awscdk.StackProps } func NewCdkDemoAppStack(scope constructs.Construct, id string, props *CdkDemoAppStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } stack := awscdk.NewStack(scope, &id, &sprops) // The code that defines your stack goes here return stack } func main() { defer jsii.Close() app := awscdk.NewApp(nil) NewCdkDemoAppStack(app, "CdkDemoAppStack", &CdkDemoAppStackProps{ awscdk.StackProps{ Env: env(), }, }) app.Synth(nil) } //...

前面的示例仅定义了一个堆栈。要创建堆栈,必须在应用程序的上下文中对其进行实例化。CDK一种常见的模式是定义您的CDK应用程序并在一个单独的文件(称为应用程序文件)上初始化堆栈。

以下是创建名为的CDK堆栈的示例MyCdkStack。在这里,CDK应用程序是在应用程序的上下文中创建和MyCdkStack实例化的:

TypeScript
#!/usr/bin/env node import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; import { MyCdkStack } from '../lib/my-cdk-stack'; const app = new cdk.App(); new MyCdkStack(app, 'MyCdkStack', { });
JavaScript
#!/usr/bin/env node const cdk = require('aws-cdk-lib'); const { MyCdkStack } = require('../lib/my-cdk-stack'); const app = new cdk.App(); new MyCdkStack(app, 'MyCdkStack', { });
Python

位于 app.py 中:

#!/usr/bin/env python3 import os import aws_cdk as cdk from my_cdk.my_cdk_stack import MyCdkStack app = cdk.App() MyCdkStack(app, "MyCdkStack",) app.synth()
Java
package com.myorg; import software.amazon.awscdk.App; import software.amazon.awscdk.Environment; import software.amazon.awscdk.StackProps; import java.util.Arrays; public class MyCdkApp { public static void main(final String[] args) { App app = new App(); new MyCdkStack(app, "MyCdkStack", StackProps.builder() .build()); app.synth(); } }
C#
using Amazon.CDK; using System; using System.Collections.Generic; using System.Linq; namespace MyCdk { sealed class Program { public static void Main(string[] args) { var app = new App(); new MyCdkStack(app, "MyCdkStack", new StackProps {}); app.Synth(); } } }
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) // ... func main() { defer jsii.Close() app := awscdk.NewApp(nil) NewMyCdkStack(app, "MyCdkStack", &MyCdkStackProps{ awscdk.StackProps{ Env: env(), }, }) app.Synth(nil) } // ...

以下示例创建了一个包含两个堆栈的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();
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) type MyFirstStackProps struct { awscdk.StackProps } func NewMyFirstStack(scope constructs.Construct, id string, props *MyFirstStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } myFirstStack := awscdk.NewStack(scope, &id, &sprops) // The code that defines your stack goes here return myFirstStack } type MySecondStackProps struct { awscdk.StackProps } func NewMySecondStack(scope constructs.Construct, id string, props *MySecondStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } mySecondStack := awscdk.NewStack(scope, &id, &sprops) // The code that defines your stack goes here return mySecondStack } func main() { defer jsii.Close() app := awscdk.NewApp(nil) NewMyFirstStack(app, "MyFirstStack", &MyFirstStackProps{ awscdk.StackProps{ Env: env(), }, }) NewMySecondStack(app, "MySecondStack", &MySecondStackProps{ awscdk.StackProps{ Env: env(), }, }) app.Synth(nil) } // ...

关于堆栈 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 命令中指定堆栈名称合成各个模板。以下示例将为 stack1 合成模板:

$ cdk synth stack1

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

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

注意

在合成期间 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(); } }
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) type ControlPlaneStackProps struct { awscdk.StackProps } func NewControlPlaneStack(scope constructs.Construct, id string, props *ControlPlaneStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } ControlPlaneStack := awscdk.NewStack(scope, jsii.String(id), &sprops) // The code that defines your stack goes here return ControlPlaneStack } type DataPlaneStackProps struct { awscdk.StackProps } func NewDataPlaneStack(scope constructs.Construct, id string, props *DataPlaneStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } DataPlaneStack := awscdk.NewStack(scope, jsii.String(id), &sprops) // The code that defines your stack goes here return DataPlaneStack } type MonitoringStackProps struct { awscdk.StackProps } func NewMonitoringStack(scope constructs.Construct, id string, props *MonitoringStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } MonitoringStack := awscdk.NewStack(scope, jsii.String(id), &sprops) // The code that defines your stack goes here return MonitoringStack } type MyServiceStackProps struct { awscdk.StackProps Prod bool } func NewMyServiceStack(scope constructs.Construct, id string, props *MyServiceStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } MyServiceStack := awscdk.NewStack(scope, jsii.String(id), &sprops) NewControlPlaneStack(MyServiceStack, "cp", &ControlPlaneStackProps{ StackProps: sprops, }) NewDataPlaneStack(MyServiceStack, "data", &DataPlaneStackProps{ StackProps: sprops, }) NewMonitoringStack(MyServiceStack, "mon", &MonitoringStackProps{ StackProps: sprops, }) return MyServiceStack } func main() { defer jsii.Close() app := awscdk.NewApp(nil) betaProps := MyServiceStackProps{ StackProps: awscdk.StackProps{ Env: env(), }, Prod: false, } NewMyServiceStack(app, "beta", &betaProps) prodProps := MyServiceStackProps{ StackProps: awscdk.StackProps{ Env: env(), }, Prod: true, } NewMyServiceStack(app, "prod", &prodProps) app.Synth(nil) } // ...

该 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" });

使用嵌套堆栈

嵌套堆CDK栈是您在另一个堆栈(称为父堆栈)中创建的堆栈。您可以使用 NestedStack 构造创建嵌套堆栈。

通过使用嵌套堆栈,您可以跨多个堆栈组织资源。嵌套堆栈还提供了绕过堆栈的 AWS CloudFormation 500 个资源限制的方法。嵌套堆栈仅算作其所属堆栈中的一个资源。但是,它最多可以包含 500 个资源,包括额外的嵌套堆栈。

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

在合成时,嵌套堆栈会合成到自己的 AWS CloudFormation 模板中,并在部署时将其上传到 AWS CDK 暂存桶。嵌套堆栈从属于其父堆栈,不会被视为独立的部署构件。它们无法使用 cdk list 列出,也无法使用 cdk deploy 部署。

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

警告

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