

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

# 堆栈删除后恢复 DLT 数据
<a name="recover-data-after-stack-deletion"></a>

本节介绍如何在删除原始堆栈后将测试场景和运行历史恢复到新的 DLT 堆栈中。当客户的 S3 存储桶和 DynamoDB 表已保留（默认行为）并且客户希望将它们重新连接到新的部署时，它适用。

## 背景
<a name="recovery-background"></a>

删除 DLT 堆栈后，由于数据保护政策，以下资源将保留在客户的 AWS 账户中：


| 资源 | 包含 | 命名 | 
| --- | --- | --- | 
| 场景 S3 存储桶 | 测试脚本 (JMeter/K6/Locust)、每个任务的结果 XML 文件、JMeter 框架资产 | Auto-generated （例如，`dltstack-scenariosbucket-abc123`） | 
| 日志 S3 存储桶 | 所有 DLT 存储桶的 S3 访问日志 | Auto-generated | 
| 控制台 S3 存储桶 | Web 用户界面静态资产 | Auto-generated | 
| 场景 DynamoDB 表 | 测试定义、配置、调度规则 | Auto-generated （例如，`DLTStack-ScenariosTable-xyz789`） | 
| 历史记录 DynamoDB 表 | 测试运行结果、持续时间、成功率 | Auto-generated （例如，`DLTStack-HistoryTable-xyz789`） | 
| CloudWatch 日志组 | Lambda 和 ECS 执行日志（保留期为 10 年） | 按函数命名 | 

所有资源名称均由自动生成。 CloudFormation它们不可预测，也无法在部署新堆栈时指定为参数。

## 重要限制
<a name="recovery-important-constraints"></a>

1. DLT CloudFormation 模板不接受现有 DynamoDB 表或 S3 存储桶的参数。新的部署总是会创造新的资源。

1. 直接修改堆栈外的 CloudFormation-managed 资源会导致堆栈偏移。所有数据迁移都必须在数据级别进行，而不是在基础架构级别进行。

1. 解决方案 UUID 是在每次新部署时随机生成的。新堆栈的 UUID 将与原始堆栈不同。

1. 默认情况下，DynamoDB 表启用 Point-in-Time 了恢复 (PITR)，提供过去 35 天的连续备份。

## 恢复过程
<a name="recovery-process"></a>

### 步骤 1：确定保留的资源
<a name="recovery-step-1-identify-retained-resources"></a>

在部署新堆栈之前，请从已删除的堆栈中找到保留的资源。

```
# List retained DynamoDB tables (look for DLT naming patterns)
aws dynamodb list-tables --query "TableNames[?contains(@, 'ScenariosTable') || contains(@, 'HistoryTable')]"

# List retained S3 buckets (look for DLT naming patterns)
aws s3api list-buckets --query "Buckets[?contains(Name, 'dlttestrunnerstoragedlts') || contains(Name, 'dltconsoleresourcesdltda')]"
```

如果您在删除之前记下了 CloudFormation 堆栈输出，请使用这些确切的名称。相关产出是：
+  `ScenariosBucket`— S3 存储桶名称
+  `ScenariosTable`— DynamoDB 场景表名称

对于未作为堆栈输出公开的资源（例如历史表），`list-stack-resources`请在删除之前使用来获取物理资源 ID。此命令列出堆栈中的所有资源，无论其是否有相应的输出。

### 步骤 2：部署新的 DLT 堆栈
<a name="recovery-step-2-deploy-new-stack"></a>

使用相同的 CloudFormation 模板版本（或更高版本）部署全新 DLT 堆栈。使用与原始部署相同的参数（VPC 设置、管理员电子邮件等）。

```
aws cloudformation create-stack \
  --stack-name distributed-load-testing-new \
  --template-url https://s3.amazonaws.com/solutions-reference/distributed-load-testing-on-aws/latest/distributed-load-testing-on-aws.template \
  --parameters ParameterKey=AdminName,ParameterValue=<admin-name> \
               ParameterKey=AdminEmail,ParameterValue=<admin-email> \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND
```

等待堆栈达到`CREATE_COMPLETE`状态。

### 步骤 3：确定新堆栈的资源
<a name="recovery-step-3-identify-new-stack-resources"></a>

从新堆栈的输出中检索资源名称。

```
aws cloudformation describe-stacks \
  --stack-name distributed-load-testing-new \
  --query "Stacks[0].Outputs"
```

