View a markdown version of this page

标识符和 AWS CDK - AWS Cloud Development Kit (AWS CDK) v2

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

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

标识符和 AWS CDK

在构建 C AWS loud Development Kit (AWS CDK) 应用程序时,您将使用多种类型的标识符和名称。要有效使用 AWS CDK 并避免错误,了解标识符的类型非常重要。

标识符在创建时必须是唯一的;它们在您的 AWS CDK 应用程序中不必是全局唯一的。

如果您尝试在相同范围内创建具有相同值的标识符, AWS CDK 将引发异常。

构造 IDs

最常见的标识符 id 是在实例化构造对象时作为第二个参数传递的标识符。与所有标识符一样,此标识符只需要在创建它的作用域内是唯一的,该作用域是实例化构造对象时的第一个参数。

注意

堆栈的 id 的也是您在 AWS CDK CLI 参考中用来对其进行引用的标识符。

我们来看一个示例,其中我们的应用程序中有两个带有标识符 MyBucket 的构造。第一个是在堆栈的作用域中定义的,标识符为 Stack1。第二个是在堆栈的作用域中定义的,标识符为 Stack2。因为它们是在不同的作用域中定义的,所以这不会造成任何冲突,而且它们可以共存于同一个应用程序中而不会导致问题。

TypeScript
import { App, Stack, StackProps } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as s3 from 'aws-cdk-lib/aws-s3'; class MyStack extends Stack { constructor(scope: Construct, id: string, props: StackProps = {}) { super(scope, id, props); new s3.Bucket(this, 'MyBucket'); } } const app = new App(); new MyStack(app, 'Stack1'); new MyStack(app, 'Stack2');
JavaScript
const { App , Stack } = require('aws-cdk-lib'); const s3 = require('aws-cdk-lib/aws-s3'); class MyStack extends Stack { constructor(scope, id, props = {}) { super(scope, id, props); new s3.Bucket(this, 'MyBucket'); } } const app = new App(); new MyStack(app, 'Stack1'); new MyStack(app, 'Stack2');
Python
from aws_cdk import App, Construct, Stack, StackProps from constructs import Construct from aws_cdk import aws_s3 as s3 class MyStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) s3.Bucket(self, "MyBucket") app = App() MyStack(app, 'Stack1') MyStack(app, 'Stack2')
Java
// MyStack.java package com.myorg; import software.amazon.awscdk.App; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; import software.constructs.Construct; import software.amazon.awscdk.services.s3.Bucket; public class MyStack extends Stack { public MyStack(final Construct scope, final String id) { this(scope, id, null); } public MyStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); new Bucket(this, "MyBucket"); } } // Main.java package com.myorg; import software.amazon.awscdk.App; public class Main { public static void main(String[] args) { App app = new App(); new MyStack(app, "Stack1"); new MyStack(app, "Stack2"); } }
C#
using Amazon.CDK; using constructs; using Amazon.CDK.AWS.S3; public class MyStack : Stack { public MyStack(Construct scope, string id, IStackProps props) : base(scope, id, props) { new Bucket(this, "MyBucket"); } } class Program { static void Main(string[] args) { var app = new App(); new MyStack(app, "Stack1"); new MyStack(app, "Stack2"); } }

路径

AWS CDK 应用程序中的构造形成了植根于该App类的层次结构。我们将 IDs 从给定构造、其父构造、其祖父构造等到构造树根部的集合称为路径

AWS CDK 通常将模板中的路径显示为字符串。各个关卡之间用斜线隔开, IDs 从根实例正下方的节点开始,根App实例通常是堆栈。例如,在前面的代码示例中,两个 Amazon S3 存储桶资源的路径分别是 Stack1/MyBucketStack2/MyBucket

您能以编程方式访问任何构造的路径,如以下示例所示。这会得到路径 myConstruct(Python:my_construct)。由于它们在创建的范围内 IDs 必须是唯一的,因此它们的路径在 AWS CDK 应用程序中始终是唯一的。

