

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

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

# 重构 CDK 代码时保留已部署的资源
<a name="refactor"></a>

**重要**  
CDK 重构目前为预览版，可能会发生变化。

借助 C AWS loud Development Kit (AWS CDK) 重构，您可以重构您的 CDK 代码，例如重命名构造、在堆栈之间移动资源以及重组应用程序，同时保留已部署的资源而不是替换它们。此功能可帮助您保持良好的软件工程最佳实践，而不会导致意外的资源替换。

## 什么是具有资源保留的 CDK 重构？
<a name="refactor-intro"></a>

部署 CDK 应用程序时，会根据资源的逻辑 IDs来 AWS CloudFormation 识别资源。 AWS CDK IDs 根据构造 ID 及其在构造树中的路径生成这些逻辑。如果您更改构造的 ID 或将其移至代码中的其他位置， AWS CloudFormation 通常会将其解释为请求创建新资源并删除旧资源。对于数据库、存储桶或队列等有状态资源，这种替换可能会导致服务中断或数据丢失。

CDK 重构通过以下方式解决此问题：
+ 检测代码中资源何时被移动或重命名。
+ 使用 AWS CloudFormation的重构功能来保留底层的物理资源。
+ 在 IDs 不替换实际资源的情况下更新逻辑。
+ 维护堆栈中资源之间的引用。

您可以使用 CDK CLI `cdk refactor` 命令或 CDK 工具包库的 `refactor` 操作来执行 CDK 重构。本指南主要讨论 CLI 方法，但基本原则适用于这两种方法。有关使用工具包库的信息，请参阅[使用 CDK 工具包库执行编程操作](toolkit-library.md)。

**重要**  
重构操作必须单独执行，与其他操作（例如，添加新资源、删除资源或修改资源属性）分开执行。  
如果您需要添加、删除或修改资源以及重构，则应首先单独部署这些更改，然后使用重构来重组您的资源。

## CDK 重构的好处
<a name="refactor-benefits"></a>

CDK 重构为 CDK 开发者提供了以下好处 AWS ：
+  **改进代码组织**：重命名结构并重组 CDK 应用程序的结构，而无需替换资源。
+  **创建可重用组件**：将重复代码提取为可重用的 L3 构造，同时保留已部署的资源。
+  **增强架构分离**：在堆栈之间移动资源，以更好地隔离应用程序的不同部分。
+  **防止意外替换资源**：避免在重命名构造时意外重新创建资源。
+  **缓解第三方库更改**：保护您的应用程序，以免您所依赖的构造库中的逻辑 ID 发生更改。
+  **应用软件工程最佳实践**：在不影响已部署基础设施的情况下重构代码。

## CDK CLI `cdk refactor` 命令的工作原理
<a name="refactor-how"></a>

**重要**  
您必须对使用此功能的所有命令提供 `--unstable=refactor` 选项。

首先，部署初始 CDK 应用程序以在 AWS 账户中建立基准资源。重构 CDK 代码（例如，重命名构造或在堆栈之间移动资源）后，使用 `cdk refactor` 命令开始重构已部署资源的过程。

当您运行重构命令时，CDK CLI 会将当前代码与已部署状态进行比较，从而检测本地更改。它验证您的 CDK 应用程序是否包含与已部署状态完全相同的资源集，只是它们在构造树中的位置不同。然后，CDK CLI 会生成一个重构计划来将旧资源位置映射到新位置。CDK CLI 会向您显示建议的更改，并在您确认后，使用 AWS CloudFormation其重构 API 更新资源的逻辑 IDs 而无需替换它们。

在后台，CDK CLI 通过比较资源的属性和依赖项来确定哪些资源已移动，识别功能等效但在构造树中的路径不同的资源。如果检测到任何资源添加、删除或修改，则重构操作将被拒绝并显示一条错误消息。

## 重构 CDK 代码时保留资源的示例
<a name="refactor-example"></a>

在此示例中，我们使用 CDK CLI `cdk refactor` 命令重构 CDK 代码，同时保留已部署的资源。

我们的示例 CDK 应用程序由包含 S3 存储桶、 CloudFront 分配和 Lambda 函数的单个堆栈组成。构造树的结构如下：

```
App
└─ MyStack
   ├─ Bucket
   ├─ Distribution
   └─ Function
```

下面是我们的应用程序代码示例：

```
const app = new cdk.App();
const myStack = new cdk.Stack(app, 'MyStack');

const bucket = new s3.Bucket(myStack, 'Bucket');
const distribution = new cloudfront.Distribution(myStack, 'Distribution', {
  defaultBehavior: { origin: new origins.S3Origin(bucket) }
});
const function = new lambda.Function(myStack, 'Function', {
  // function properties
});

// Synthesize the app
app.synth();
```

现在，假设您想重构此代码来实现以下目的：

1. 将存储桶从 `Bucket` 重命名为更具描述性的 `WebsiteOrigin`。

