【问题标题】:AWS Lambda connection to SQS timed outAWS Lambda 与 SQS 的连接超时
【发布时间】:2020-11-14 08:04:13
【问题描述】:

我正在处理一项涉及 Lambda 函数 在 VPC 中运行的任务。

这个函数应该将消息推送到SQS,并且lambda执行角色有策略:AWSLambdaSQSQueueExecutionRoleAWSLambdaVPCAccessExecutionRole添加。

Lambda 函数:

# Create SQS client
sqs = boto3.client('sqs')

queue_url = 'https://sqs.ap-east-1a.amazonaws.com/073x08xx43xx37/xyz-queue'

# Send message to SQS queue
response = sqs.send_message(
    QueueUrl=queue_url,
    DelaySeconds=10,
    MessageAttributes={
        'Title': {
            'DataType': 'String',
            'StringValue': 'Tes1'
        },
        'Author': {
            'DataType': 'String',
            'StringValue': 'Test2'
        },
        'WeeksOn': {
            'DataType': 'Number',
            'StringValue': '1'
        }
    },
    MessageBody=(
        'Testing'
     )
)

print(response['MessageId'])

测试执行结果如下:

{
  "errorMessage": "2020-07-24T12:12:15.924Z f8e794fc-59ba-43bd-8fee-57f417fa50c9 Task timed out after 3.00 seconds"
}

我将超时从基本设置增加到 5 秒和 10 秒为好。但是错误不断出现。

如果有人过去遇到过类似的问题或想知道如何解决这个问题,请帮助我。

提前谢谢你。

【问题讨论】:

  • 您是否有允许您的 Lambda 连接到 SQS 的 VPC 终端节点或 NAT?
  • 是的。我已经添加了一个 NAT 安全组。

标签: amazon-web-services aws-lambda boto3 amazon-sqs amazon-vpc


【解决方案1】:

当 AWS Lambda 函数配置为使用 Amazon VPC 时,它会连接到 VPC 的指定子网。这允许 Lambda 函数与 VPC 内的其他资源进行通信。但是,它无法与 Internet 通信。这是一个问题,因为 Amazon SQS 公共终端节点位于 Internet 上,并且该函数由于无法访问 Internet 而超时。

因此,您有 3 个选项:

选项 1:不要连接到 VPC

如果您的 Lambda 函数不需要与 VPC 中的资源通信(例如您上面提供的简单函数),只需 不要将其连接到 VPC。当 Lambda 函数连接到 VPC 时,它可以与 Internet 和 Amazon SQS 公共端点进行通信。

选项 2:使用 VPC 端点

VPC 终端节点提供了一种访问 AWS 服务的方法无需通过 Internet。您将为 Amazon SQS 配置 VPC endpoint。然后,当 Lambda 函数希望连接 SQS 队列时,它可以通过端点而不是通过 Internet 访问 SQS。如果 Lambda 函数需要与 VPC 中的其他资源进行通信,这通常是一个不错的选择。

选项 3:使用 NAT 网关

如果 Lambda 函数配置为使用私有子网,那么如果 已在公有子网中配置了 NAT 网关和私有子网点的路由表,它将能够访问 Internet到 NAT 网关。这需要额外的费用,只有在需要额外的 NAT 网关时才值得。

【讨论】:

  • 感谢您的解释!这真的很翔实。我正在使用 NAT 网关(选项 3)。我添加了 VPC、公共子网和 NAT 安全组(我已在其中配置了入站和出站规则)。我想这应该允许 Lambda 连接到 SQS ?不幸的是,我仍然收到超时错误。如果我遗漏了什么,请告诉我。
  • 确保与私有子网(尤其是具有 Lambda 函数的子网)关联的路由表配置为将目的地为 0.0.0.0/0 的流量重定向到 NAT 网关。 NAT 网关上的安全组应允许来自私有子网(或整个 VPC)的任何入站流量,并允许所有出站流量。
  • 更多关于选项 2 的信息可以在这里找到docs.aws.amazon.com/AWSSimpleQueueService/latest/…
【解决方案2】:

如果您在 VPC 的 lambda 中使用 boto3 python 库,并且无法通过 vpc 端点连接到 sqs 队列,则必须在创建 sqs 客户端时设置 endpoint_url。 Issue 1900 描述了这背后的背景。

解决方案如下所示(对于 us-east-1 中的 sqs vpc 端点):

sqs_client = boto3.client('sqs',
    endpoint_url='https://sqs.us-east-1.amazonaws.com')

然后照常调用 send_message 或 send_message_batch。

【讨论】:

  • 我花了一天的时间在这个问题上问了this question,然后才找到你的答案。谢谢!
【解决方案3】:

