为什么处于VPC公共子网中的AWS Lambda函数无法连接到互联网?

90
我按照这里的教程创建了一个包含公共和私有子网的VPC。
然后我在公共子网中设置了一个AWS lambda函数来测试它是否能够连接到外部互联网。
以下是我用python3编写的lambda函数。
import requests

def lambda_handler(event, context):
    r = requests.get('http://www.google.com')
    print(r)

我将函数放在VPC的公共子网中,但是无法获取 http://www.google.com 的内容。

这里是错误消息:

"errorMessage": "HTTPConnectionPool(host='www.google.com', port=80): Max retries exceeded with url: / (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 110] Connection timed out',))", "errorType": "ConnectionError",

我不明白为什么。

公共子网的路由表如下所示:

enter image description here

对于 http://www.google.comGET 请求应该匹配 igw-XXXXXXXXX 的目标。为什么互联网网关(igw)不能将请求传递到 http://www.google.com 并获取网站内容?

这篇文章说,我必须将lambda函数设置在私有子网中才能访问Internet。

如果您的Lambda函数需要访问私有VPC资源(例如Amazon RDS DB实例或Amazon EC2实例),则必须将函数与VPC关联。如果您的函数还需要访问Internet(例如到达公共AWS服务终端点),则必须使用NAT网关或实例。

但它没有解释为什么我不能将lambda函数设置在公共子网中。


Lambda在Cloudwatch日志中记录了什么?假设您已经将请求模块包含在部署包中?可能是NACL阻止出站流量吗? - toringe
您是否真的需要将Lambda函数部署到VPC中? - jarmod
2
您需要设置一个NAT网关。https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html - kichik
3个回答

201

连接到 VPC 公共子网的 Lambda 函数通常无法访问互联网。

要从公共子网访问互联网,您需要一个公共 IP 或者需要通过具有公共 IP 的 NAT 进行路由。您还需要一个Internet Gateway (IGW)。但是:

  1. Lambda 函数没有也不能拥有公共 IP 地址,
  2. VPC 公共 子网中的默认路由目标是 IGW,而不是 NAT。

因此,由于 Lambda 函数只有私有 IP,并且其流量被路由到 IGW 而不是 NAT,所有从 Lambda 函数到互联网的数据包都将在 IGW 处被丢弃。

** 有一种所谓的解决方法,即可以将弹性 IP 与 Lambda 函数的 ENI 关联起来。请参阅this post以获取更多详细信息和一些可能重要的限制。

我应该为我的 Lambda 函数配置 VPC 访问吗?

如果您的Lambda函数不需要访问VPC内的私有资源(例如RDS数据库或Elasticsearch集群),则不要配置Lambda函数连接到VPC。

如果您的Lambda函数需要访问VPC内的私有资源,则配置Lambda函数连接到私有子网(仅限私有子网)。

NAT还是不需要?

如果Lambda函数只需要访问VPC中的资源(例如私有子网中的RDS数据库),则无需通过NAT路由。

如果Lambda函数只需要访问VPC中的资源和所有可通过私有VPC Endpoint访问的AWS服务,则无需通过NAT路由。使用VPC端点。

如果您的Lambda函数需要访问Internet上的终端,则请确保从Lambda函数的私有子网到公共子网中的NAT实例或NAT网关的默认路由。如果需要,请配置IGW,否则无法访问Internet。

请注意,NAT网关费用按小时和每GB处理收费,因此了解如何减少NAT网关的数据传输成本是值得的。

最佳实践

在为VPC访问配置Lambda函数时,将多个(私有)子网配置到不同的可用区(AZ)中是一种高可用性的最佳实践。

间歇性连接

确保您为Lambda函数配置的所有子网都是私有子网。 配置例如1个私有子网和1个公共子网是常见错误。 这将导致Lambda函数有时可以正常工作,而其他时间则没有任何明显原因而无法正常工作。

例如,Lambda函数可能连续成功5次,然后由于超时而失败(无法访问某些互联网资源或AWS服务)。这是因为第一次启动在私有子网中,启动2-5重用了相同的Lambda函数执行环境在同一个私有子网中(所谓的“热启动”),然后启动6是一个“冷启动”,其中AWS Lambda服务将Lambda函数部署在公共子网中,Lambda函数没有路由到互联网。

2
你建议在私有子网中运行 lambda 有什么原因吗?与在公共子网上运行相比,有什么缺点吗? - LLL
5
在使用Lambda函数时,如果需要让它们可以访问私有资源(例如VPC内的MySQL数据库或通过专用端点将访问权限限制为特定VPC的S3存储桶),则可以在VPC中运行这些函数。但是,在VPC中运行的缺点是冷启动延迟比不在VPC中运行要高(因为必须附加ENI)。公共子网和私有子网之间的区别在于路由(默认路由0.0.0.0/0指向IGW还是NAT)。如果您的Lambda函数需要对外部进行访问,则不能在公共子网中运行,因为默认路由是IGW。 - jarmod
4
谢谢!我之前没有意识到只有私有子网才能拥有NAT网关,现在这点很清楚了。顺便说一下,当我深入学习时,我发现实际上可以使lambda函数从公共子网访问互联网,但需要将弹性IP附加到其ENI上。虽然这听起来有点傻,但了解这点也是好的。 - LLL
3
一切又再度发生变化。AWS在冷启动和ENI消耗问题方面进行了倾听,并为Lambda提供了对Hyperplain的支持,您仍需添加适当的IAM权限,但看起来改进显著:https://aws.amazon.com/blogs/compute/announcing-improved-vpc-networking-for-aws-lambda-functions/ - Aardvark
2
谢谢!这是一个非常清晰的解释。 - Ken Colton
显示剩余12条评论

