问题描述
我想要一个lambda(变压器)来调用另一个lambda(源),而无需通过lambda.invoke
,而只是通过再一次(专用)api网关来请求它。背景是这样可以进行更直接的调试,测试和开发(不依赖主要功能中的AWS设施)。在本地运行transfomer可以正常工作(容器可以请求api网关,在这种情况下,我将aws_api_root_uri
替换为http://host.docker.internal:3000
,这将为源lambda启动另一个容器)。但是,在AWS上部署后尝试相同的操作最终还是
HTTPSConnectionPool(host='<id>.execute-api.eu-central-1.amazonaws.com',port=443): Max retries exceeded with url: /sandBox/sources/test (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f73ebda3790>: Failed to establish a new connection: [Errno -2] Name or service not kNown'))
这似乎是访问私有资源的DNS问题。在同一Lambda上使用公共互联网可以正常使用,即可以要求google.com
。另一方面,访问在lambda中产生错误的相同URI,但在同一VPC中的EC2实例上,也可以正常工作。
这排除了几件事
- 不能是一般的DNS协议访问问题
- lambda源可用,可以按预期在网络上请求
我是否需要添加VPC EC2实例隐式具有的特定策略,但是该VPC上的lambda不需要呢?如果是这样,那将是哪个政策?还是您会想到导致此问题的另一个问题?
在Transfomer URI上调用GET时预期的响应
{"message": "hello world","location": "<ip>","added": "value"}
实际反应
{"message": "Internal server error"}
template.yaml
AWstemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
SAM template
Globals:
Function:
Timeout: 10 # need a relatively long timeout for the local deployment - two container spin-ups in series needed
Parameters:
MyEndpointId:
Type: String
Description: The AWS endpoint ID for the API Gateway endpoint
Resources:
MyTestSourceFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.8
CodeUri: data_sources/test_source
Handler: service.lambda_handler.handle_lambda
Events:
MyTestSourceApi:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /sources/test
Method: get
MyTestTransformerFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.8
CodeUri: data_transformers/test_transformer
Handler: service.lambda_handler.handle_lambda
Events:
MyTestTransformerApi:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /transformers/test
Method: get
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: sandBox
MethodSettings:
- HttpMethod: '*'
ResourcePath: /*/*/*
LoggingLevel: ERROR
ThrottlingBurstLimit: 5000
ThrottlingRateLimit: 10000
EndpointConfiguration:
Type: PRIVATE
VPCEndpointIds:
- !Ref MyEndpointId
Auth:
ResourcePolicy:
CustomStatements: [{
"Effect": "Allow","Principal": "*","Action": "execute-api:Invoke","Resource": "execute-api:/*/*/*/*"
}]
data_sources / test_source / service / lambda_handler.py
import json
from .main import generate_test_data
def handle_lambda(event,context):
return {
"statusCode": 200,"body": json.dumps(generate_test_data()),}
data_sources / test_source / service / main.py
import requests
def generate_test_data():
try:
ip_address = requests.get("http://checkip.amazonaws.com/").text.replace("\n","")
except requests.RequestException as exc:
print(exc)
raise exc
return {
"message": "hello world","location": ip_address
}
data_transformers / test_transformer / service / lambda_handler.py
import os
import json
from .main import load_and_transform
def handle_lambda(event,context):
aws_api_root_uri = "https://{}.execute-api.{}.amazonaws.com/{}/".format(
event['requestContext']['apiId'],os.environ['AWS_REGION'],event['requestContext']['stage']
)
return {
"statusCode": 200,"body": json.dumps(load_and_transform(aws_api_root_uri)),}
data_transformers / test_transformer / service / main.py
import urllib.parse
import requests
def load_and_transform(api_root_path):
test_data = {}
try:
# do we have DNS working?
print(requests.get("https://www.google.com/").text)
# can we receive data from another lambda?
uri = urllib.parse.urljoin(api_root_path,"sources/test")
print(f"test transfomer is sending query to {uri}")
test_data = requests.get(uri).json()
test_data["added"] = "value"
except Exception as exc:
print(exc)
raise exc
return test_data
解决方法
@Corin,谢谢您提供正确的提示!我缺少lambda函数上的VPC配置以及使它起作用的策略:
其他参数
Parameters:
MySubnet1:
Type: String
Description: The subnet to use for lambda functions in the first availability zone
MySG:
Type: String
Description: The security group to be used for the lambda functions
其他全局变量
通过这种方式,可以在所有lambda之间共享VPC配置
Globals:
Function:
VpcConfig:
SubnetIds:
- !Ref MySubnet1
SecurityGroupIds:
- !Ref MySG
每个lambda的其他政策
否则,VPC配置将失败,因为Lambda不能获得对网络执行任何操作的权限
Resources:
MyLambdaFunction:
Properties:
Policies:
- VPCAccessPolicy: {}