在 VPC 中从 Lambda 访问 AWS S3

总的来说,我对在 VPC 中使用 AWS Lambda 感到非常困惑。问题是 Lambda 在尝试访问 S3 bucket 时超时了。解决方案似乎是 VPC 端点。

我已经将 Lambda 函数添加到 VPC 中,这样它就可以访问 RDS 托管的数据库(下面的代码中没有显示,但它是功能性的)。但是,现在我不能访问 S3,并且任何尝试这样做的尝试都会超时。

我尝试创建一个 VPC S3端点,但是什么都没有改变。

VPC 配置

每当我第一次创建 EC2实例时,我都使用默认创建的简单 VPC。它有四个子网,都是默认创建的。

路线表

_Destination - Target - Status - Propagated_


172.31.0.0/16 - local - Active - No


pl-63a5400a (com.amazonaws.us-east-1.s3) - vpce-b44c8bdd - Active - No


0.0.0.0/0 - igw-325e6a56 - Active - No

简单 S3下载 Lambda:

import boto3
import pymysql
from StringIO import StringIO


def lambda_handler(event, context):
s3Obj = StringIO()


return boto3.resource('s3').Bucket('marineharvester').download_fileobj('Holding - Midsummer/sample', s3Obj)
39652 次浏览

问题的原因是没有正确配置安全组的出站规则。具体来说,我需要添加目标为 pl-XXXXXXXX (S3服务)的自定义协议出站规则。实际值由 AWS 控制台提供)。

使用 boto3时,S3 URL 默认为 虚拟的,然后需要将 Internet 访问解析为特定区域的 URL。这会导致 Lambda 函数挂起,直到超时。

要解决这个问题,需要在创建客户机时使用 Config对象,它告诉 boto3创建基于 路径的 S3 URL:

import boto3
import botocore


client = boto3.client('s3', 'ap-southeast-2', config=botocore.config.Config(s3={'addressing_style':'path'}))

请注意,调用中的区域必须是要将 lambda 和 VPC 端点部署到的区域。

然后,您将能够在 Lambda 的安全组中使用 VPC 端点的 pl-xxxxxx前缀列表,并且仍然可以访问 S3。

下面是一个工作的 云阵脚本,它演示了这一点。它创建了一个 S3 bucket、一个与 VPC 关联的 lambda (将记录放入 bucket) ,该 VPC 仅包含私有子网和 VPC 端点,以及必要的 IAM 角色。

还有另一个问题与子网和路由有关,这在其他答案中没有涉及到,所以我创建了一个单独的答案,但前提是上述所有答案都适用。为了让 lambda 函数能够访问 S3,必须让它们运行正常。

当您创建一个新的 AWS 帐户,我去年秋天做的,没有路由表自动关联到您的默认 VPC (参见路由表-> 子网关联在控制台)。

因此,如果按照 指示创建端点并为该端点创建路由,则不会添加任何路由,因为没有子网可以放置它。像往常一样,AWS 不会收到错误消息... ..。

您应该为 lambda 函数创建一个子网,将该子网与路由表和 lambda 函数关联起来,然后重新运行 Endpoint 指令,如果成功,您将找到一个路由表,其中包含三个条目,如下所示:

Destination     Target
10.0.0.0/16     Local
0.0.0.0/0       igw-1a2b3c4d
pl-1a2b3c4d     vpce-11bb22cc

如果只有两个条目(没有‘ pl-xxxxx’条目) ,那么还没有成功。

最后,我想 lambda 函数像网络中的任何其他实体一样需要一个子网来存活,这应该不足为奇。并且可能建议它不要与您的 EC2实例位于同一个子网上,因为 lambda 可能需要不同的路由或安全权限。注意,lambda 中的 GUI 确实希望在两个不同的 AZ 中有两个子网,这也是一个好主意。

还有另一个与 VPC 端点相关的解决方案。

在 AWS 控制台上,选择 VPC 服务,然后选择端点。创建一个新的端点,将其关联到 s3服务

VPC S3 endpoint selection

然后选择 VPC 和路由表。

然后选择访问级别(完全或自定义) ,它将工作。

我只是想在其中添加一个其他的答案,这可能会影响那些运行的函数与缓慢的冷启动时间。

我遵循了所有关于为 S3设置网关的说明,但它仍然不起作用。我创建了一个测试 Node.js 函数,它只是列出了存储桶——我验证了这个函数在没有 S3网关的情况下是不能工作的,但是在网关建立之后就可以工作了。所以我知道事情的一部分进行得很顺利。

在我调试这个函数时,我改变了函数的超时时间,以确保函数被更新,并且在调用和测试时使用了最新版本的代码。

我把超时时间减少到了10秒,结果发现我的功能需要更多的15秒冷启动

要从 VPC 内部的 Lambda 函数访问 S3,可以使用 Natgate (与 VPC 端点相比,这是一个非常昂贵的解决方案)。如果在 VPC 中有两个私有子网(其中子网有一个到 NAT 网关的路由)并将它们与 Lambda 相关联,那么它可以像 VPC 之外的任何 Lambda 一样访问 S3存储桶。 抓到你了

  1. 如果您将一个公共子网与 Lambda 相关联,并期望它能够工作,那么它将不能工作。
  2. 确保您的安全组已就位,可以接受进入。

这种方法将使互联网上的任何服务都可以访问 Lambda 函数。详细的步骤,你可以按照这个博客 https://blog.theodo.com/2020/01/internet-access-to-lambda-in-vpc/

为了补充 Luis RM给出的答案,这是一个可以在 CDK 中使用的构造:

 const vpcEndpoint = new ec2.GatewayVpcEndpoint(this, 'S3GatewayVpcEndpoint', {
vpc: myVpc,
service: { name: 'com.amazonaws.us-west-1.s3' },
})


const rolePolicies = [
{
Sid: 'AccessToSpecificBucket',
Effect: 'Allow',
Action: [
's3:ListBucket',
's3:GetObject',
's3:PutObject',
's3:DeleteObject',
's3:GetObjectVersion',
],
Resource: ['arn:aws:s3:::myBucket', arn:aws:s3:::myBucket/*'],
Principal: '*',
},
]
rolePolicies.forEach((policy) => {
vpcEndpoint.addToPolicy(iam.PolicyStatement.fromJson(policy))
})

在 VPC 中,有3种方法可以从私有子网内访问 S3

  • NAT 网关
  • 网关端点
  • 接口端点

我猜你不想使用 NAT 网关。

如果您使用网关端点-您不需要更改连接到 S3的端点。但是如果使用接口端点,则需要更新 s3端点。

有一个详细的一步一步的指南做同样的事情在这里-https://www.cloudtechsimplified.com/aws-lambda-vpc-s3/