

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

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

# 排查常见 AWS CDK 问题
<a name="troubleshooting"></a>

本主题介绍如何排查 AWS CDK 的以下问题。
+  [更新 AWS CDK 后，AWS CDK 工具包 (CLI) 会报告与 AWS 构造库的不匹配](#troubleshooting-toolkit) 
+  [部署我的 AWS CDK 堆栈时，我收到 NoSuchBucket 错误](#troubleshooting-nobucket) 
+  [部署我的 AWS CDK 堆栈时，我收到禁用：null 消息](#troubleshooting-forbidden-null) 
+  [在合成 AWS CDK 堆栈时，我收到消息表明命令行的 cdk.json 或 \~/.cdk.json 中需要 --app](#troubleshooting-app-required) 
+  [合成 AWS CDK 堆栈时，由于 AWS CloudFormation 模板包含了太多资源，我收到错误](#troubleshooting-resource-count) 
+  [我为自动扩缩组或 VPC 指定了三个（或更多）可用区，但只部署在两个可用区中](#troubleshooting-availability-zones) 
+  [在我发出 cdk destroy 命令时，我的 S3 存储桶 DynamoDB 表或其他资源没有被删除](#troubleshooting-resource-not-deleted) 

## 更新 AWS CDK 后，AWS CDK 工具包 (CLI) 会报告与 AWS 构造库的不匹配
<a name="troubleshooting-toolkit"></a>

AWS CDK 工具包（提供 `cdk` 命令）的版本必须至少与 AWS 主构造库模块 `aws-cdk-lib` 的版本相同。该工具包旨在向后兼容。该工具包的最新 2.x 版本可与该库的任何 1.x 或 2.x 版本一起使用。为此，我们建议您全局安装此组件并使其保持最新版本。

```
npm update -g aws-cdk
```

如果您需要使用 AWS CDK 工具包的多个版本，请在项目文件夹中本地安装该工具包的特定版本。

如果您使用的是 TypeScript 或 JavaScript，那么您的项目目录中已经包含 CDK Toolkit 受版本控制的本地副本。

如果您使用的是其他语言，请使用 `npm` 安装 AWS CDK 工具包，省略 `-g` 标志并指定所需的版本。例如：

```
npm install aws-cdk@2.0
```

要运行本地安装的 AWS CDK 工具包，请使用命令 `npx aws-cdk` 而不是仅使用命令 `cdk`。例如：

```
npx aws-cdk deploy MyStack
```

 如果有 AWS CDK 工具包的本地版本，则 `npx aws-cdk` 会运行该版本。当某个项目没有进行本地安装时，其会回退到全局版本。您可能会发现设置一个 shell 别名以确保始终以这种方式调用 `cdk` 会很方便。

**Example**  

```
alias cdk="npx aws-cdk"
```

```
doskey cdk=npx aws-cdk $*
```

## 部署我的 AWS CDK 堆栈时，我收到一个 `NoSuchBucket` 错误
<a name="troubleshooting-nobucket"></a>

您的 AWS 环境尚未被引导，因此没有用于在部署期间存放资源的 Amazon S3 存储桶。您可以使用以下命令创建暂存存储桶和其他所需资源：

```
cdk bootstrap aws://ACCOUNT-NUMBER/REGION
```

为避免产生意外 AWS 费用，AWS CDK 不会自动引导任何环境。必须显式引导要部署到的每个环境。

默认情况下，在当前 AWS CDK 应用程序中堆栈使用的一个或多个区域中创建引导资源。或者，在您的本地 AWS 配置文件（由 `aws configure` 设置）指定的区域中使用该配置文件的账户创建这些资源。您可以在命令行上指定不同的账户和区域，如下所示。（如果您不在应用程序目录中，则必须指定账户和区域。）

```
cdk bootstrap aws://ACCOUNT-NUMBER/REGION
```

有关更多信息，请参阅 [AWS CDK 引导](bootstrapping.md)。

## 部署我的 AWS CDK 堆栈时，我收到一条 `forbidden: null` 消息
<a name="troubleshooting-forbidden-null"></a>

您部署的堆栈需要引导资源，但使用的 IAM 角色或账户缺少写入权限。（部署包含资产或合成大于 50 K AWS CloudFormation 模板的堆栈时，会使用暂存存储桶。） 使用有权对错误消息中提及的存储桶执行 `s3:*` 操作的账户或角色。

## 合成 AWS CDK 堆栈时，我收到消息 `--app is required either in command-line, in cdk.json or in ~/.cdk.json`
<a name="troubleshooting-app-required"></a>

此消息通常意味着当您发出 `cdk synth` 命令时，您不在 AWS CDK 项目的主目录中。此目录中的 `cdk.json` 文件由 `cdk init` 命令创建，包含运行（从而合成）AWS CDK 应用程序所需的命令行。例如，对于 TypeScript 应用程序，默认值 `cdk.json` 如下所示：

```
{
  "app": "npx ts-node bin/my-cdk-app.ts"
}
```

我们建议仅在项目的主目录中发出 `cdk` 命令，这样 AWS CDK 工具包就可以在那里找到 `cdk.json` 并成功运行您的应用程序。

如果由于某种原因这不可行，AWS CDK 工具包会在另外两个位置查找应用程序的命令行：
+ 主目录中的 `cdk.json` 中
+ 在 `cdk synth` 命令本身上，使用 `-a` 选项

例如，您可以按如下方式从 TypeScript 应用程序中合成堆栈。

```
cdk synth --app "npx ts-node my-cdk-app.ts" MyStack
```

## 合成 AWS CDK 堆栈时，由于 AWS CloudFormation 模板包含了太多资源，我收到错误
<a name="troubleshooting-resource-count"></a>

AWS CDK 生成并部署 AWS CloudFormation 模板。AWSCloudFormation 对堆栈可以包含的资源数量有硬性限制。使用 AWS CDK，您可以比预期更快地达到此限制。

**注意**  
在撰写本文时，AWS CloudFormation 资源限制为 500。有关当前资源的限制，请参阅 [AWS CloudFormation 配额](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html)。

AWS 构造库中更高级别、基于意图的构造会自动预置日志记录、密钥管理、授权和其他目的所需的任何辅助资源。例如，向一个资源授予对另一个资源的访问权限会生成相关服务进行通信所需的任何 IAM 对象。

根据我们的经验，实际中使用基于意图的构造会导致每个构造生成 1-5 个 AWS CloudFormation 资源（数据可能有所出入）。对于无服务器应用程序，通常每个 API 端点生成 5-8 个 AWS 资源。

模式代表更高级别的抽象，允许您使用更少的代码定义更多的 AWS 资源。例如，[示例：使用 AWS CDK 创建 AWS Fargate 服务](ecs-example.md)中的 AWS CDK 代码仅定义三个构造，并生成了 50 多个 AWS CloudFormation 资源！

超过 AWS CloudFormation 资源限制是 AWS CloudFormation 合成过程中出现的错误。如果您的堆栈超过限制的 80%，则 AWS CDK 会发出警告。您可以通过在堆栈上设置 `maxResources` 属性来使用不同的限制，也可以通过将 `maxResources` 设置为 0 来禁用验证。

**提示**  
您可以使用以下实用程序脚本获取合成输出中资源的精确计数。（由于每个 AWS CDK 开发人员都需要 Node.js，因此脚本是用 JavaScript 编写的。）  

```
// rescount.js - count the resources defined in a stack
// invoke with: node rescount.js <path-to-stack-json>
// e.g. node rescount.js cdk.out/MyStack.template.json

import * as fs from 'fs';
const path = process.argv[2];

if (path) fs.readFile(path, 'utf8', function(err, contents) {
  console.log(err ? `${err}` :
  `${Object.keys(JSON.parse(contents).Resources).length} resources defined in ${path}`);
}); else console.log("Please specify the path to the stack's output .json file");
```

当堆栈的资源计数接近限制时，可以考虑重新架构以减少堆栈包含的资源数量：例如，组合一些 Lambda 函数，或者将您的堆栈分成多个堆栈。CDK 支持[堆栈之间的引用](resources.md#resource-stack)，因此您可以用任何您认为最合理的方式将应用程序的功能分离到不同的堆栈中。

**注意**  
 AWS CloudFormation 专家经常建议使用嵌套堆栈作为资源限制的解决方案。AWS CDK 通过 [NestedStack](stacks.md#stack-nesting) 构造支持这种方法。

## 我为自动扩缩组或 VPC 指定了三个（或更多）可用区，但只部署在两个可用区中
<a name="troubleshooting-availability-zones"></a>

要获取您请求的可用区数量，请在堆栈的 `env` 属性中指定账户和区域。如果未同时指定两者，则 AWS CDK 会默认将堆栈合成为与环境无关的堆栈。然后，您可以使用 AWS CloudFormation 将堆栈部署到特定区域。由于某些区域只有两个可用区，因此与环境无关的模板不会使用两个以上的区域。

**注意**  
过去，区域偶尔会只启动一个可用区。无法将与环境无关的 AWS CDK 堆栈部署到此类区域。但是，在撰写本文时，所有 AWS 区域都至少有两个可用区。

您可以通过覆盖堆栈的 [availabilityZones](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stack.html#availabilityzones)（Python：`availability_zones`）属性来更改此行为，从而明确指定要使用的区域。

有关在合成时指定堆栈的账户和区域，同时保留部署到任何区域的灵活性的更多信息，请参阅 [Environments for the AWS CDK](environments.md)。

## 在我发出 `cdk destroy` 命令时，我的 S3 存储桶、DynamoDB 表或其他资源没有被删除
<a name="troubleshooting-resource-not-deleted"></a>

默认情况下，可以包含用户数据的资源具有 `RETAIN` 的 `removalPolicy`（Python：`removal_policy`）属性，并且在销毁堆栈不会删除该资源。相反，该资源从堆栈中孤立出来。然后，必须在销毁堆栈后手动删除该资源。除非手动删除该资源，否则重新部署堆栈将失败。这是因为部署期间创建的新资源的名称与孤立资源的名称冲突。

如果您将资源的删除策略设置为 `DESTROY`，则将在销毁堆栈时删除该资源。

**Example**  

```
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';

export class CdkTestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const bucket = new s3.Bucket(this, 'Bucket', {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });
  }
}
```

```
const cdk = require('aws-cdk-lib');
const s3 = require('aws-cdk-lib/aws-s3');

class CdkTestStack extends cdk.Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    const bucket = new s3.Bucket(this, 'Bucket', {
      removalPolicy: cdk.RemovalPolicy.DESTROY
    });
  }
}

module.exports = { CdkTestStack }
```

```
import aws_cdk as cdk
from constructs import Construct
import aws_cdk.aws_s3 as s3

class CdkTestStack(cdk.stack):
    def __init__(self, scope: Construct, id: str, **kwargs):
        super().__init__(scope, id, **kwargs)

        bucket = s3.Bucket(self, "Bucket",
            removal_policy=cdk.RemovalPolicy.DESTROY)
```

```
software.amazon.awscdk.*;
import software.amazon.awscdk.services.s3.*;
import software.constructs;

public class CdkTestStack extends Stack {
    public CdkTestStack(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public CdkTestStack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);

        Bucket.Builder.create(this, "Bucket")
                .removalPolicy(RemovalPolicy.DESTROY).build();
    }
}
```

```
using Amazon.CDK;
using Amazon.CDK.AWS.S3;

public CdkTestStack(Construct scope, string id, IStackProps props) : base(scope, id, props)
{
    new Bucket(this, "Bucket", new BucketProps {
        RemovalPolicy = RemovalPolicy.DESTROY
    });
}
```

**注意**  
 AWS CloudFormation 无法删除非空 Amazon S3 存储桶。如果您将 Amazon S3 存储桶的删除策略设置为 `DESTROY`，并且其中包含数据，则尝试销毁该堆栈将失败，因为无法删除该存储桶。您可以将存储桶的 `autoDeleteObjects` prop 设置为 `true`，从而让 AWS CDK 在尝试销毁存储桶之前删除其中的对象。