You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AWS CDK循环引用错误排查:RDS与Lambda栈互依赖原因不明

AWS CDK循环引用错误定位与分析

问题背景

部署VPC、RDS、Lambda三类资源栈,设计逻辑为Lambda栈依赖RDS栈,两者均依赖独立的VPC栈,但CDK提示RDS栈与Lambda栈存在互相依赖,触发循环引用错误。

主文件栈定义

vpc_stack = VPCStack(
    app,
    "VPCStack",
    env=cdk.Environment(account=Cfg.ACCOUNT, region=Cfg.REGION),
)

rds_stack = RDSStack(
    app,
    "RDSStack",
    vpc=vpc_stack.vpc,
    env=cdk.Environment(account=Cfg.ACCOUNT, region=Cfg.REGION),
)

main_lambda_stack = MainLambdaStack(
    app,
    "MainLambdaStack",
    vpc=vpc_stack.vpc,
    rds_proxy=rds_stack.rds_proxy,
    image_deleter_queue=bid_lambda_stack.image_deleter_queue,
    redis_cluster=redis_stack.redis_cluster,
    packages_bucket=s3_stack.packages_bucket,
    sg_redis=redis_stack.sg_redis,
    sg_rds_proxy=rds_stack.sg_rds_proxy,
    images_bucket=s3_stack.user_images_bucket,
    env=cdk.Environment(account=Cfg.ACCOUNT, region=Cfg.REGION),
)

错误信息

RuntimeError: Error: 'RDSStack' depends on 'MainLambdaStack' 
(RDSStack -> MainLambdaStack/LambdaSecurityGroup/Resource.GroupId). 
Adding this dependency (MainLambdaStack -> RDSStack/RDSProxy/Resource.Endpoint) 
would create a cyclic reference.

RDS栈完整代码

class RDSStack(Stack):
    def __init__(
        self, scope: Construct, construct_id: str, vpc: ec2.Vpc, **kwargs
    ) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # RDS security group
        sg_rds = ec2.SecurityGroup(
            self,
            "RDSSecurityGroup",
            vpc=vpc,
            description="Security group for RDS",
            security_group_name="rds-sg",
        )

        # RDS Proxy security group
        self.sg_rds_proxy = ec2.SecurityGroup(
            self,
            "RDSProxySecurityGroup",
            vpc=vpc,
            description="Security group for RDS Proxy",
            security_group_name="rds-proxy-sg",
        )

        # RDS instance
        rds_instance = rds.DatabaseInstance(  # noqa: F841
            self,
            "RDSInstance",
            engine=rds.DatabaseInstanceEngine.postgres(
                version=rds.PostgresEngineVersion.VER_16_1
            ),
            instance_type=ec2.InstanceType.of(
                ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO
            ),
            vpc=vpc,
            vpc_subnets=ec2.SubnetSelection(
                subnet_type=ec2.SubnetType.PRIVATE_ISOLATED
            ),
            security_groups=[sg_rds],
            removal_policy=RemovalPolicy.RETAIN,
            allocated_storage=20,
            auto_minor_version_upgrade=True,
            enable_performance_insights=True,
            monitoring_interval=Duration.minutes(10),
            backup_retention=Duration.days(7),
        )

        # RDS Proxy
        self.rds_proxy = rds.DatabaseProxy(  # noqa: F841
            self,
            "RDSProxy",
            vpc=vpc,
            vpc_subnets=ec2.SubnetSelection(
                subnet_type=ec2.SubnetType.PRIVATE_ISOLATED
            ),
            security_groups=[self.sg_rds_proxy],
            db_proxy_name="rds-proxy",
            proxy_target=rds.ProxyTarget.from_instance(rds_instance),
            secrets=[rds_instance.secret],
        )

        # RDS ingress from RDS Proxy
        sg_rds.add_ingress_rule(
            ec2.Peer.security_group_id(self.sg_rds_proxy.security_group_id),
            ec2.Port.tcp(5432),
            "Allow PostgreSQL traffic from RDS Proxy security group",
        )

Main Lambda栈关键代码片段

class MainLambdaStack(Stack):

    def __init__(
        self,
        scope: Construct,
        construct_id: str,
        vpc: ec2.Vpc,
        rds_proxy: rds.DatabaseProxy,
        image_deleter_queue: sqs.Queue,
        redis_cluster: elasticache.CfnCacheCluster,
        packages_bucket: s3.Bucket,
        sg_redis: ec2.SecurityGroup,
        sg_rds_proxy: ec2.SecurityGroup,
        images_bucket: s3.Bucket,
        **kwargs,
    ) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # company lambda security group
        sg_company_lambda = ec2.SecurityGroup(
            self,
            "companyLambdaSecurityGroup",
            vpc=vpc,
            description="Security group for company Lambda",
            security_group_name="company-lambda-sg",
        )

        # ... 其他代码省略 ...

        # RDS Proxy ingress from company Lambda
        sg_rds_proxy.add_ingress_rule(
            ec2.Peer.security_group_id(sg_company_lambda.security_group_id),
            ec2.Port.tcp(5432),
            "Allow PostgreSQL traffic from company Lambda security group",
        )

        # ... 其他代码省略 ...

问题原因定位

循环引用的核心原因在于跨栈修改资源属性:

  1. Lambda栈依赖RDS栈的rds_proxy和sg_rds_proxy,形成MainLambdaStack → RDSStack的依赖关系。
  2. 在Lambda栈中,通过sg_rds_proxy.add_ingress_rule给RDS栈所属的安全组添加了 ingress 规则,该规则引用了Lambda栈内的sg_company_lambda的GroupId。CDK会自动为这种跨栈修改添加反向依赖,即RDSStack → MainLambdaStack(因为RDS的安全组需要等待Lambda的安全组创建完成才能获取其GroupId)。
  3. 双向依赖形成循环,触发CDK的循环引用校验错误。

解决方案思路

避免跨栈修改资源,将安全组规则的创建逻辑移至资源所属的栈中:

  • 修改RDS栈的构造函数,添加一个参数接受Lambda的安全组sg_company_lambda。
  • 在RDS栈内部,为sg_rds_proxy添加允许Lambda安全组访问的 ingress 规则。
  • 调整栈的依赖传递,确保RDS栈能获取到Lambda栈的安全组(注意需要通过CDK的依赖机制正确传递,避免新的循环)。

内容的提问来源于stack exchange,提问作者Nikolaisyl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 07:59:51