【问题标题】:How to invoke an AWS Lambda function from EC2 instances with python asyncio如何使用 python asyncio 从 EC2 实例调用 AWS Lambda 函数
【发布时间】:2017-09-03 16:35:00
【问题描述】:

我最近发布了一个关于How to allow invoking an AWS Lambda function only from EC2 instances inside a VPC 的问题。 我设法通过将具有“AWS lambda 角色”策略的 IAM 角色附加到 EC2 实例来使其工作,现在我可以使用 boto3 调用 lambda 函数。

现在,我想使用 asyncio await 语法异步调用 lambda 函数。我读到 lambda 函数通过设置 InvokeType='Event' 提供了一个异步版本,但这实际上使调用立即返回而没有得到函数的结果。

由于该函数需要一些时间,并且我想并行启动许多函数,因此我希望避免在等待函数返回时阻塞执行。

我尝试使用 aiobotocore,但它只支持基本的“s3”服务功能。

解决这个问题的最佳方法(以拙见)是使用 AWS API Gateway 服务通过 GET/POST 请求调用 lambda 函数,该请求可以使用 aiohttp 轻松处理。

不过我还是没能成功。

我向 EC2 IAM 角色添加了策略“AmazonAPIGatewayInvokeFullAccess”,但每次我尝试:

import requests
r = requests.get('https://url_to_api_gateway_for_function')

我收到了禁止回复<Response [403]>

我直接使用 lambda 函数中的触发器创建了 API 网关。

我还尝试编辑 API Gateway 设置,方法是在函数路径中添加一个 post 方法并设置“AWS_IAM”身份验证,然后将其部署为“prod”部署......不走运。仍然是相同的禁止响应。当我通过“API 网关上的测试屏幕进行测试时,它工作正常”。

知道如何解决这个问题吗?我错过了一些步骤吗?

【问题讨论】:

  • docs.aws.amazon.com/apigateway/latest/developerguide/…you can use or customize the managed policy of AWSLambdaFullAccess (arn:aws:iam::aws:policy/AWSLambdaFullAccess) and attach it to the IAM user
  • 我尝试了该教程,并且只有当我没有将 AWS_IAM 设置为 API 的授权时,我才设法使其工作......只要我打开它,从即使我对 EC2 实例使用了策略 AWSLambdaFullAccess...,EC2 我也会收到 403 响应
  • 什么是 AWS_IAM 授权?
  • 请参阅此链接上的第 5 点:docs.aws.amazon.com/apigateway/latest/developerguide/… 我认为我的问题是“请求”无法获取我的 EC2 实例的 IAM 授权,因此无法签署对 API 网关的请求。从这个意义上说,当我打开 AWS_IAM 授权以避免外部用户调用 lambda 时,一切都变得废话......
  • 取决于作者,部分 AWS 文档可能会或可能不会通过链接告诉您“预申请”。文档页面顶部提到了解释 API 网关权限要求的部分:docs.aws.amazon.com/apigateway/latest/developerguide/…

标签: python-3.x amazon-web-services aws-lambda boto3 python-asyncio


【解决方案1】:

经过一番挣扎,我设法解决了我的问题。

问题在于 curl 和 python 模块(如 python 的请求)不会使用运行它们的 EC2 机器的 IAM 凭证对 http 请求进行签名。对 AWS GATEWAY API 的 http 请求必须使用 AWS v4 登录协议进行签名。

这里有一个例子: http://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html

幸运的是,为了简单起见,有一些辅助模块,例如 requests-aws-sign: https://github.com/jmenga/requests-aws-sign

最后,代码可能如下所示:

import aiohttp
import asyncio
from requests_aws_sign import AWSV4Sign
from boto3 import session

session = session.Session()
credentials = session.get_credentials()
region = session.region_name or 'ap-southeast-2'
service = 'execute-api'
url = "get_it_from_api->stages->your_deployment->invoke_url"
auth=AWSV4Sign(credentials, region, service)

async def invoke_func(loop):
    async with aiohttp.request('GET', url, auth=auth, loop=loop) as resp:
        html = await resp.text()
        print(html)

loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))

希望这会为其他人节省时间!

编辑:

为了完整和帮助他人,我不得不说上面的代码不起作用,因为 requests_aws_sign 与 aiohttp 不兼容。我收到了一些“身份验证字段错误”。

我设法通过使用来解决它:

async with session.get(url, headers=update_headers()) as resp:

其中 update_headers() 是一个简单的函数,它模仿 requests_aws_sign 对标头所做的事情(这样我就可以使用标头参数将它们直接设置为上面的请求)。 它看起来像这样:

def update_headers(sim_id):
    url = urlparse("get_it_from_api->stages->your_deployment->invoke_url")
    path = url.path or '/'
    querystring = ''
    if url.query:
        querystring = '?' + urlencode(parse_qs(url.query), doseq=True)
    safe_url = url.scheme + '://' + url.netloc.split(':')[0] + path + querystring
    request = AWSRequest(method='GET', url=safe_url)
    SigV4Auth(credentials, service, region).add_auth(request)
    return dict(request.headers.items())

【讨论】:

  • update_headers 函数中的 sim_id 参数是什么?
猜你喜欢
  • 1970-01-01
  • 2015-07-13
  • 1970-01-01
  • 1970-01-01
  • 2021-02-24
  • 2020-12-18
  • 2018-02-01
  • 2020-06-26
  • 2021-09-19
相关资源
最近更新 更多