

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

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

# AWS CDK 堆栈简介
<a name="stacks"></a>

 AWS CDK 堆栈是最小的单个部署单元。它表示您使用 CDK 结构定义的 AWS 资源集合。部署 CDK 应用程序时，CDK 堆栈中的资源将作为 AWS CloudFormation 堆栈一起部署。要了解有关 AWS CloudFormation 堆栈的更多信息，请参阅《* AWS CloudFormation 用户*指南》中的[使用 AWS CloudFormation 堆栈将 AWS 资源作为单个单元进行管理](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacks.html)。

您可以通过从 [https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stack.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stack.html) 构造扩展或继承，以此来定义堆栈。以下示例是在单独的文件（称为*堆栈文件*）上定义 CDK 堆栈的常用模式。在本例中，我们扩展或继承 `Stack` 类并定义一个接受 `scope`、`id` 和 `props` 的构造函数。然后，我们使用 `super` 和接收到的 `scope`、`id` 和 `props` 调用 `Stack` 基类构造函数：

**Example**  

```
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

  }
}
```

```
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 }
```

```
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
```

```
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
  }
}
```

```
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
    }
  }
}
```

```
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 应用程序，然后在单独的文件（称为*应用程序文件*）上初始化堆栈。

下面是创建名为 `MyCdkStack` 的 CDK 堆栈的示例。本例将创建 CDK 应用程序，并在应用程序的上下文中实例化 `MyCdkStack`：

**Example**  

```
#!/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', {
});
```

```
#!/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', {
});
```
位于 `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()
```

```
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();
  }
}
```

```
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();
    }
  }
}
```

```
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 应用程序：

**Example**  

```
const app = new App();

new MyFirstStack(app, 'stack1');
new MySecondStack(app, 'stack2');

app.synth();
```

```
const app = new App();

new MyFirstStack(app, 'stack1');
new MySecondStack(app, 'stack2');

app.synth();
```

```
app = App()

MyFirstStack(app, 'stack1')
MySecondStack(app, 'stack2')

app.synth()
```

```
App app = new App();

new MyFirstStack(app, "stack1");
new MySecondStack(app, "stack2");

app.synth();
```

```
var app = new App();

new MyFirstStack(app, "stack1");
new MySecondStack(app, "stack2");

app.Synth();
```

```
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
<a name="stack-api"></a>

[https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stack.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stack.html) 对象提供了丰富的 API，包括以下内容：
+  `Stack.of(construct)`：一种静态方法，会返回在其中定义构造的**堆栈**。如果您需要在可复用的构造中与堆栈进行交互，此方法非常有用。如果在作用域中找不到堆栈，则调用失败。
+  `stack.stackName`（Python：`stack_name`）：返回堆栈的物理名称。如前所述，所有 AWS CDK 堆栈都有一个物理名称， AWS CDK 可以在合成过程中解析该名称。
+  `stack.region`和 `stack.account` — 分别返回此堆栈将部署到的 AWS 区域和账户。这些属性会返回以下值之一：
  + 定义堆栈时显式指定的账户或区域
  + 字符串编码的令牌，可解析为账户和区域的 AWS CloudFormation 伪参数，以表明此堆栈与环境无关

    有关如何确定堆栈环境的信息，请参阅 [AWS CDK 的环境](environments.md)。
+  `stack.addDependency(stack)`（Python：`stack.add_dependency(stack)`）– 可用于显式定义两个堆栈之间的依赖项顺序。同时部署多个堆栈时，`cdk deploy` 命令会遵守此顺序。
+  `stack.tags`— 返回[TagManager](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.TagManager.html)可用于添加或删除堆栈级别标签的。此标签管理器会标记堆栈中的所有资源，并在通过创建堆栈时对堆栈本身进行标记 AWS CloudFormation。
+  `stack.partition`、`stack.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 模板选项，例如转换、描述和元数据。

## 使用 堆栈
<a name="stacks-work"></a>

堆 AWS CloudFormation 栈作为堆栈部署到 AWS [环境](environments.md)中。环境涵盖特定的 AWS 账户和 AWS 区域。

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

您可以通过在 `cdk synth` 命令中指定堆栈名称合成各个模板。以下示例将为 `stack1` 合成模板：

```
$ cdk synth <stack1>
```

[这种方法在概念上与通常使用 AWS CloudFormation 模板的方式不同，在模板中，模板可以多次部署并通过参数进行参数化。AWS CloudFormation ](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html)尽管可以在 AWS CDK 中定义 AWS CloudFormation 参数，但通常不鼓励使用这些参数，因为 AWS CloudFormation 参数只能在部署期间解析。这意味着您无法在代码中确定它们的值。

例如，要根据参数值有条件地将资源包含在应用程序中，您必须设置 [AWS CloudFormation 条件](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html)并使用该条件标记资源。 AWS CDK 采用一种在合成时解析混凝土模板的方法。因此，您可以使用 `if` 语句检查该值，以确定是应定义资源还是应应用某些行为。

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

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

**Example**  

```
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();
```

```
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();
```

```
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()
```

```
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();
    }
}
```

```
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();
    }
}
```

```
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`）指定显式名称，如下所示。

**Example**  

```
new MyStack(this, 'not:a:stack:name', { stackName: 'this-is-stack-name' });
```

```
new MyStack(this, 'not:a:stack:name', { stackName: 'this-is-stack-name' });
```

```
MyStack(self, "not:a:stack:name", stack_name="this-is-stack-name")
```

```
new MyStack(this, "not:a:stack:name", StackProps.builder()
    .StackName("this-is-stack-name").build());
```

```
new MyStack(this, "not:a:stack:name", new StackProps
{
    StackName = "this-is-stack-name"
});
```

### 使用嵌套堆栈
<a name="stack-nesting"></a>

*嵌套堆栈*是您在另一个堆栈（称为父堆栈）中创建的 CDK 堆栈。您可以使用 [https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.NestedStack.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.NestedStack.html) 构造创建嵌套堆栈。

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

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

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

父堆栈和嵌套堆栈之间的引用会自动转换为生成的 AWS CloudFormation 模板中的堆栈参数和输出，就像任何[跨](resources.md#resource-stack)堆栈引用一样。

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