请注意以下各项的值：
+  `ScenariosBucket`（新的存储桶名称）
+  `ScenariosTable`（新表名）

对于任何未作为堆栈输出公开的资源（例如历史表），请使用`list-stack-resources`其逻辑 ID 进行查找：

```
aws cloudformation list-stack-resources \
  --stack-name distributed-load-testing-new \
  --query "StackResourceSummaries[?contains(LogicalResourceId, 'HistoryTable')].{LogicalId:LogicalResourceId,PhysicalId:PhysicalResourceId}"
```

此命令列出了堆栈管理的所有资源。您可以按逻辑 ID 子字符串筛选以查找任何资源。

### 步骤 4：迁移 DynamoDB 数据
<a name="recovery-step-4-migrate-dynamodb"></a>

从保留的表中导出数据并将其导入到新表中。使用 DynamoDB 数据导出或扫描和写入操作。

 **选项 A：使用 Dynamo Export/Import DB（建议用于大型数据集）** 

对于包含数千个项目的表，使用 DynamoDB 导出到 S3，然后再进行导入：

```
# Export from retained table to S3
aws dynamodb export-table-to-point-in-time \
  --table-arn arn:aws:dynamodb:<region>:<account>:table/<old-scenarios-table> \
  --s3-bucket <temporary-export-bucket> \
  --s3-prefix dlt-migration/scenarios/ \
  --export-format DYNAMODB_JSON

# Wait for export to complete, then import into new table
aws dynamodb import-table \
  --s3-bucket-source S3Bucket=<temporary-export-bucket>,S3KeyPrefix=dlt-migration/scenarios/ \
  --table-creation-parameters '{"TableName":"<new-scenarios-table>","KeySchema":[{"AttributeName":"testId","KeyType":"HASH"}],"AttributeDefinitions":[{"AttributeName":"testId","AttributeType":"S"}],"BillingMode":"PAY_PER_REQUEST"}' \
  --input-format DYNAMODB_JSON
```

**注意**  
`import-table`API 会创建一个新表。由于新堆栈已经创建了目标表，因此请改用选项 B，或者先删除新的空表，然后通过导入重新创建它（这将导致堆栈偏移，请参阅下面的警告）。

 **选项 B：扫描和 BatchWrite （推荐）** 

这种方法直接从保留的表中读取并写入新堆栈的表，而不会导致堆栈偏移。它需要 Python 3 和`boto3`库。

```
# Install boto3 if not already available
pip install boto3
```

```
#!/usr/bin/env python3
"""Migrate DynamoDB items directly from retained tables to new stack tables."""

import boto3

dynamodb = boto3.resource("dynamodb", region_name="<region>")

def migrate_table(source_table_name, target_table_name):
    source = dynamodb.Table(source_table_name)
    target = dynamodb.Table(target_table_name)

    # Scan all items from the retained table (handles pagination automatically)
    items = []
    response = source.scan()
    items.extend(response["Items"])
    while "LastEvaluatedKey" in response:
        response = source.scan(ExclusiveStartKey=response["LastEvaluatedKey"])
        items.extend(response["Items"])

    print(f"Scanned {len(items)} items from {source_table_name}")

    # Write items to the new table using batch_writer (handles batching automatically)
    with target.batch_writer() as batch:
        for item in items:
            batch.put_item(Item=item)

    print(f"Wrote {len(items)} items to {target_table_name}")

# Migrate scenarios
migrate_table("<old-scenarios-table>", "<new-scenarios-table>")

# Migrate history
migrate_table("<old-history-table>", "<new-history-table>")
```

对于大型表（成千上万个项目），可以考虑使用并行扫描段来加快读取阶段。

 **选项 C：从 PITR 恢复（如果在 35 天的时间范围内）** 

如果在过去 35 天内删除了堆栈，并且启用了 PITR（默认情况下），则可以将表恢复到某个时间点。但是，PITR 还原会创建一个名称不同的新表，因此您仍然需要使用选项 B 将数据复制到堆栈管理的表中。

```
# Restore to a new temporary table
aws dynamodb restore-table-to-point-in-time \
  --source-table-name <old-scenarios-table> \
  --target-table-name dlt-scenarios-restored \
  --restore-date-time <timestamp-before-deletion>

# Then scan from dlt-scenarios-restored and batch-write into the new stack's table
```

### 步骤 5：迁移 S3 数据
<a name="recovery-step-5-migrate-s3"></a>

将测试脚本和结果文件从保留的存储桶复制到新堆栈的存储桶。

