我们很高兴宣布一项新的AWS Cloud Development Kit(CDK)功能,该功能使重构基础设施即代码变得更简单、更安全。CDK重构旨在在您重命名构造、在堆栈间移动资源以及重组CDK应用程序时保留您的AWS资源——这些操作以前存在资源替换风险。
在使用CDK编写基础设施即代码时,开发人员偶尔需要重命名构造或在堆栈或目录之间移动它们。无论他们是需要更好地组织代码、遵循编码最佳实践,还是利用类继承等面向对象编程模式,这些更改在已部署资源的环境中可能存在风险,因为它们会更改CDK生成的这些资源的逻辑ID。在CDK部署期间,AWS CloudFormation会将这些更改解释为新资源,这通常需要删除现有资源并使用新的逻辑ID创建新资源。对于有状态资源,这可能导致潜在的停机甚至数据丢失。为了减轻ID更改的这种影响,开发人员必须分阶段进行更改以创建新资源,制定数据或网络迁移计划,然后删除旧资源以防止这些重构影响。有时,开发人员认为这些更改的风险超过了重构的好处,并选择根本不执行重构。
如今,开发人员可以使用新的cdk refactor
命令来检测、审查、确认和安全地将重构的更改应用到他们的资源,而无需替换资源。此功能利用了最近发布的AWS CloudFormation重构功能,但CDK会自动计算CloudFormation重新定义重构资源所需的映射,提供一层抽象,使开发人员能够专注于代码而不是资源配置。让我们通过一个示例来演示此重构功能的好处。
先决条件
除了通常的CDK先决条件外,如果您在此发布之前引导了CDK项目,则需要在尝试重构之前重新引导环境以获得与CDK重构功能相关的新权限。
单体到微服务示例
对于此示例,假设我们有一个传统的CDK应用程序,它部署一个单体堆栈,其中包含用于用户、产品和订单的Amazon DynamoDB表,以及一个在所有实体上实现CRUD操作的AWS Lambda函数。
单体应用程序
function monolithApp() {const monolith = new CdkAppStack(app, monolithStackName, {env});const usersTable = makeTable(monolith, 'users');const productsTable = makeTable(monolith, 'products');const ordersTable = makeTable(monolith, 'orders');// 我们的应用程序中有一个单一的Lambda函数const func = new Function(monolith, `MonolithFunction`, {code: Code.fromInline(`访问所有三个表的一些代码`),runtime: Runtime.NODEJS_22_X,handler: 'index.handler',});usersTable.grantReadWriteData(func);productsTable.grantReadWriteData(func);ordersTable.grantReadWriteData(func);// 此函数创建REST API、资源、方法,并将所有内容链接到函数。// 现在,我们在三个地方传递同一个函数。makeApi(monolith, {usersFunction: func,productsFunction: func,ordersFunction: func,});
}monolithApp();
我们被要求遵循Well Architected Framework最佳实践,并将单体分解为单独的Lambda函数,以便它们可以独立扩展。因为它们非常相似,我们还将创建一个可继承的Lambda类,我们可以重用它来提高代码的可读性和可维护性,并避免重新定义在所有函数中一致的Lambda配置设置。
最后,单体仅使用L1 CDK构造。为了进一步抽象我们的代码并利用辅助函数,我们将开始为DynamoDB、Lambda和API Gateway使用L2 CDK构造。此更改将允许自动定义IAM角色和权限,进一步简化我们的代码。
重构后的应用程序(提议的)
将应用程序重构为每个域的独立堆栈。
如果没有重构功能,CloudFormation将删除并重新创建Lambda和DynamoDB资源,这将导致后者中的所有数据丢失。或者,您可以在一次部署中创建全新的Lambda和Amazon DynamoDB表,执行从旧表到新表的带外、时间点和流式数据迁移,在第二次部署中更新API Gateway配置以指向新的Lambda,并关闭流式迁移过程。
使用重构功能,我们可以将资源定义移动到新文件,将它们更新为L2构造,并将有状态资源保留在原处!
首先替换无状态资源
首先,让我们重构CDK代码,将单体Lambda分解为3个特定领域的Lambda。CloudFormation的重构功能不支持创建新资源或更新现有资源的配置,因此我们将照常部署这些更改,而不使用新的重构功能。目前所有资源将保留在单体堆栈内。
将无状态单一Lambda函数重构为3个函数,作为有状态DynamoDB表重构的先决条件。
function singleStackMicroservicesApp() {// 我们仍然有一个单一的堆栈const monolith = new CdkAppStack(app, monolithStackName, {env});// makeFunctionAndTable为每个作为参数传递的域创建一个不同的Lambda函数和一个DynamoDB表。// 在真实的CDK应用程序中,您可能会独立定义它们中的每一个。makeApi(monolith, {usersFunction: makeFunctionAndTable(monolith, 'users'),productsFunction: makeFunctionAndTable(monolith, 'products'),ordersFunction: makeFunctionAndTable(monolith, 'orders'),});
}singleStackMicroservicesApp();
重构有状态资源
现在我们可以将有状态的DynamoDB表及其各自的Lambda重构到它们自己的堆栈中,使用cdk refactor
来映射它们的新ID,而无需替换资源。
但在重构之前,我们需要创建将接收函数和表的新堆栈:
singleStackMicroservicesApp();const usersStack = new Stack(app, 'Users', {env});const productsStack = new Stack(app, 'Products', {env});const ordersStack = new Stack(app, 'Orders', {env});
将Lambda函数和DynamoDB表重构到它们自己独立的堆栈中。
function fullMicroservicesApp() {const monolith = new Stack(app, monolithStackName, {env});const usersStack = new Stack(app, 'Users', {env});const productsStack = new Stack(app, 'Products', {env});const ordersStack = new Stack(app, 'Orders', {env});makeApi(monolith, {// 现在每对函数+表都在自己的堆栈中usersFunction: makeFunctionAndTable(usersStack, 'users'),productsFunction: makeFunctionAndTable(productsStack, 'products'),ordersFunction: makeFunctionAndTable(ordersStack, 'orders'),});
}fullMicroservicesApp();
运行cdk refactor --unstable=refactor
启动过程。(需要unstable
标志,因为此功能仍可能发生破坏性更改。)CDK将比较应用程序的当前状态(已部署的单体应用程序)与新状态(重构后的CDK应用程序的输出)。
CDK重构确认对话框
正如预期的那样,它显示了一个表格,列出了从Monolith堆栈移动到各自重构堆栈的资源。默认情况下,CLI会在继续之前请求确认。通过传递--force
标志绕过确认,或者确认更改并执行重构:所有资源,包括有状态表,都已安全地移动到其他堆栈,我们现在拥有了架构良好的应用程序。
CDK重构结果
结论
如果您有一直等待执行的重构,请在CDK重构文档中阅读有关功能集的更多信息,并立即开始重构您自己的CDK应用程序!
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)
公众号二维码
公众号二维码