TypeScript
const path: string = myConstruct.node.path;
JavaScript
const path = myConstruct.node.path;
Python
path = my_construct.node.path
Java
String path = myConstruct.getNode().getPath();
C#
string path = myConstruct.Node.Path;

独特 IDs

AWS CloudFormation 要求模板 IDs 中的所有逻辑都必须是唯一的。因此, AWS CDK 必须能够为应用程序中的每个构造生成唯一标识符。资源的路径是全局唯一的(从堆栈到特定资源的所有作用域的名称)。因此, AWS CDK 通过连接路径元素并添加 8 位哈希来生成必要的唯一标识符。(哈希值是区分不同路径(例如A/B/C和)所必需的A/BC,因为这些路径会生成相同的 AWS CloudFormation 标识符。 AWS CloudFormation 标识符是字母数字,不能包含斜杠或其他分隔符。) AWS CDK 将此字符串称为构造的唯一 ID

通常,你的 AWS CDK 应用程序不需要知道唯一性 IDs。但是,您能以编程方式访问任何构造的唯一 ID,如以下示例所示。

TypeScript
const uid: string = Names.uniqueId(myConstruct);
JavaScript
const uid = Names.uniqueId(myConstruct);
Python
uid = Names.unique_id(my_construct)
Java
String uid = Names.uniqueId(myConstruct);
C#
string uid = Names.Uniqueid(myConstruct);

地址是另一种用于唯一区分 CDK 资源的唯一标识符。它源自路径的 SHA-1 哈希值,人类不可读。但是,长度恒定、相对较短(始终为 42 个十六进制字符)的特点使其在“传统”唯一 ID 可能过长的情况下非常有用。某些构造可能会使用合成 AWS CloudFormation 模板中的地址而不是唯一 ID。同样,您的应用程序通常不需要知道其构造的地址,但您可以按如下方式检索构造的地址。

TypeScript
const addr: string = myConstruct.node.addr;
JavaScript
const addr = myConstruct.node.addr;
Python
addr = my_construct.node.addr
Java
String addr = myConstruct.getNode().getAddr();
C#
string addr = myConstruct.Node.Addr;

逻辑 IDs

当 AWS CDK 将您的应用程序合成 AWS CloudFormation 模板时,它会为每个资源生成一个逻辑 ID。 AWS CloudFormation 使用逻辑 IDs 来识别模板中的资源并在部署中跟踪这些资源。了解逻辑 IDs 的生成方式有助于您在重构 CDK 代码时避免意外的资源替换。

如何生成 IDs 逻辑

AWS CDK 使用以下算法 IDs 从构造路径生成逻辑:

  1. 连接构造树中的路径组件,不包括堆栈本身。

  2. 应用启发式方法来提高可读性(参见逻辑 ID 路径组件启发式算法)。

  3. 附加完整路径的 8 个字符的哈希值以确保唯一性。

生成的格式为:

<human-readable-portion><8-character-hash>

例如,VPC 私有子网路由表可能会生成逻辑 ID VPCPrivateSubnet2RouteTable0A19E10E

以下规则适用于逻辑 ID 的生成:

  • 最大长度为 255 个字符。人类可读部分上限为 240 个字符。

  • 8 个字符的哈希可确保A/B/C和之类的路径A/BC(连接成同一个字符串)产生不同的逻辑。 IDs

  • 直接作为堆栈子项的资源(单组件路径),只要名称不超过 255 个字符,就直接使用其名称,而不使用哈希。

逻辑 ID 路径组件启发式算法

AWS CDK 在生成逻辑的人类可读部分时,将以下启发式方法应用于路径组件。 IDs

Default— 完全移除

如果路径组件是Default,CDK 会将其从人类可读部分和哈希输入中删除。这意味着,将现有构造封装在新构造中——并命名内部构造 Default ——产生的逻辑 ID 与原始未包装的构造完全相同。这是在不更改已部署资源标识的情况下将平面代码安全地重构为更高级别结构的关键机制。