```
# Copy all objects from the old scenarios bucket to the new one
aws s3 sync \
  s3://<old-scenarios-bucket> \
  s3://<new-scenarios-bucket> \
  --source-region <region> \
  --region <region>
```

这复制了：
+ 测试脚本（JMeter `.jmx` 文件、K6 脚本、Locust 文件、ZIP 存档）
+ 按测试 ID 和区域组织的测试结果 XML 文件
+ JMeter 框架资产和插件

### 步骤 6：验证迁移
<a name="recovery-step-6-verify"></a>

1. 使用新堆栈的 URL（可在堆`WebConsole`栈输出中找到）登录 DLT Web 控制台。

1. 确认所有测试场景都显示在场景列表中。

1. 打开各个场景并验证测试运行历史记录是否可见。

1. 确认可以从场景详细信息页面下载测试脚本。

1. 运行一个小型测试，验证新堆栈的端到端功能。

### 步骤 7：清理保留的资源
<a name="recovery-step-7-clean-up"></a>

确认所有数据均已成功迁移后，删除保留的旧资源以避免持续的存储成本。

```
# Delete old DynamoDB tables
aws dynamodb delete-table --table-name <old-scenarios-table>
aws dynamodb delete-table --table-name <old-history-table>

# Empty and delete old S3 buckets
aws s3 rm s3://<old-scenarios-bucket> --recursive
aws s3api delete-bucket --bucket <old-scenarios-bucket>

aws s3 rm s3://<old-logs-bucket> --recursive
aws s3api delete-bucket --bucket <old-logs-bucket>

aws s3 rm s3://<old-console-bucket> --recursive
aws s3api delete-bucket --bucket <old-console-bucket>
```

只有在确认新堆栈已完全运行且所有历史数据完好无损后才执行此步骤。

## 避免堆栈漂移
<a name="recovery-avoiding-stack-drift"></a>

当资源的实际状态与 CloudFormation 预期的状态不同时，就会发生堆栈偏移。为避免在此恢复过程中出现偏差，请执行以下操作：
+ 请勿直接重命名或修改新堆栈的 DynamoDB 表或 S3 存储桶。
+ 请勿使用`import-table`来替换新堆栈的表（这需要先删除 CloudFormation-managed 表）。
+ 请勿在之外修改 IAM 策略、存储桶策略或表设置 CloudFormation。
+ 请仅使用数据级操作：`PutItem`、、`BatchWriteItem``s3 sync`、`s3 cp`。
+ 请务必将数据写入 CloudFormation 创建的表和存储桶。向 DynamoDB 表添加项目或向 S3 存储桶添加对象并不构成偏差。

将数据写入 CloudFormation-managed 表和存储桶是安全的，因为会 CloudFormation 跟踪资源配置（表架构、计费模式、加密设置），而不是数据内容。

## 无法恢复的东西
<a name="recovery-what-cannot-be-recovered"></a>

删除堆栈后，以下内容将丢失，且无法通过此过程恢复：


| Item | Reason | 
| --- | --- | 
| Cognito 用户帐户 | 用户池随堆栈一起删除。用户必须重新注册。 | 
| 活动 EventBridge 日程安排 | 计划测试规则已删除。在 DLT 用户界面中手动重新创建计划。 | 
| CloudWatch 仪表板 | Per-test 仪表板 (`EcsLoadTesting*`) 已删除。新的运行将创建新的仪表板。 | 
| 解决方案 UUID | 将生成一个新的随机 UUID。这仅影响运营指标的相关性。 | 
| 区域堆栈协会 | 必须重新部署区域堆栈并将其与新的中心堆栈重新关联。 | 

## 预防措施
<a name="recovery-preventive-measures"></a>

要简化 future 恢复方案，请执行以下操作：

1. 在任何删除之前记录堆栈输出。保存`ScenariosBucket``ScenariosTable`、和历史表的名称。

1. 启用 DynamoDB PITR（在 DLT 中默认启用）。验证它是否保持活动状态。

1. 启用 S3 版本控制（默认情况下在场景存储桶上启用）。这样可以防止意外删除对象。

1. 考虑在场景和历史表上启用 DynamoDB 删除保护，以此作为额外的保护措施：

   ```
   aws dynamodb update-table \
     --table-name <scenarios-table> \
     --deletion-protection-enabled
   ```

1. 使用 AWS Backup 创建 DynamoDB 表的定时备份，以便在 35 天 PITR 窗口之后长期保留。