AWS Lambda请求私有API网关后面的另一个lambda-DNS解析不起作用 其他参数其他全局变量每个lambda的其他政策

问题描述

我想要一个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: {}