VPC 中的 Lambda 与 AWS 连接错误 API
Lambda in VPC has Connection Error against AWS API
我有一个 Python lambda 需要访问 AWS API。如果它不与 VPC 子网相关联,它就可以工作。但是当它与 VPC 子网关联时,它会收到异常 botocore.exceptions.EndpointConnectionError
和消息 Could not connect to the endpoint URL: "https://ec2.us-east-1.amazonaws.com/"
。我见过 and , usually caused by a missing NAT gateway route. However, I have all the correct "pieces" 描述的此类问题,但 仍然 不起作用。
我有的是:
- 具有创建两个逻辑 ENI 的关联的 Lambda:
- 两个私有子网,每个 AZ 一个。
- 一个完全允许的安全组(允许所有 inbound/outbound 流量)
- 私有子网有自定义路由 tables,路由发送 0.0.0.0/0 到 NAT 网关
- 每个 NAT 网关都在一个 public 子网(每个 AZ 一个)中,该子网有一个自定义路由 table,该路由将 0.0.0.0/0 发送到 VPC 的 IGW。
- 所有子网都与允许 所有 入站和出站流量的 NACL 关联。
当我检查流日志时,我看到 lambda ENI 成功发起了 DNS 请求(端口 53),如下所示:
2 405857719141 eni-03bb24a034d226e5c 10.136.95.104 10.136.7.233 38109 53 17 1 73 1571250675 1571250733 ACCEPT OK
除此之外没有其他 VPC 流日志记录...没有任何迹象表明 "REJECTED"。我的实际 Python 代码在 lambda 未与 VPC 关联时有效,看起来像这样:
def lambda_handler(event, context):
from botocore.client import Config
from botocore.session import Session
logger.info(f'Create Session')
s = Session()
logger.info(f'Session Created')
logger.info(f'fetching client')
ec2_res = boto3.resource('ec2')
logger.info(f'got vpc resource')
#I've tried different approaches to creating a client
ec2_client = s.create_client('ec2',config=Config(connect_timeout=45, read_timeout=45, retries={'max_attempts': 0}))
#ec2_client = boto3.client('ec2', config=config)
#ec2_client = boto3.client('ec2', endpoint_url="https://aws.amazon.com/ec2",config=config)
#ec2_client = boto3.client('ec2',endpoint_url=endpoint)
logger.info(f'fetched_client')
route_table_id = os.environ['fromTGWRouteTableId']
logger.info(f'got route table id from environment')
try:
logger.info(f'route table(s):{route_table_id}')
#this request will throw an exception in 40 seconds.
route_table = ec2_client.describe_route_tables(RouteTableIds=[route_table_id])
logger.info(f'got client response for route_tables')
rt = route_table['RouteTables'][0]
logger.info(f'The RT ID is: {rt.id}')
except Exception as e:
logger.info(f'{type(e)}')
logger.info(f'{e}')
return
我不得不调整 lambda 和 boto3 客户端超时 恰到好处 以实际捕获错误。
其他任何事情都会导致超时。以下是 lambda 的 CloudWatch 日志条目:
START RequestId: a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 Version: $LATEST
[INFO] 2019-10-16T20:22:38.914Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 Create Session
[INFO] 2019-10-16T20:22:39.36Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 Session Created
[INFO] 2019-10-16T20:22:39.92Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 fetching client
[INFO] 2019-10-16T20:22:39.94Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 Found credentials in environment variables.
[INFO] 2019-10-16T20:22:40.33Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 got vpc resource
[INFO] 2019-10-16T20:22:40.33Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 fetched_client
[INFO] 2019-10-16T20:22:40.33Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 got route table id from environment
[INFO] 2019-10-16T20:22:40.33Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 route table(s):rtb-0d92f4db98072d6fc
[INFO] 2019-10-16T20:22:49.493Z bd2bf6b7-2fa6-46ea-8115-cb830cb07f32 <class 'botocore.exceptions.EndpointConnectionError'>
[INFO] 2019-10-16T20:22:49.493Z bd2bf6b7-2fa6-46ea-8115-cb830cb07f32 Could not connect to the endpoint URL: "https://ec2.us-east-1.amazonaws.com/"
END RequestId: bd2bf6b7-2fa6-46ea-8115-cb830cb07f32
REPORT RequestId: bd2bf6b7-2fa6-46ea-8115-cb830cb07f32 Duration: 40960.97 ms Billed Duration: 41000 ms Memory Size: 128 MB Max Memory Used: 83 MB
2 unknown eni-07003b087845964ff - - - - - - - 1571257388 1571257400 - NODATA
有什么我忽略的想法吗?
更新
在我的 Python 代码中,我添加了以下测试:
contents = urllib.request.urlopen("https://google.com").readline()
logger.info(f'http response: {contents}')
以上抛出 URLError 消息 urlopen error [Errno -3] Temporary failure in name resolution
。
然后我在我的 VPC 的 public 子网中创建了一个 Ubuntu EC2 实例。对 google.com 的 ping 测试因 "unknown host" 而失败。 如果我明确提供 public 互联网 IP 地址进行 ping,那么它就可以工作。
同样host
和dig
失败,如图:
ubuntu@ip-10-136-80-220:/etc$ host google.com
;; connection timed out; no servers could be reached
ubuntu@ip-10-136-80-220:/etc$ dig google.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> google.com
;; global options: +cmd
;; connection timed out; no servers could be reached
如果我明确地将它指向 public DNS 服务器,我可以使 dig
成功。这有效:dig @8.8.8.8 google.com
.
以下是我的resolv.conf的内容,真实的公司名称被"mycompany.com"屏蔽:
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 10.136.7.233
nameserver 10.136.7.249
search preprod.awse1.mycompany.com
以上对应以下DHCP选项集。
domain-name = preprod.awse1.mycompany.com; domain-name-servers = 10.136.7.233, 10.136.7.249;
我认为上述两个 DNS 服务器是在不同的 AWS 账户中提供的。尽管如此,ping
测试在这两个 DNS 服务器地址上都失败了。我不确定这是否意味着这些服务器不存在,或者它们是否根本不响应 ICMP。
刚才我创建了自己的DHCP Option Set,和上面一样,但是我把DNS服务器改成了8.8.8.8和8.8.4.4,并关联到VPC上。然后我修改了我的 lambda 以输出 /etc/resolv.conf 的内容来验证它 "took" 新的 8.8.8.8/8.8.4.4 DNS 服务器 - 而 lambda 仍然有相同的 DNS 错误!非常奇怪的是,来自 EC2 实例的显式 dig @8.8.8.8 google.com
有效,但与同一子网关联的 lambda 出现 DNS 错误。我想知道与 Lambda 关联的临时 ENI 是否有自己的 DNS 服务器记录 - 并且它们的更新速度不够快以反映我对我的 lambda 所做的更改?
顺便说一下,VPC 有 "DNS resolution" 和 "DNS hostnames" 两个 enabled
。
为什么 DNS 不工作?如图所示,无论我使用自己的 DNS 服务器还是 google.
提供的服务器都无关紧要
我现在已经解决了这个问题。问题的核心是我的 VPC 的 DHCP 选项集要求位于不同 VPC 中的 DNS 服务器。我启用了 VPC 的 lambda 与一个子网相关联,该子网没有路由 table 条目来专门寻址 DNS。我需要的是路由 table 中的一条路由,它将通过中转网关指向那些 DNS 服务器地址以到达另一个 VPC。相反,我的子网有一条路由将 DNS 请求定向到 public 互联网(毫不奇怪,它没有找到到另一个 VPC 的路径)。
我的 VPC 中还有另一个子网,它确实有一条路由 table 和指向中转网关的路由。这个其他子网也有资格获得相关的 lambda。因此,仅更改我的 lambda 使用的子网就足以使整个过程正常运行。
发现此问题的根源受到其他因素的阻碍,例如 AWS Flow Logs 显示 "ACCEPT / OK" 的 DNS 请求 - 实际上它们没有工作。我需要更好地掌握解释流日志(并在适当的时候忽略它们)。
我有一个 Python lambda 需要访问 AWS API。如果它不与 VPC 子网相关联,它就可以工作。但是当它与 VPC 子网关联时,它会收到异常 botocore.exceptions.EndpointConnectionError
和消息 Could not connect to the endpoint URL: "https://ec2.us-east-1.amazonaws.com/"
。我见过
我有的是:
- 具有创建两个逻辑 ENI 的关联的 Lambda:
- 两个私有子网,每个 AZ 一个。
- 一个完全允许的安全组(允许所有 inbound/outbound 流量)
- 私有子网有自定义路由 tables,路由发送 0.0.0.0/0 到 NAT 网关
- 每个 NAT 网关都在一个 public 子网(每个 AZ 一个)中,该子网有一个自定义路由 table,该路由将 0.0.0.0/0 发送到 VPC 的 IGW。
- 所有子网都与允许 所有 入站和出站流量的 NACL 关联。
当我检查流日志时,我看到 lambda ENI 成功发起了 DNS 请求(端口 53),如下所示:
2 405857719141 eni-03bb24a034d226e5c 10.136.95.104 10.136.7.233 38109 53 17 1 73 1571250675 1571250733 ACCEPT OK
除此之外没有其他 VPC 流日志记录...没有任何迹象表明 "REJECTED"。我的实际 Python 代码在 lambda 未与 VPC 关联时有效,看起来像这样:
def lambda_handler(event, context):
from botocore.client import Config
from botocore.session import Session
logger.info(f'Create Session')
s = Session()
logger.info(f'Session Created')
logger.info(f'fetching client')
ec2_res = boto3.resource('ec2')
logger.info(f'got vpc resource')
#I've tried different approaches to creating a client
ec2_client = s.create_client('ec2',config=Config(connect_timeout=45, read_timeout=45, retries={'max_attempts': 0}))
#ec2_client = boto3.client('ec2', config=config)
#ec2_client = boto3.client('ec2', endpoint_url="https://aws.amazon.com/ec2",config=config)
#ec2_client = boto3.client('ec2',endpoint_url=endpoint)
logger.info(f'fetched_client')
route_table_id = os.environ['fromTGWRouteTableId']
logger.info(f'got route table id from environment')
try:
logger.info(f'route table(s):{route_table_id}')
#this request will throw an exception in 40 seconds.
route_table = ec2_client.describe_route_tables(RouteTableIds=[route_table_id])
logger.info(f'got client response for route_tables')
rt = route_table['RouteTables'][0]
logger.info(f'The RT ID is: {rt.id}')
except Exception as e:
logger.info(f'{type(e)}')
logger.info(f'{e}')
return
我不得不调整 lambda 和 boto3 客户端超时 恰到好处 以实际捕获错误。 其他任何事情都会导致超时。以下是 lambda 的 CloudWatch 日志条目:
START RequestId: a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 Version: $LATEST
[INFO] 2019-10-16T20:22:38.914Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 Create Session
[INFO] 2019-10-16T20:22:39.36Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 Session Created
[INFO] 2019-10-16T20:22:39.92Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 fetching client
[INFO] 2019-10-16T20:22:39.94Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 Found credentials in environment variables.
[INFO] 2019-10-16T20:22:40.33Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 got vpc resource
[INFO] 2019-10-16T20:22:40.33Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 fetched_client
[INFO] 2019-10-16T20:22:40.33Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 got route table id from environment
[INFO] 2019-10-16T20:22:40.33Z a3ce5c07-f5ec-4b91-b6d8-c94e05fbecc9 route table(s):rtb-0d92f4db98072d6fc
[INFO] 2019-10-16T20:22:49.493Z bd2bf6b7-2fa6-46ea-8115-cb830cb07f32 <class 'botocore.exceptions.EndpointConnectionError'>
[INFO] 2019-10-16T20:22:49.493Z bd2bf6b7-2fa6-46ea-8115-cb830cb07f32 Could not connect to the endpoint URL: "https://ec2.us-east-1.amazonaws.com/"
END RequestId: bd2bf6b7-2fa6-46ea-8115-cb830cb07f32
REPORT RequestId: bd2bf6b7-2fa6-46ea-8115-cb830cb07f32 Duration: 40960.97 ms Billed Duration: 41000 ms Memory Size: 128 MB Max Memory Used: 83 MB
2 unknown eni-07003b087845964ff - - - - - - - 1571257388 1571257400 - NODATA
有什么我忽略的想法吗?
更新
在我的 Python 代码中,我添加了以下测试:
contents = urllib.request.urlopen("https://google.com").readline()
logger.info(f'http response: {contents}')
以上抛出 URLError 消息 urlopen error [Errno -3] Temporary failure in name resolution
。
然后我在我的 VPC 的 public 子网中创建了一个 Ubuntu EC2 实例。对 google.com 的 ping 测试因 "unknown host" 而失败。 如果我明确提供 public 互联网 IP 地址进行 ping,那么它就可以工作。
同样host
和dig
失败,如图:
ubuntu@ip-10-136-80-220:/etc$ host google.com
;; connection timed out; no servers could be reached
ubuntu@ip-10-136-80-220:/etc$ dig google.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> google.com
;; global options: +cmd
;; connection timed out; no servers could be reached
如果我明确地将它指向 public DNS 服务器,我可以使 dig
成功。这有效:dig @8.8.8.8 google.com
.
以下是我的resolv.conf的内容,真实的公司名称被"mycompany.com"屏蔽:
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 10.136.7.233
nameserver 10.136.7.249
search preprod.awse1.mycompany.com
以上对应以下DHCP选项集。
domain-name = preprod.awse1.mycompany.com; domain-name-servers = 10.136.7.233, 10.136.7.249;
我认为上述两个 DNS 服务器是在不同的 AWS 账户中提供的。尽管如此,ping
测试在这两个 DNS 服务器地址上都失败了。我不确定这是否意味着这些服务器不存在,或者它们是否根本不响应 ICMP。
刚才我创建了自己的DHCP Option Set,和上面一样,但是我把DNS服务器改成了8.8.8.8和8.8.4.4,并关联到VPC上。然后我修改了我的 lambda 以输出 /etc/resolv.conf 的内容来验证它 "took" 新的 8.8.8.8/8.8.4.4 DNS 服务器 - 而 lambda 仍然有相同的 DNS 错误!非常奇怪的是,来自 EC2 实例的显式 dig @8.8.8.8 google.com
有效,但与同一子网关联的 lambda 出现 DNS 错误。我想知道与 Lambda 关联的临时 ENI 是否有自己的 DNS 服务器记录 - 并且它们的更新速度不够快以反映我对我的 lambda 所做的更改?
顺便说一下,VPC 有 "DNS resolution" 和 "DNS hostnames" 两个 enabled
。
为什么 DNS 不工作?如图所示,无论我使用自己的 DNS 服务器还是 google.
提供的服务器都无关紧要我现在已经解决了这个问题。问题的核心是我的 VPC 的 DHCP 选项集要求位于不同 VPC 中的 DNS 服务器。我启用了 VPC 的 lambda 与一个子网相关联,该子网没有路由 table 条目来专门寻址 DNS。我需要的是路由 table 中的一条路由,它将通过中转网关指向那些 DNS 服务器地址以到达另一个 VPC。相反,我的子网有一条路由将 DNS 请求定向到 public 互联网(毫不奇怪,它没有找到到另一个 VPC 的路径)。
我的 VPC 中还有另一个子网,它确实有一条路由 table 和指向中转网关的路由。这个其他子网也有资格获得相关的 lambda。因此,仅更改我的 lambda 使用的子网就足以使整个过程正常运行。
发现此问题的根源受到其他因素的阻碍,例如 AWS Flow Logs 显示 "ACCEPT / OK" 的 DNS 请求 - 实际上它们没有工作。我需要更好地掌握解释流日志(并在适当的时候忽略它们)。