1. 将存储桶和分配移动到新的 `WebStack` 堆栈。

重构后，构造树将如下所示：

```
App
├─ WebStack
│  ├─ WebsiteOrigin
│  └─ Distribution
└─ MyStack
   └─ Function
```

重构后的代码如下：

```
// Refactored structure
const app = new cdk.App();

// New WebStack with the bucket and distribution
const webStack = new cdk.Stack(app, 'WebStack');
const bucket = new s3.Bucket(webStack, 'WebsiteOrigin');
const distribution = new cloudfront.Distribution(webStack, 'Distribution', {
  defaultBehavior: { origin: new origins.S3Origin(bucket) }
});

// Original MyStack with just the function
const myStack = new cdk.Stack(app, 'MyStack');
const function = new lambda.Function(myStack, 'Function', {
  // function properties
});

// Synthesize the app
app.synth();
```

如果没有 CDK 重构，这些更改将 AWS CloudFormation 导致创建新资源并删除旧资源，因为逻辑 IDs 会发生变化：
+  `MyStack/Bucket/Resource` 会变为 `WebStack/WebsiteOrigin/Resource`。
+  `MyStack/Distribution/Resource` 会变为 `WebStack/Distribution/Resource`。

通过 CDK 重构，CDK CLI 可以检测到这些路径变化，并使用其重构功能 AWS CloudFormation来保留底层资源。运行 `cdk refactor` 后，CLI 会显示它将进行的更改：

```
$ cdk refactor

The following resources were moved or renamed:

┌───────────────────────────────┬───────────────────────────────┬───────────────────────────────────┐
│ Resource Type                 │ Old Construct Path            │ New Construct Path                │
├───────────────────────────────┼───────────────────────────────┼───────────────────────────────────┤
│ AWS::S3::Bucket               │ MyStack/Bucket/Resource       │ WebStack/WebsiteOrigin/Resource   │
├───────────────────────────────┼───────────────────────────────┼───────────────────────────────────┤
│ AWS::CloudFront::Distribution │ MyStack/Distribution/Resource │ WebStack/Distribution/Resource    │
└───────────────────────────────┴───────────────────────────────┴───────────────────────────────────┘

Do you wish refactor these resources (y/n)?
```

输入 `y` 确认后，CDK CLI 会显示重构操作的进度：

```
Refactoring...
✅  Stack refactor complete
```

确认后，CDK CLI 将运行重构操作，保留这两个资源，同时更新其逻辑 IDs 以匹配您的新代码结构。

`cdk diff` 命令的输出也会显示相同映射，按堆栈进行组织：

```
Stack MyStack
Resources
[-] AWS::S3::Bucket Bucket Bucket1234567 destroy (OR move to WebStack.WebsiteOrigin1234567 via refactoring)
[-] AWS::CloudFront::Distribution Distribution Distribution1234567 destroy (OR move to WebStack.Distribution1234567)
...

Stack WebStack
Resources
[+] AWS::S3::Bucket WebsiteOrigin WebsiteOrigin1234567 (OR move from MyStack.Bucket1234567)
[+] AWS::CloudFront::Distribution Distribution Distribution1234567 (OR move from MyStack.Distribution1234567)
...
```

## 开始使用 CDK 重构
<a name="_get_started_with_cdk_refactoring"></a>

要开始重构，请完成下面的先决条件：

 **使用最新模板引导环境**   
CDK 重构在引导堆栈中需要新权限。为确保您拥有必要的权限，请使用最新模板引导您的环境：  

```
cdk bootstrap
```
有关引导的更多信息，请参阅 [AWS CDK 的引导环境](bootstrapping-env.md)。

 **安装最新的 CDK CLI 版本**   
CDK 重构需要最新版本的 CDK CLI。为了确保您拥有最新版本，请运行以下命令：  

```
npm install -g aws-cdk
```
有关详细的安装说明，请参阅[开始使用 AWS CDK](getting-started.md)。

## 使用覆盖文件来解决重构中的歧义
<a name="override-file"></a>

CDK CLI 会根据您的代码与已部署资源的比较结果自动计算所有资源映射。大多数情况下，这种自动检测效果很好，但有时 CLI 可能会遇到它自己无法解决的歧义。要为 CDK CLI 提供指导，请使用覆盖文件。

 **创建覆盖文件以解决歧义**   
覆盖文件是一个 JSON 文件，用于在 CDK CLI 无法确定资源的重构解析时提供映射。该文件包含按环境组织的资源映射：  

```
{
    "environments": [
        {
            "account": "123456789012",
            "region": "us-east-2",
            "resources": {
                "StackA.OldName": "StackB.NewName",
                "StackC.Foo": "StackC.Bar"
            }
        }
    ]
}
```
在此文件中：  
+ `environments` 数组包含一个或多个带有账户和区域信息的环境条目。
+ 在每个环境内，`resources` 对象都包含映射。
+ 键表示当前位置，格式为 `<stack name>.<logical ID>`。
+ 值表示新位置，格式相同。
要将覆盖文件与 CDK CLI 一起使用，请运行以下命令：  

