0

I have been struggling for days trying to get a deployment of Lambda+EFS that actually works

I am using Pulumi in Python

Everything I've tried results in:

Calling the invoke API action failed with this message: The function couldn't connect to the Amazon EFS file system with access point arn:aws:elasticfilesystem:eu-west-2:003478557222:access-point/fsap-0bf5cec491c7daa7d. Check your network configuration and try again.

I want them both to live in a private subnet with no NAT gateway

This article: https://docs.aws.amazon.com/lambda/latest/dg/troubleshooting-invocation.html#troubleshooting-invocation-efsconnect ...suggests that this error is due to:

The function couldn't establish a connection to the function's file system with the NFS protocol (TCP port 2049). Check the security group and routing configuration for the VPC's subnets.

Initially I had thought that I could put the Lambda and the EFS Mount Target in the same security group and then wouldn't need explicit NFS ingress/egress rules. AFAICT that didn't work.

In the process of following various advice and docs I have subsequently ended up with two security groups (e.g. as described here: https://docs.aws.amazon.com/efs/latest/ug/network-access.html) one having NFS egress and the other having NFS ingress. This doesn't work either.

I am beginning to suspect that this error may have another cause as I think by now I have tried about every combination of SG rules trying to enable connectivity.

But maybe I've done something stupid that I'm not seeing.

My current code looks like:

vpc = awsx.ec2.Vpc(
    "myvpc",
    subnet_specs=[
        awsx.ec2.SubnetSpecArgs(type=awsx.ec2.SubnetType.PUBLIC),
        awsx.ec2.SubnetSpecArgs(type=awsx.ec2.SubnetType.PRIVATE),
    ],
    subnet_strategy=awsx.ec2.SubnetAllocationStrategy.AUTO,
    number_of_availability_zones=1,
    nat_gateways=awsx.ec2.NatGatewayConfigurationArgs(
        strategy=awsx.ec2.NatGatewayStrategy.NONE,
    ),
    # allow VPC endpoints:
    enable_dns_hostnames=True,
    enable_dns_support=True,
)
lambda_sg = aws.ec2.SecurityGroup(
    "lambda-sg",
    vpc_id=vpc.vpc_id,
    ingress=[],
    egress=[],
)
def lambda_sg_rules(subnet_ids: Sequence[str]):
    subnets = [aws.ec2.get_subnet(id=subnet_id) for subnet_id in subnet_ids]
    cidr_blocks = [subnet.cidr_block for subnet in subnets]
    aws.ec2.SecurityGroupRule(
        "lambda-sg-ingress-https",
        security_group_id=lambda_sg.id,
        type="ingress",
        from_port=443,
        to_port=443,
        protocol="tcp",
        cidr_blocks=pulumi.Output.all(*cidr_blocks),
    )
    aws.ec2.SecurityGroupRule(
        "lambda-sg-egress-nfs",
        security_group_id=lambda_sg.id,
        type="egress",
        from_port=2049,
        to_port=2049,
        protocol="tcp",
        cidr_blocks=pulumi.Output.all(*cidr_blocks),
    )
vpc.private_subnet_ids.apply(lambda_sg_rules)

efs_sg = aws.ec2.SecurityGroup( "efs-sg", vpc_id=vpc.vpc_id, ingress=[], egress=[], ) aws.ec2.SecurityGroupRule( "efs-sg-ingress-nfs", security_group_id=efs_sg.id, type="ingress", from_port=2049, to_port=2049, protocol="tcp", source_security_group_id=lambda_sg.id, ) aws.ec2.SecurityGroupRule( "efs-sg-egress-all", security_group_id=efs_sg.id, type="egress", from_port=0, to_port=0, protocol="-1", cidr_blocks=["0.0.0.0/0"], )

if Lambda is in private subnet with no NAT then we need to add VPC endpoints

for service in ("ssm", "ssmmessages", "ec2messages", "logs", "elasticfilesystem"): aws.ec2.VpcEndpoint( f"{service}-endpoint", vpc_id=vpc.vpc_id, service_name=f"com.amazonaws.{aws.get_region().name}.{service}", vpc_endpoint_type="Interface", private_dns_enabled=True, subnet_ids=vpc.private_subnet_ids, security_group_ids=[lambda_sg.id, efs_sg.id], )

db_efs = aws.efs.FileSystem("db-filesystem") aws.efs.BackupPolicy( "db-filesystem-backup-policy", file_system_id=db_efs.id, backup_policy=aws.efs.BackupPolicyBackupPolicyArgs(status="ENABLED"), ) db_efs_mount_targets = vpc.public_subnet_ids.apply( lambda subnet_ids: [ aws.efs.MountTarget( f"db-filesystem-mount-target-subnet-{i}", file_system_id=db_efs.id, subnet_id=subnet_id, security_groups=[lambda_sg.id], ) for i, subnet_id in enumerate(subnet_ids) ] ) aws.efs.AccessPointRootDirectoryCreationInfoArgs db_efs_access_point = aws.efs.AccessPoint( "db-filesystem-accesspoint", file_system_id=db_efs.id, posix_user=aws.efs.AccessPointPosixUserArgs( uid=1000, gid=1000, ), root_directory=aws.efs.AccessPointRootDirectoryArgs( path="/dbroot", creation_info=aws.efs.AccessPointRootDirectoryCreationInfoArgs( owner_gid=1000, owner_uid=1000, permissions="755", ), ), opts=pulumi.ResourceOptions(depends_on=db_efs_mount_targets), )

lambda_network_policy = aws.iam.get_policy_document( statements=[ aws.iam.GetPolicyDocumentStatementArgs( effect="Allow", actions=[ "ec2:CreateNetworkInterface", "ec2:DescribeNetworkInterfaces", "ec2:DeleteNetworkInterface", ], resources=["*"], ), ] ) lambda_logging_policy = aws.iam.get_policy_document( statements=[ aws.iam.GetPolicyDocumentStatementArgs( effect="Allow", actions=[ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ], resources=["arn:aws:logs:::*"], ) ] ) djangoadmin_lambda_role = aws.iam.Role( "djangoadmin-lambda-role", assume_role_policy=lambda_assume_role_policy.json, managed_policy_arns=[ "arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy", "arn:aws:iam::aws:policy/AmazonElasticFileSystemClientReadWriteAccess", ], ) aws.iam.RolePolicy( "djangoadmin-lambda-network-policy", role=djangoadmin_lambda_role.name, policy=lambda_network_policy.json, ) aws.iam.RolePolicy( "djangoadmin-lambda-logging-policy", role=djangoadmin_lambda_role.name, policy=lambda_logging_policy.json, )

djangoadmin_lambda_log_group = aws.cloudwatch.LogGroup( "djangoadmin-lambda-log-group", name=f"/aws/lambda/{APP_NAME}", retention_in_days=30, ) djangoadmin_lambda = aws.lambda_.Function( "djangoadmin-lambda", opts=pulumi.ResourceOptions(depends_on=[djangoadmin_lambda_log_group]), package_type="Image", image_uri=lambda_image_uri, architectures=["arm64"], memory_size=512, timeout=30, reserved_concurrent_executions=1, # limit max concurrency file_system_config=aws.lambda_.FunctionFileSystemConfigArgs( arn=db_efs_access_point.arn, local_mount_path="/mnt/efs", ), role=djangoadmin_lambda_role.arn, environment=aws.lambda_.FunctionEnvironmentArgs( variables={ "AWS_LWA_PORT": "5000", }, ), vpc_config=aws.lambda_.FunctionVpcConfigArgs( subnet_ids=vpc.private_subnet_ids, security_group_ids=[lambda_sg.id], ), logging_config=aws.lambda_.FunctionLoggingConfigArgs( log_format="JSON", log_group=djangoadmin_lambda_log_group.name, ), )

Anentropic
  • 101
  • 2

0 Answers0