我非常确信,您不能使用 Lambda 使用 SQS 端点从 VPC 中调用 SQS 队列。我认为这是一个错误,但也许 Lambda 团队这样做是有原因的。在任何情况下,您都会收到消息超时。我做了一个简单的测试 Lambda

import json
import boto3
import socket

def lambda_handler(event, context):
    print('lambda-test SQS...')
    sqsDomain='sqs.us-west-2.amazonaws.com'
    
    addr1 = socket.gethostbyname(sqsDomain)
    print('%s=%s' %(sqsDomain, addr1))
     
    print('Creating sqs client...')
    sqs = boto3.client('sqs')
    
    print('Sending Test Message...')
    response = sqs.send_message(
            QueueUrl='https://sqs.us-west-2.amazonaws.com/1234567890/testq.fifo',
            MessageBody='Test SQS Lambda!',
            MessageGroupId='test')
            
    print('SQS send response: %s' % response)

    return {
        'statusCode': 200,
        'body': json.dumps(response)
    }

我根据 Configuring a Lambda function to access resources in a VPC 创建了一个 VPC、子网等。根据本教程,此示例中的 EC2 实例通过 CLI 的私有端点调用 SQS 没有问题。

如果我将上面的简单 Lambda 放到同一个 VPC 和子网中,具有 SQS 发布权限等并调用测试函数,它将正确解析子网内 SQS 端点的 IP 地址,但调用将超时(使确保您的 Lambda 超时时间超过 60 秒以使 boto 失败)。启用 boto 调试日志记录进一步确认 IP 已正确解析并且对 SQS 的 HTTP 请求超时。

我没有尝试使用非 FIFO 队列,但由于 HTTP 调用在连接请求上失败,这应该无关紧要。这一定是来自 Lambda 的路由问题,因为同一子网中的 EC2 工作正常。

我修改了我的简单 Lambda 并添加了一个 SNS 端点并进行了同样有效的测试。据我所知,该问题似乎是 SQS 特有的。

import json
import boto3
import socket

def testSqs():
    print('lambda-test SQS...')
    sqsDomain='sqs.us-west-2.amazonaws.com'
    
    addr1 = socket.gethostbyname(sqsDomain)
    print('%s=%s' %(sqsDomain, addr1))
    
    print('Creating sqs client...')
    sqs = boto3.client('sqs')
    
    print('Sending Test Message...')
    response = sqs.send_message(
            QueueUrl='https://sqs.us-west-2.amazonaws.com/1234567890/testq.fifo',
            MessageBody='Test SQS Lambda!',
            MessageGroupId='test')
            
    print('SQS send response: %s' % response)

    return {
        'statusCode': 200,
        'body': json.dumps(response)
    }
    

def testSns():
    print('lambda-test SNS...')

    print('Creating sns client...')
    sns = boto3.client('sns')
    
    print('Sending Test Message...')
    response = sns.publish(
            TopicArn='arn:aws:sns:us-west-2:1234567890:lambda-test',
            Message='Test SQS Lambda!'
            )
            
    print('SNS send response: %s' % response)

    return {
        'statusCode': 200,
        'body': json.dumps(response)
    }
    

def lambda_handler(event, context):
    #return testSqs()
    return testSns()

我认为您唯一的选择是 NAT(根据上面的 John),将您的呼叫从本地 EC2 退回(NAT 会更简单、更便宜、更可靠),或者在 VPC 之外使用 Lambda 代理。其他人在类似的帖子中建议。您还可以将 SQS 队列订阅到 SNS 主题(我对此进行了原型设计,并且可以正常工作)并以这种方式将其路由出去,但这似乎很愚蠢,除非您出于某种模糊的原因绝对必须拥有 SQS。

我切换到 SNS。我只是希望获得更多有关 SQS 的经验。希望有人能证明我错了,但我称之为错误。

【讨论】:

  • 有一个错误 - 但它不在 AWS 中,它实际上在 boto3 库中! github.com/boto/boto3/issues/1900 有一个未解决的问题。为了通过 vpc 端点连接到队列,您必须在实例化 sqs 客户端时告诉 boto3 使用它 - 请参阅下面的答案。
【解决方案4】:

您需要将您的 lambda 放入您的 VPC,然后为 SQS 或 NAT 网关设置一个 VPC 端点,当您将您的 lambda 函数添​​加到子网时,请确保您只将其添加到私有子网,否则将无法正常工作。

参考

https://docs.aws.amazon.com/lambda/latest/dg/vpc.html

https://aws.amazon.com/premiumsupport/knowledge-center/internet-access-lambda-function/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-23
    • 2017-07-25
    • 1970-01-01
    • 2017-10-22
    • 1970-01-01
    • 2018-07-23
    • 2020-12-11
    • 1970-01-01
    相关资源
    最近更新 更多