Вопрос или проблема
Я уже несколько дней пытаюсь настроить работу развертывания Lambda+EFS
Я использую Pulumi на Python
Все, что я пробовал, заканчивается:
Ошибка вызова API: Функция не смогла подключиться к файловой системе Amazon EFS с арном точки доступа arn:aws:elasticfilesystem:eu-west-2:003478557222:access-point/fsap-0bf5cec491c7daa7d. Проверьте свою сетевую конфигурацию и повторите попытку.
Я хочу, чтобы они оба находились в частной подсети без NAT-шлюза
В этой статье:
https://docs.aws.amazon.com/lambda/latest/dg/troubleshooting-invocation.html#troubleshooting-invocation-efsconnect
…говорится, что эта ошибка вызвана:
Функция не смогла установить соединение с файловой системой функции с протоколом NFS (TCP порт 2049). Проверьте настройки группы безопасности и маршрутизации для подсетей VPC.
Сначала я думал, что могу поместить Lambda и точку монтирования EFS в одну и ту же группу безопасности, и тогда не нужны будут явные правила входящего/исходящего трафика NFS. Насколько я понимаю, это не сработало.
В процессе следования различным советам и документации я в итоге получил две группы безопасности (например, как описано здесь: https://docs.aws.amazon.com/efs/latest/ug/network-access.html), одна из которых имеет исходящий NFS, а другая – входящий. Это тоже не работает.
Я начинаю подозревать, что эта ошибка может иметь другую причину, так как, по моим подсчетам, я попробовал почти все комбинации правил ГС, пытаясь обеспечить подключение.
Но, возможно, я совершил какую-то глупость, которую не замечаю.
Мой текущий код выглядит так:
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,
),
# разрешить конечные точки VPC:
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"],
)
# если Lambda находится в частной подсети без NAT, то нужно добавить конечные точки VPC
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, # ограничить максимальную параллельность
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,
),
)
Ответ или решение
Если ваша функция AWS Lambda не может подключиться к файловой системе Amazon EFS, есть несколько ключевых аспектов, которые могут вызвать проблему. Давайте подробно рассмотрим возможные причины и их решения, основываясь на предоставленном сценарии и конфигурации.
1. Сетевые настройки VPC
Ваша конфигурация показывает, что вы создали VPC с частными подсетями и без NAT-шлюза. Это может стать проблемой, если ваша Lambda-функция и EFS находятся в разных сетевых сегментах. Поскольку вы нацелены на частные подсети, убедитесь, что обе службы находятся в одной VPC и частных подсетях.
Рекомендации:
- Проверьте, что Lambda-функция и EFS Mount Target находятся в одной и той же VPC.
- Убедитесь, что EFS доступен через NFS (TCP порт 2049), и что настройка маршрутизации корректна.
2. Настройки Security Group
Ваше текущее использование двух security group (SG) для Lambda и EFS – это правильный подход. Однако, необходимо проверить, корректные ли правила заданы для NFS.
Возможные проблемы:
-
Security Group для Lambda:
- Вы настроили egress правило для порта 2049, однако, возможно, оно не срабатывает должным образом.
- Попробуйте добавить ingress правило в Security Group EFS, чтобы позволить трафик от вашей Lambda функции.
-
Security Group для EFS:
- Правило ingress NFS указывает на Security Group вашей Lambda, но убедитесь, что вы правильно указали source_security_group_id.
Примеры правил:
# Для Lambda
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=["0.0.0.0/0"], # или более ограниченный CIDR для безопасности
)
# Для EFS
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,
)
3. Проверка маршрутизации
Маршруты внутри VPC могут повлиять на ваше подключение. Убедитесь, что:
- Подсети имеют маршруты на уровне VPC, и ваш EFS Mount Target доступен из частных подсетей Lambda.
4. Проверка конфигурации файловой системы
Убедитесь, что файловая система EFS правильно настроена:
- Проверьте, что EFS Mounted Target без проблем доступен в ожидании соединений.
- Проверьте, существует ли точка доступа EFS (Access Point) и правильно ли указаны пути и параметры доступа.
5. Логирование и диагностика
Для дальнейшей диагностики вы можете:
- Включить дополнительное логирование в вашей Lambda-функции, чтобы отслеживать любые проблемы с подключением.
- Используйте CloudWatch для анализа логов вашей Lambda, чтобы получить дополнительную информацию о произошедших ошибках.
Заключение
Чтобы успешно установить соединение между AWS Lambda и Amazon EFS, необходимо убедиться в корректной настройке сетевой инфраструктуры и безопасности. Следуя приведённым выше рекомендациям и внимательно проверяя конфигурацию, вы сможете устранить возникшие трудности. Если проблема не устраняется, рассмотрите возможность обращения в службу поддержки AWS для более детального анализа конфигурации.