AWS CDK 应用程序的可重构性如何?
How refactorable are AWS CDK applications?
我正在探索 CDK 应用程序的可重构性。假设我定义了一个自定义构造(堆栈)来创建 EKS 集群。我们称它为 EksStack
。理想情况下,我会创建与集群和 EKS 集群本身相关联的角色,如以下代码段所述(我使用 Scala 而不是 Java,因此这些代码段将采用 Scala 语法):
class EksStack (scope: Construct, id: String, props: StackProps) extends Stack(scope, id, props) {
private val role = new Role(this, "eks-role", RoleProps.builder()
.description(...)
.managedPolicies(...)
.assumedBy(...)
.build()
)
private val cluster = new Cluster(this, "eks-cluster", ClusterProps.builder()
.version(...)
.role(role)
.defaultCapacityType(DefaultCapacityType.EC2)
.build()
)
}
当我合成应用程序时,我可以看到生成的模板包含 VPC 的定义,以及弹性 IP、NAT、互联网网关等。
现在假设我想重构 EksStack
并有一个不同的堆栈,比如 VpcStack
,显式创建 VPC:
class VpcStack (scope: Construct, id: String, props: StackProps) extends Stack(scope, id, props) {
val vpc = new Vpc(this, VpcId, VpcProps.builder()
.cidr(...)
.enableDnsSupport(true)
.enableDnsHostnames(true)
.maxAzs(...)
.build()
)
}
理想情况下,EksStack
中的集群将只使用对 VpcStack
创建的 VPC 的引用,类似于(注意集群构建器中对 vpc()
的新调用):
class EksStack (scope: Construct, id: String, props: StackProps, vpc: IVpc) extends Stack(scope, id, props) {
private val role = new Role(this, "eks-role", RoleProps.builder()
.description(...)
.managedPolicies(...)
.assumedBy(...)
.build()
)
private val cluster = new Cluster(this, "eks-cluster", ClusterProps.builder()
.version(...)
.role(role)
.vpc(vpc)
.defaultCapacityType(DefaultCapacityType.EC2)
.build()
)
}
这显然行不通,因为 CloudFormation 会删除 EksStack
创建的 VPC 以支持 VpcStack
创建的 VPC。我在这里和那里阅读并尝试在 EksStack
中添加保留策略并覆盖 VpcStack
中 VPC 的逻辑 ID,使用我最初在 EksStack
的 CloudFormation 模板中看到的 ID :
val cfnVpc = cluster.getVpc.getNode.getDefaultChild.asInstanceOf[CfnVPC]
cfnVpc.applyRemovalPolicy(RemovalPolicy.RETAIN)
和
val cfnVpc = vpc.getNode.getDefaultChild.asInstanceOf[CfnVPC]
cfnVpc.overrideLogicalId("LogicalID")
然后重试 diff
。同样,VPC 似乎被删除并重新创建。
现在,我看到可以使用“将资源导入堆栈”操作迁移 CloudFormation 资源 (https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/refactor-stacks.html)。我的问题是:我可以在 CDK 中将资源的创建从一个堆栈移动到另一个堆栈而不重新创建它吗?
编辑:
详细说明我的问题,当我在 VpcStack
中定义 VPC 时,我希望 CDK 认为 资源是由 VpcStack
创建的,而不是 ok EksStack
。像 将它的定义从一个堆栈移动到另一个堆栈,而无需 CloudFormation 删除原始定义以重新创建它。在我的用例中,我有一个堆栈最初定义一个创建(显式或隐式,例如我的 VPC),但之后,我可能想重构我的应用程序,将该资源的创建移动到专用堆栈。我试图了解此 移动 是否总是导致资源被重新创建,是否有任何方法可以避免它。
我不确定我是否理解这个问题,但如果您尝试引用现有资源,您可以使用上下文查询。 (例如 Vpc.fromLookup
)。
https://docs.aws.amazon.com/cdk/latest/guide/context.html
此外,如果您想在 EksStack 中使用从 VpcStack 创建的 Vpc,您可以从 VpcStack 输出 vpc id 并以这种方式在 eks 堆栈中使用上下文查询。
这是 C# 代码,但原理是一样的。
var myVpc = new Vpc(...);
new CfnOutput(this, "MyVpcIdOutput", new CfnOutputProps()
{
ExportName = "VpcIdOutput",
Value = myVpc.VpcId
}
然后当您创建 EksStack 时,您可以导入您之前导出的 vpc id。
new EksStack(this, "MyCoolStack", new EksStackProps()
{
MyVpcId = Fn.ImportValue("VpcIdOutput")
}
EksStackProps 在哪里
public class EksStackProps
{
public string MyVpcId { get; set; }
}
我认为当涉及到 CDK 和 CloudFormation 的可重构性时,尤其是多堆栈配置时,需要牢记一些原则。
整个应用程序应该可以完全删除并重新创建。所有数据管理都在应用程序中处理,无需手动处理。
不要总是依赖使用堆栈导出的自动堆栈间依赖管理。我喜欢将 CloudFormation 依赖项分为两类:硬和软。硬依赖意味着你不能删除资源,因为使用它的东西会阻止它发生。软依赖则相反,即使其他东西正在使用资源,也可以毫无问题地删除和重新创建资源。硬依赖示例:VPC、子网。软依赖示例:Topic/Queue/Role.
您将有更好的时间将堆栈软依赖项作为 SSM 参数类型的堆栈参数传递,因为您将能够更新提供独立于使用它的依赖项的堆栈。而使用默认堆栈导出方法时会陷入僵局。您不能删除资源,因为其他东西正在导入它。所以你最终不得不做一些烦人的事情来让它工作,比如部署一次复制它然后再次部署删除旧的东西。在不导致堆栈导出的情况下使用 SSM 参数需要一些额外的工作,但对于软依赖项来说,长期这样做是值得的。
对于硬依赖,我不同意使用查找,因为如果有东西正在使用它,你真的想要防止删除,因为你最终会得到一个 DELETE_FAILED 堆栈,那就是一个可怕的地方结束。所以对于 VPC/Subnets 之类的事情,我认为实际使用堆栈 export/import 技术非常重要,如果你确实需要重新创建你的 VPC 因为更改,如果你遵循原则 1,你只需要做CDK 销毁然后部署,一切都会好起来的,因为您构建的 CDK 应用程序是完全可重新创建的。
当谈到数据的可重现性时,CustomResources 是您的朋友。
我正在探索 CDK 应用程序的可重构性。假设我定义了一个自定义构造(堆栈)来创建 EKS 集群。我们称它为 EksStack
。理想情况下,我会创建与集群和 EKS 集群本身相关联的角色,如以下代码段所述(我使用 Scala 而不是 Java,因此这些代码段将采用 Scala 语法):
class EksStack (scope: Construct, id: String, props: StackProps) extends Stack(scope, id, props) {
private val role = new Role(this, "eks-role", RoleProps.builder()
.description(...)
.managedPolicies(...)
.assumedBy(...)
.build()
)
private val cluster = new Cluster(this, "eks-cluster", ClusterProps.builder()
.version(...)
.role(role)
.defaultCapacityType(DefaultCapacityType.EC2)
.build()
)
}
当我合成应用程序时,我可以看到生成的模板包含 VPC 的定义,以及弹性 IP、NAT、互联网网关等。
现在假设我想重构 EksStack
并有一个不同的堆栈,比如 VpcStack
,显式创建 VPC:
class VpcStack (scope: Construct, id: String, props: StackProps) extends Stack(scope, id, props) {
val vpc = new Vpc(this, VpcId, VpcProps.builder()
.cidr(...)
.enableDnsSupport(true)
.enableDnsHostnames(true)
.maxAzs(...)
.build()
)
}
理想情况下,EksStack
中的集群将只使用对 VpcStack
创建的 VPC 的引用,类似于(注意集群构建器中对 vpc()
的新调用):
class EksStack (scope: Construct, id: String, props: StackProps, vpc: IVpc) extends Stack(scope, id, props) {
private val role = new Role(this, "eks-role", RoleProps.builder()
.description(...)
.managedPolicies(...)
.assumedBy(...)
.build()
)
private val cluster = new Cluster(this, "eks-cluster", ClusterProps.builder()
.version(...)
.role(role)
.vpc(vpc)
.defaultCapacityType(DefaultCapacityType.EC2)
.build()
)
}
这显然行不通,因为 CloudFormation 会删除 EksStack
创建的 VPC 以支持 VpcStack
创建的 VPC。我在这里和那里阅读并尝试在 EksStack
中添加保留策略并覆盖 VpcStack
中 VPC 的逻辑 ID,使用我最初在 EksStack
的 CloudFormation 模板中看到的 ID :
val cfnVpc = cluster.getVpc.getNode.getDefaultChild.asInstanceOf[CfnVPC]
cfnVpc.applyRemovalPolicy(RemovalPolicy.RETAIN)
和
val cfnVpc = vpc.getNode.getDefaultChild.asInstanceOf[CfnVPC]
cfnVpc.overrideLogicalId("LogicalID")
然后重试 diff
。同样,VPC 似乎被删除并重新创建。
现在,我看到可以使用“将资源导入堆栈”操作迁移 CloudFormation 资源 (https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/refactor-stacks.html)。我的问题是:我可以在 CDK 中将资源的创建从一个堆栈移动到另一个堆栈而不重新创建它吗?
编辑:
详细说明我的问题,当我在 VpcStack
中定义 VPC 时,我希望 CDK 认为 资源是由 VpcStack
创建的,而不是 ok EksStack
。像 将它的定义从一个堆栈移动到另一个堆栈,而无需 CloudFormation 删除原始定义以重新创建它。在我的用例中,我有一个堆栈最初定义一个创建(显式或隐式,例如我的 VPC),但之后,我可能想重构我的应用程序,将该资源的创建移动到专用堆栈。我试图了解此 移动 是否总是导致资源被重新创建,是否有任何方法可以避免它。
我不确定我是否理解这个问题,但如果您尝试引用现有资源,您可以使用上下文查询。 (例如 Vpc.fromLookup
)。
https://docs.aws.amazon.com/cdk/latest/guide/context.html
此外,如果您想在 EksStack 中使用从 VpcStack 创建的 Vpc,您可以从 VpcStack 输出 vpc id 并以这种方式在 eks 堆栈中使用上下文查询。
这是 C# 代码,但原理是一样的。
var myVpc = new Vpc(...);
new CfnOutput(this, "MyVpcIdOutput", new CfnOutputProps()
{
ExportName = "VpcIdOutput",
Value = myVpc.VpcId
}
然后当您创建 EksStack 时,您可以导入您之前导出的 vpc id。
new EksStack(this, "MyCoolStack", new EksStackProps()
{
MyVpcId = Fn.ImportValue("VpcIdOutput")
}
EksStackProps 在哪里
public class EksStackProps
{
public string MyVpcId { get; set; }
}
我认为当涉及到 CDK 和 CloudFormation 的可重构性时,尤其是多堆栈配置时,需要牢记一些原则。
整个应用程序应该可以完全删除并重新创建。所有数据管理都在应用程序中处理,无需手动处理。
不要总是依赖使用堆栈导出的自动堆栈间依赖管理。我喜欢将 CloudFormation 依赖项分为两类:硬和软。硬依赖意味着你不能删除资源,因为使用它的东西会阻止它发生。软依赖则相反,即使其他东西正在使用资源,也可以毫无问题地删除和重新创建资源。硬依赖示例:VPC、子网。软依赖示例:Topic/Queue/Role.
您将有更好的时间将堆栈软依赖项作为 SSM 参数类型的堆栈参数传递,因为您将能够更新提供独立于使用它的依赖项的堆栈。而使用默认堆栈导出方法时会陷入僵局。您不能删除资源,因为其他东西正在导入它。所以你最终不得不做一些烦人的事情来让它工作,比如部署一次复制它然后再次部署删除旧的东西。在不导致堆栈导出的情况下使用 SSM 参数需要一些额外的工作,但对于软依赖项来说,长期这样做是值得的。
对于硬依赖,我不同意使用查找,因为如果有东西正在使用它,你真的想要防止删除,因为你最终会得到一个 DELETE_FAILED 堆栈,那就是一个可怕的地方结束。所以对于 VPC/Subnets 之类的事情,我认为实际使用堆栈 export/import 技术非常重要,如果你确实需要重新创建你的 VPC 因为更改,如果你遵循原则 1,你只需要做CDK 销毁然后部署,一切都会好起来的,因为您构建的 CDK 应用程序是完全可重新创建的。
当谈到数据的可重现性时,CustomResources 是您的朋友。