解决 AWS CDK CloudFormation 堆栈之间的循环依赖关系

Posted

技术标签:

【中文标题】解决 AWS CDK CloudFormation 堆栈之间的循环依赖关系【英文标题】:Resolving cyclical dependencies between AWS CDK CloudFormation stacks 【发布时间】:2020-06-04 01:06:56 【问题描述】:

上下文,我有一个带有两个堆栈的 CDK 应用程序,使用以下设置:

Stack_A:
    StateMachine_A
    Lambda_A
    S3Bucket_A
    IAMRole_A

Stack_B:
    StateMachine_B
    SageMakerTrainJob_B
    IAMRole_B

StateMachine_A 使用执行角色 IAMRole_A 运行 Lambda_A。 StateMachine_A 中的一个单独步骤将数据写入 S3Bucket_A。 StateMachine_B 使用执行角色 IAMRole_B 运行 SageMakerTrainJob_B。 Lambda_A 的目的是开始执行 StateMachine_B,其 SageMakerTrainJob_B 需要从 S3Bucket_A 中读取。因此,我们必须配置以下权限:

IAMRole_A 需要对 StateMachine_B 的 startExecution 权限。 IAMRole_B 需要 S3Bucket_A 的读取权限。

我们尝试在 CDK 中对此进行建模,方法是在 Stack_B 中创建对 Stack_A 的直接依赖,使用 Stack_B 定义中对 IAMRole_A 和 S3Bucket_A 的引用来授予代码所需的权限。但是,这会产生以下错误:

Error: 'Stack_B' depends on 'Stack_A' (dependency added using stack.addDependency()). Adding this dependency (Stack_A -> Stack_B/IAMRole_B/Resource.Arn) would create a cyclic reference.

同样,尝试对另一个方向的依赖进行建模也会出现同样的错误:

Error: 'Stack_A' depends on 'Stack_B' (dependency added using stack.addDependency()). Adding this dependency (Stack_B -> Stack_A/S3Bucket_A/Resource.Arn) would create a cyclic reference.

有没有办法使用代码依赖项解决这个问题?对于这种情况,是否有任何推荐的最佳实践?我们考虑过的一些选项包括:

使用依赖于两者的第三个堆栈让 Stack_A 和 Stack_B 访问彼此的资源。 为每个堆栈中的必要资源创建额外的访问角色,并在 CDK 之外的某处维护 Lambda/SageMaker 角色的假设角色权限。 将它们全部放在一个堆栈中。不利于组织并使资源真正紧密耦合——我们可能不希望 StateMachine_A 成为未来 StateMachine_B 的唯一入口点。

另外,我发现在 CDK 开发过程中,CodeCommit/CodePipeline 和 APIGateway/Lambda 存在类似的问题。这是一个相关的错误,还是我们只是试图做一些不受支持的事情?

【问题讨论】:

根据您的描述,似乎 S3Bucket_A 不直接依赖于任何其他 A 构造,S3Bucket_A 也没有直接依赖于任何其他 A 构造。对吗? 为了简单起见,我在 StateMachine_A 中省略了一个写入 S3Bucket_A 的步骤。我编辑了描述以反映这一点。 【参考方案1】:

循环引用总是很棘手。这不是 CDK 独有的问题。当您从逻辑上解释问题时,您会看到事情开始崩溃的地方。 CloudFormation 必须先创建另一个资源所依赖的任何资源,然后才能创建依赖资源。没有一种适合所有方法的解决方案,但我会给出一些可行的想法。

    将共享资源提升到另一个堆栈。在您的情况下,两个堆栈都需要使用 S3 存储桶,因此如果它在两个堆栈之前运行的堆栈中,您可以创建 S3 存储桶,使用堆栈 B 中的导出/导入来引用 S3 存储桶,并使用和导出堆栈 A 中的 /import 以同时引用 S3 存储桶和 B 中的状态机。 在权限中使用通配符。通常,您可以知道资源的名称或足够的名称,以便在您的权限中使用通配符。您希望将权限保持在严格的范围内,但通常部分名称匹配就足够了。当然,请谨慎使用此选项。另请记住,您可以命名许多资源。许多人不喜欢这样做,有些资源你不应该这样做(比如和 S3 存储桶),但我经常发现命名事物更容易。 创建自定义资源以将事物联系在一起。如果您有一个无法解决的真正循环依赖项(即使在同一个堆栈中),您可能需要使用自定义资源来为您完成工作。 S3 bucket events 就是一个典型的例子。

【讨论】:

【参考方案2】:

除了 Jason 的指导方针之外,我想提一下第四个选项:

您还可以尝试通过更多地转向事件驱动架构来解耦这两个堆栈。虽然在此示例中您无法解析 IAMRole_B needs read permissions on S3Bucket_A 的依赖关系,但您可以解析依赖关系 IAMRole_A needs startExecution permissions on StateMachine_B。堆栈 A 可以引入一个 EventBridge,StateMachine_A 在完成后立即引发一个事件。 StateMachine_B 可以订阅此事件并在它上升后立即开始。堆栈如下所示:

Stack_A:
    StateMachine_A
    Event_A
    S3Bucket_A
    IAMRole_A

Stack_B:
    StateMachine_B
    SageMakerTrainJob_B
    IAMRole_B

您仍然有两个依赖项:

IAMRole_B 需要对 Event_A 的读取权限。 IAMRole_B 需要 S3Bucket_A 的读取权限。

【讨论】:

以上是关于解决 AWS CDK CloudFormation 堆栈之间的循环依赖关系的主要内容,如果未能解决你的问题,请参考以下文章

AWS CloudFormation:Cognito LambdaTrigger CustomEmailSender - 属性“AWS CloudFormation 目前不支持。”和 CDK 的使用

AWS - 如何使用 CDK/CloudFormation 将服务链接角色传递给自动缩放组?

如何在 Cloudformation 模板/CDK 中添加 AWS IoT 配置模板

如何使用 aws cloudformation 或 aws cdk 设置 aws aurora mysql 表?

是否有用于导入整个 DNS 区域传输的 CDK 或 CloudFormation 构造?

如何使用 AWS CDK 将域别名添加到现有 CloudFront 分配