为了简单起见,我有一个 VPC 堆栈、一个 RDS 堆栈和一个 Lambda 堆栈。
Lambda堆栈依赖于RDS堆栈,并且它们都依赖于VPC堆栈,这是独立的。然而,AWS 告诉我 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 堆栈中找不到任何 Lambda 依赖项。
有关更多上下文,这里是整个 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",
)
如有任何帮助,我们将不胜感激。
编辑:添加主 Lamba 堆栈以获得额外的上下文(它相当大,但我不知道要为 MRE 切断什么)
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",
)
# company lambda environment variables
DB_HOST = rds_proxy.endpoint
IMAGE_DELETER_QUEUE_URL = image_deleter_queue.queue_url
REDIS_HOST = redis_cluster.attr_redis_endpoint_address
env_vars = {
"DB_FORCE_ROLLBACK": "False",
"DB_HOST": DB_HOST,
"DB_PORT": "5432",
"DB_SCHEMA": "companyMVP_DEV",
"ENV_STATE": "DEV",
"IMAGE_DELETER_QUEUE_URL": IMAGE_DELETER_QUEUE_URL,
"LOG_LEVEL": "DEBUG",
"REDIS_HOST": REDIS_HOST,
"REDIS_PORT": "6379",
}
# Execution role
multi_service_role = iam.Role(
self,
"LambdaMultiServiceRole",
assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"),
managed_policies=[
iam.ManagedPolicy.from_aws_managed_policy_name(
"AmazonElastiCacheFullAccess"
),
iam.ManagedPolicy.from_aws_managed_policy_name("AmazonS3FullAccess"),
iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSESFullAccess"),
iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSQSFullAccess"),
iam.ManagedPolicy.from_aws_managed_policy_name("AWSLambdaExecute"),
iam.ManagedPolicy.from_aws_managed_policy_name(
"SecretsManagerReadWrite"
),
],
role_name="lambda-multi-service-role",
)
# Add logs policy
multi_service_role.add_to_policy(
statement=iam.PolicyStatement(
actions=[
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
resources=[f"arn:aws:logs:us-east-1:{Cfg.ACCOUNT}:*"],
effect=iam.Effect.ALLOW,
)
)
# Add Cognito policy
multi_service_role.add_to_policy(
statement=iam.PolicyStatement(
actions=[
"cognito-identity:GetOpenIdTokenForDeveloperIdentity",
"cognito-identity:GetId",
"cognito-identity:GetCredentialsForIdentity",
],
resources=["*"],
effect=iam.Effect.ALLOW,
)
)
# Add RDS Proxy policy
multi_service_role.add_to_policy(
statement=iam.PolicyStatement(
actions=[
"rds-db:connect",
],
# CHECK HERE IF SHIT DONT WORK
resources=[f"arn:aws:rds-db:us-east-1:{Cfg.ACCOUNT}:*/*"],
effect=iam.Effect.ALLOW,
)
)
# Add X-ray policy
multi_service_role.add_to_policy(
statement=iam.PolicyStatement(
actions=[
"xray:PutTraceSegments",
"xray:PutTelemetryRecords",
],
resources=["*"],
effect=iam.Effect.ALLOW,
)
)
# Add VPC Access policy
multi_service_role.add_to_policy(
statement=iam.PolicyStatement(
actions=[
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:DescribeNetworkInterfaces",
],
resources=["*"],
effect=iam.Effect.ALLOW,
)
)
# company lambda function
lambda_fn = _lambda.Function(
self,
"companyLambda",
runtime=_lambda.Runtime.PYTHON_3_12,
handler="main.handler",
memory_size=1024,
vpc=vpc,
security_groups=[sg_company_lambda],
vpc_subnets=ec2.SubnetSelection(
subnet_type=ec2.SubnetType.PRIVATE_ISOLATED
),
environment=env_vars,
timeout=Duration.seconds(30),
role=multi_service_role,
code=_lambda.Code.from_bucket(packages_bucket, "main_lambda.zip"),
)
# Add alias
self.lambda_alias = _lambda.Alias(
self,
"MainLambdaAlias",
alias_name="DEV",
version=lambda_fn.current_version,
provisioned_concurrent_executions=1,
)
# Add alias ARN to parameter store
ssm.StringParameter(
self,
"main_lambda_alias_arn",
parameter_name="/cdk/main_lambda/alias_arn",
string_value=self.lambda_alias.function_arn,
)
# Parameter extension layer
parameters_layer = _lambda.LayerVersion.from_layer_version_arn(
self,
"ParametersLayer",
layer_version_arn="arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11",
)
# Add parameter extension layer to lambda function
lambda_fn.add_layers(parameters_layer)
# Redis ingress from company Lambda
sg_redis.add_ingress_rule(
ec2.Peer.security_group_id(sg_company_lambda.security_group_id),
ec2.Port.tcp(6379),
"Allow Redis traffic from company Lambda security group",
)
# 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",
)
# Create a new SES domain identity
domain_identity = ses.EmailIdentity( # noqa F841
self, "DomainIdentity", identity=ses.Identity.domain("company.io")
)
# Create cognito identity pool provider
cognito_identity_pool = cognito.CfnIdentityPool( # noqa F841
self,
"CognitoIdentityPool",
allow_unauthenticated_identities=False,
identity_pool_name="company-verified-users",
developer_provider_name="company.io",
)
# Create cognito identity pool role
cognito_identity_pool_role = iam.Role( # noqa F841
self,
"CognitoIdentityPoolRole",
assumed_by=iam.FederatedPrincipal(
federated="cognito-identity.amazonaws.com",
conditions={
"StringEquals": {
"cognito-identity.amazonaws.com:aud": cognito_identity_pool.ref
}
},
assume_role_action="sts:AssumeRoleWithWebIdentity",
),
)
# Add get credentialts for identity policy
cognito_identity_pool_role.add_to_policy(
statement=iam.PolicyStatement(
actions=["cognito-identity:GetCredentialsForIdentity"],
resources=["*"],
effect=iam.Effect.ALLOW,
)
)
images_bucket_name = images_bucket.bucket_name
# Add S3 get object policy
cognito_identity_pool_role.add_to_policy(
statement=iam.PolicyStatement(
actions=["s3:GetObject"],
resources=[f"arn:aws:s3:::{images_bucket_name}/*"],
effect=iam.Effect.ALLOW,
)
)
我想说这些命令将 RDS 堆栈的引用添加到 lambda 堆栈
# Redis ingress from company Lambda
sg_redis.add_ingress_rule(
ec2.Peer.security_group_id(sg_company_lambda.security_group_id),
ec2.Port.tcp(6379),
"Allow Redis traffic from company Lambda security group",
)
# 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",
)
有时,CDK 构造源代码会以其他方式产生依赖关系,而不是您的代码在堆栈之间进行构造函数注入。
我建议使用 CDK 合成器并研究跨堆栈使用的输出导出的输出 CloudFormation 模板。
您还可以在 GitHub 中查看 CDK 构造的源代码。