18
你可以使Lambda函数从VPC中访问公共互联网。方案A是正确答案,方案B是一个更优雅的替代方案。

方案A - Lambda位于VPC + 与ENI关联的公共IP

要访问AWS外部的资源(例如OP的示例中的Google API),您确实需要公共IP。对于其他情况,如RDS或S3,您不需要公共IP,可以使用VPC端点,因此Lambda和所需的AWS服务之间的通信不会离开AWS网络。

默认情况下,确实可以通过公共互联网访问某些AWS服务,但这并非必须。

如果要访问实际的外部资源(例如Google),则需要为与Lambda关联的每个子网的网络接口分配弹性公共IP。首先让我们确定与Lambda相关联的子网和安全组:

Lambda screen

接下来,进入EC2服务,在“网络和安全性”下找到“公共IP”菜单。为每个子网分配一个IP(在上面的示例中有两个子网)。

转到“网络接口”菜单,找到附加到Lambda的网络接口(相同的子网和安全组)。

Network Interfaces

在每个操作菜单中关联公共IP:

Actions menu

Associate IP

这就是了,现在您的Lambda可以访问公共互联网了。

[编辑] 有人对方案A的可伸缩性问题表示担忧,称每个Lambda实例都有一个新的网络接口,但他们错过了AWS文档中的这一点

"如果函数共享相同的子网和安全组,则多个Lambda函数可以共享网络接口"

因此,您可能会面临与此解决方案和Lambda如何使用ENI以及EC2、ECS、EKS等不同的问题相同的可伸缩性问题。


方案B - 拆分为多个Lambda

需要访问外部资源和VPC资源对于单个函数来说似乎是过重的责任。您可能需要重新考虑设计并将单个lambda函数分解成至少两个lambda函数:
- Lambda A 访问外部资源(例如Google API),提取所需的数据,添加到SQS。不需要连接到VPC,也不需要手动将弹性公共IP关联到ENI。 - Lambda B 处理来自SQS的消息,将结果存储到存储区域(数据库,S3,EFS,另一个队列等)。此函数位于您的VPC中,无需外部访问。
这种方式更具可扩展性,更安全,每个单独的lambda函数都较简单且易于维护,整体架构看起来更好。
当然,生活并不总是一帆风顺,因此方案A已经足够好和可扩展,但是改进架构会更好。

1
有人知道AWS什么时候允许这个吗?我发现很多“不起作用”的材料(这些都是旧的),但我也看到现在这个可以工作(至少在2021年之后的某个时间)。 - Augunrik
1
我认为这种方法在使用lambda时不太可靠,因为当它扩展时,每个lambda实例都有一个新的网络接口。因此,虽然你可以这样做,但在扩展时它不会起作用。 - Capaj
从AWS文档中得到的信息显示,如果多个Lambda函数使用相同的子网和安全组,则它们可以共享网络接口。此外,无论是Lambda、EC2、ECS还是NAT Gateway,您都必须拥有公共IP才能访问公共互联网。即使是通过NAT Gateway访问公共互联网,也需要具备弹性公共IP。 - Alisson Reinaldo Silva
@Veer 如果您需要访问其中的资源,只有在这种情况下才需要显式地将VPC附加到您的Lambda。如果您需要外部资源(AWS之外),则只需要Public IP(通过NAT或ENI)。进一步改进的一种方法是尝试将Lambda分解为两个较小的Lambda,一个用于访问外部资源,另一个用于VPC资源。例如,Lambda A从外部API获取数据,将输出添加到SQS,Lambda B处理消息并存储到数据库中。我会更新我的答案,并提出此建议。 - Alisson Reinaldo Silva
我一直在开发一个“测试”Lambda函数,需要连接到我的本地环境。我原以为每次需要处理它时都必须暂时创建一个NAT网关,然后在完成后销毁NAT网关。解决方案A要好得多!但在关联表单中,为什么只有一个弹性IP和一个私有地址可供选择? - ScottyB
显示剩余2条评论

1
有点晚了,但还是要说一下。@jarmod的答案是正确的,但我想进一步发展一下。关于Lambda函数无法访问Internet的原因解释如下:
Lambda函数在AWS管理的虚拟私有云(VPC)中运行。当您在Lambda函数和指定的VPC之间建立“连接”时,会在选择的子网中生成一个弹性网络接口(ENI),具体来说是一个Hyperplane ENI,用于Lambda函数的执行。该ENI假定一个“私有”IP,将Lambda函数的所有网络流量通过它进行传输。那么,为什么没有分配公共IP呢?
这是因为AWS中公共IP的分配方式。EC2实例的公共IP配置不驻留在实例本身上,而是通过网络地址转换(NAT)Internet Gateway(IGW)中进行编排。因此,在公共子网中部署EC2实例并不能自动获得公共IP。对于Lambda函数也是如此,但与EC2实例不同的是,Lambda函数缺乏手动分配公共IP的能力。
希望对您有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接