【发布时间】:2020-02-13 18:41:25
【问题描述】:
我有一个需要访问 AWS API 的 Python lambda。如果它不与 VPC 子网关联,则它可以工作。但是,当它与 VPC 子网关联时,它会收到异常 botocore.exceptions.EndpointConnectionError 和消息 Could not connect to the endpoint URL: "https://ec2.us-east-1.amazonaws.com/"。我见过here 和here 描述的这种问题,通常是由于缺少 NAT 网关路由引起的。但是,我有all the correct "pieces",它仍然不起作用。
我拥有的是:
- 具有创建两个逻辑 ENI 的关联的 Lambda:
- 两个私有子网,每个 AZ 一个。
- 一个完全允许的安全组(允许所有入站/出站流量)
- 私有子网具有自定义路由表,其中路由将 0.0.0.0/0 发送到 NAT 网关
- 每个 NAT 网关都位于一个公共子网中(每个 AZ 一个),该子网有一个自定义路由表,其中路由将 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 接受确定
除此之外没有其他 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}')
上面会抛出一个带有消息urlopen error [Errno -3] Temporary failure in name resolution 的URLError。
然后,我在我的 VPC 的公共子网中创建了一个 Ubuntu EC2 实例。对 google.com 的 ping 测试因“未知主机”而失败。 如果我明确提供一个公共互联网 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
如果我明确地将 dig 指向公共 DNS 服务器,我可以使它成功。这有效: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 选项集,和上面一样,但是我将 DNS 服务器更改为 8.8.8.8 和 8.8.4.4 并将其关联到 VPC。然后我修改了我的 lambda 以输出 /etc/resolv.conf 的内容,以验证它“占用”了新的 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 解析”和“DNS 主机名”,enabled。
为什么 DNS 无法正常工作?如图所示,我使用的是自己的 DNS 服务器还是 google 提供的服务器都没有关系。
【问题讨论】:
-
您能否通过来自同一个 Lambda 的 HTTP 请求访问非 AWS 端点?如果您只有一个子网/安全组组合(因此只有一个 ENI),它是否有效?同一子网中具有相同 SG 的 EC2 实例能否访问公共端点?
-
@jarmod 我在我的问题中添加了
Update部分。会喜欢你的想法。 -
@jarmod 我又更新了。我的网络代理问题干扰了我的测试。现在我的问题包含有关 VPC 中 DNS 问题的更新。
-
您使用的是自定义 DHCP 选项集还是 VPC 默认选项?您可能需要进一步调试。检查 dig,检查 /etc/resolv.conf 设置等。
-
@jarmod 根据您的提示,我已经提供了更多信息。
标签: amazon-web-services aws-lambda amazon-vpc