```
cdk refactor --override-file=overrides.json
```

## 跨多个环境重构堆栈
<a name="refactor-environments"></a>

一个 CDK 应用程序可以包含部署到不同环境（AWS 账户和区域）的多个堆栈。在重构此类应用程序期间，为了保留资源，CDK CLI 会以特定方式处理环境：
+ CLI 会按环境对堆栈进行分组，并在每个环境中分别执行重构。
+ 重构期间，您可以在堆栈之间移动资源，但所有参与移动的堆栈必须位于同一环境中。
+ 尝试跨环境移动资源将导致错误。

这种行为可确保资源保留在其原始 AWS 账户和区域内，这是必要的，因为 CloudFormation 资源无法在账户或区域边界之间实际移动。

例如，如果您的 CDK 应用程序针对开发环境和生产环境都定义了堆栈，则重构操作将在每个环境中独立执行。资源可以在开发环境或生产环境内的堆栈之间移动，但不能从开发环境移动到生产环境，反之亦然。

## 处理需要替换的资源
<a name="refactor-replaceable-resources"></a>

某些 CDK 构造依赖于 CloudFormation的资源替换行为作为其设计的一部分。例如，API Gateway 的 `Deployment` 和 Lambda 的 `Version` 构造都旨在当资源属性发生变化时创建新资源。

重构时，请勿进行任何会导致资源替换的更改。否则，CDK CLI 可能会检测到并保留这些资源。这意味着需要替换的资源必须与重构操作分开处理。

要正确管理需要替换的资源，请执行以下操作：

1. 首先，部署您的应用程序，根据需要替换这些资源。

1. 然后，分别执行重构操作来重新组织代码。

这种两步法确保需要替换的资源得到正确处理，同时还能让您从其他资源的 CDK 重构中受益。

## 一般注意事项和限制
<a name="refactor-considerations"></a>

在 CDK 重构期间保留资源时，请记住以下注意事项：
+  **环境约束**：资源只能在同一环境内的不同堆栈之间移动。不支持跨环境移动。
+  **歧义**：如果您同时重命名多个相同的资源，CDK CLI 可能无法自动确定正确的映射。在这些情况下，您需要使用覆盖文件提供显式映射。
+  **引导要求**：要在重构期间保留资源，您需要将引导堆栈更新到包含必要权限的最新版本。
+  **排除某些构造**：某些构造（例如 API Gateway 的 `Deployment` 和 Lambda 的 `Version`）依赖于资源替换，因此会自动从重构中排除。

## 使用管道进行重构 CI/CD
<a name="refactor-pipelines"></a>

要在 CI/CD 管道中使用重构功能，您需要能够将 CDK CLI 作为管道的一部分运行。以下是将重构集成到工作流程中的 CI/CD 一些重要注意事项。

 **在 CI/CD 中使用重构的先决条件**   
您必须能够在您的 CI/CD 环境中使用 CDK CLI 才能从此功能中受益。

 **将重构集成到管线工作流中**   
如果您在 CI/CD 管道中使用 CLI 进行部署，则脚本通常如下所示：  

```
...
cdk deploy <stack filter>
...
```
如果要将重构纳入工作流，下面是一个基本示例：  

```
...
cdk refactor <stack filter>
cdk deploy <stack filter>
...
```
您也可以将重构作为管线中的一个单独步骤。

 **处理重构失败**   
请注意，如果您的代码在重构的同时还包含实际的资源修改，则 `cdk refactor` 将会失败。由于您在管线中自动调用了重构，因此需要处理潜在失败：  

```
# Allow refactoring to fail but continue the pipeline
cdk refactor <stack filter> || true
cdk deploy <stack filter>
```
或者，您可能需要确保在成功完成重构之前不要进行任何部署：  

```
# Only deploy if refactoring succeeds
cdk refactor <stack filter> && cdk deploy <stack filter>
```

 ** CI/CD 环境最佳实践**   
要在管道中 CI/CD 有效地使用重构，请执行以下操作：  
+  **将重构与其他变更分开**：请记住，重构操作必须与资源的添加、删除或修改分开。在您的管线中，请考虑为重构采用专用的提交和部署。
+  **适当使用覆盖文件**：请注意，CDK CLI 仅将覆盖文件用作解决歧义情况的备用方案。
+  **无需`--force`在管道中使用：在管道**等 CI/CD 非交互式环境中，CDK 重构命令会自动执行而不提示确认。`--force` 选项仅在交互式环境中需要。

## 相关资源
<a name="refactor-resources"></a>

有关 CDK CLI `cdk refactor` 命令的选项和参数的信息，请参阅 ` cdk refactor `。

要开始使用 CDK 工具包库的 `refactor` 操作，请参阅[使用 CDK 工具包库执行编程操作](toolkit-library.md)。