Resource— 仅在人类可读部分中隐藏

如果路径组件是Resource,CDK 会将其从人类可读部分中省略,但仍将其包含在哈希计算中。按照惯例,L1 (CloudFormation) 构造使用Resource作为其构造 ID。这样可以 IDs 缩短逻辑时间,而不会失去唯一性。

重复的连续组件 — 已删除重复数据

如果前面的路径组件名称以当前组件名称结尾,则 CDK 会跳过当前组件。这样可以防止逻辑 IDs上的冗余重复。

用于Default在重构 IDs 时保持逻辑

当您将平面堆栈重构为更高级别的构造时,可以将其Default用作主资源的构造 ID 以保留其逻辑 ID。这样可以 AWS CloudFormation 防止在部署期间更换资源。

以下示例显示了包含直接定义资源的堆栈:

TypeScript
export class MyStack extends cdk.Stack { constructor(scope: Construct, id: string) { super(scope, id); new s3.Bucket(this, 'DataBucket'); new lambda.Function(this, 'ProcessFunction', { /* ... */ }); } }
JavaScript
class MyStack extends cdk.Stack { constructor(scope, id) { super(scope, id); new s3.Bucket(this, 'DataBucket'); new lambda.Function(this, 'ProcessFunction', { /* ... */ }); } }
Python
from aws_cdk import ( Stack, aws_s3 as s3, aws_lambda as _lambda, ) from constructs import Construct class MyStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) s3.Bucket(self, "DataBucket") _lambda.Function(self, "ProcessFunction", # ... )
Java
package com.myorg; import software.constructs.Construct; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; import software.amazon.awscdk.services.s3.Bucket; import software.amazon.awscdk.services.lambda.Function; public class MyStack extends Stack { public MyStack(final Construct scope, final String id) { this(scope, id, null); } public MyStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); new Bucket(this, "DataBucket"); Function.Builder.create(this, "ProcessFunction") // ... .build(); } }
C#
using Amazon.CDK; using Constructs; using Amazon.CDK.AWS.S3; using Amazon.CDK.AWS.Lambda; namespace MyApp { public class MyStack : Stack { public MyStack(Construct scope, string id, StackProps props = null) : base(scope, id, props) { new Bucket(this, "DataBucket"); new Function(this, "ProcessFunction", new FunctionProps { // ... }); } } }
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/aws-cdk-go/awscdk/v2/awslambda" "github.com/aws/aws-cdk-go/awscdk/v2/awss3" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) type MyStackProps struct { awscdk.StackProps } func NewMyStack(scope constructs.Construct, id string, props *MyStackProps) awscdk.Stack { stack := awscdk.NewStack(scope, &id, &props.StackProps) awss3.NewBucket(stack, jsii.String("DataBucket"), &awss3.BucketProps{}) awslambda.NewFunction(stack, jsii.String("ProcessFunction"), &awslambda.FunctionProps{ // ... }) return stack }

存储桶的路径是MyStack/DataBucket/Resource,生成的逻辑 ID 为DataBucket<hash>

您可以将存储桶提取到更高级别的构造中,并通过命名内部构造Default来保留相同的逻辑 ID:

TypeScript
class DataPipeline extends Construct { constructor(scope: Construct, id: string) { super(scope, id); new s3.Bucket(this, 'Default'); // 'Default' is hidden from logical ID new lambda.Function(this, 'ProcessFunction', { /* ... */ }); } } export class MyStack extends cdk.Stack { constructor(scope: Construct, id: string) { super(scope, id); new DataPipeline(this, 'DataBucket'); } }
JavaScript
class DataPipeline extends Construct { constructor(scope, id) { super(scope, id); new s3.Bucket(this, 'Default'); // 'Default' is hidden from logical ID new lambda.Function(this, 'ProcessFunction', { /* ... */ }); } } class MyStack extends cdk.Stack { constructor(scope, id) { super(scope, id); new DataPipeline(this, 'DataBucket'); } }
Python
from aws_cdk import ( Stack, aws_s3 as s3, aws_lambda as _lambda, ) from constructs import Construct class DataPipeline(Construct): def __init__(self, scope: Construct, id: str) -> None: super().__init__(scope, id) s3.Bucket(self, "Default") # 'Default' is hidden from logical ID _lambda.Function(self, "ProcessFunction", # ... ) class MyStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) DataPipeline(self, "DataBucket")
Java
package com.myorg; import software.constructs.Construct; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; import software.amazon.awscdk.services.s3.Bucket; import software.amazon.awscdk.services.lambda.Function; public class DataPipeline extends Construct { public DataPipeline(final Construct scope, final String id) { super(scope, id); new Bucket(this, "Default"); // 'Default' is hidden from logical ID Function.Builder.create(this, "ProcessFunction") // ... .build(); } } public class MyStack extends Stack { public MyStack(final Construct scope, final String id) { this(scope, id, null); } public MyStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); new DataPipeline(this, "DataBucket"); } }
C#
using Amazon.CDK; using Constructs; using Amazon.CDK.AWS.S3; using Amazon.CDK.AWS.Lambda; namespace MyApp { public class DataPipeline : Construct { public DataPipeline(Construct scope, string id) : base(scope, id) { new Bucket(this, "Default"); // 'Default' is hidden from logical ID new Function(this, "ProcessFunction", new FunctionProps { // ... }); } } public class MyStack : Stack { public MyStack(Construct scope, string id, StackProps props = null) : base(scope, id, props) { new DataPipeline(this, "DataBucket"); } } }
Go
package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/aws-cdk-go/awscdk/v2/awslambda" "github.com/aws/aws-cdk-go/awscdk/v2/awss3" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) type DataPipeline struct { constructs.Construct } func NewDataPipeline(scope constructs.Construct, id string) constructs.Construct { this := constructs.NewConstruct(scope, &id) // 'Default' is hidden from logical ID awss3.NewBucket(this, jsii.String("Default"), &awss3.BucketProps{}) awslambda.NewFunction(this, jsii.String("ProcessFunction"), &awslambda.FunctionProps{ // ... }) return this } type MyStackProps struct { awscdk.StackProps } func NewMyStack(scope constructs.Construct, id string, props *MyStackProps) awscdk.Stack { stack := awscdk.NewStack(scope, &id, &props.StackProps) NewDataPipeline(stack, "DataBucket") return stack }

存储桶的路径现在是MyStack/DataBucket/Default/Resource。由于Default已从人类可读部分和哈希输入中删除,因此逻辑 ID 保持不变DataBucket<hash>,与原始的 ID 相同。

重要

Default每个构造作用域只能有一个具有 ID 的子项。如果您需要同一级别的多个资源,请对其进行描述。 IDs该Default模式最适用于具有一个主要资源的单一责任结构。

限制和注意事项

使用逻辑时,请记住以下几点 IDs:

  • 每个作用域只能为一个子级分配Default构造 ID。

  • 如果您在部署后更改构造 ID,则逻辑 ID 会发生变化,从而 AWS CloudFormation 导致资源被替换。cdk diff用于在部署之前验证更改。

  • 对于逻辑 IDs 已经更改的情况,您可以使用cdk refactor命令将旧逻辑映射 IDs 到新逻辑。有关更多信息,请参阅重构 CDK 代码时保留已部署的资源

  • 有关合成的模板中逻辑 IDs 显示方式的更多信息,请参阅在AWS CloudFormation 模板 IDs 中生成逻辑

逻辑 ID 的稳定性

避免在资源创建后更改其逻辑 ID。 AWS CloudFormation 通过资源的逻辑 ID 来标识资源。因此,如果您更改资源的逻辑 ID,则使用新的逻辑 ID AWS CloudFormation 创建新资源,然后删除现有资源。根据资源的类型,这可能会导致服务中断或数据丢失,或同时导致这两种问题。