【问题标题】:AWS Lambda function using JavaScript SDK always throws timeout error使用 JavaScript SDK 的 AWS Lambda 函数总是抛出超时错误
【发布时间】:2017-09-28 09:28:31
【问题描述】:

我正在尝试在我的 AWS Lambda 函数(NodeJS 6.10 运行时)中使用 AWS SDK for JavaScript。我的最终目标是使用它来管理我的 ECS 实例,但现在我只是尝试使用 API 的任何部分并且每次尝试都失败了。我已将功能简化为最简单的;看看:

exports.handler = (event, context, callback) => {
    var AWS = require('aws-sdk');
    (new AWS.ECS({"apiVersion": '2014-11-13'})).listClusters({}, (err, data) => {
        if (err) console.log(err, err.stack);
        else     console.log(data);

        callback(null, "DONE");
    })
};

我已经给这个函数一个具有这个定义的 IAM 角色:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "aws:*",
                "ecs:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

我已将此功能设置为在我现有的 VPC、子网和安全组中运行。我已经增加了超出所有可能需求的超时和内存上限。

此函数的每次执行都会因超时异常而失败。我尝试过使用许多不同的 API 调用,甚至是不同的服务 API,但每次尝试在我的函数中调用 API 都会导致超时。

我什至为此功能启用了 X-Ray 跟踪,但从表面上看,似乎什么都没有离开 Lambda 执行环境 - X-Ray 没有向 AWS 的其他部分(例如 ECS)报告任何活动。

我错过了什么?为什么我不能在 Lambda 中使用任何 JS SDK?

【问题讨论】:

  • 我认为"aws:*" 的操作没有任何作用,因为它不是服务的名称。
  • @JohnRotenstein 是的 aws:* 只是众多失败尝试之一。

标签: amazon-web-services aws-lambda aws-sdk


【解决方案1】:

您的代码对我来说运行得非常好,但是我必须先做一些事情:

以上是因为 Lambda 函数需要 Internet 访问才能调用 AWS API 端点。附加到 VPC 的 Lambda 函数只有一个私有 IP 地址,因此它们需要 NAT 网关或 NAT 实例才能访问 Internet。见:Internet Access for Lambda Functions

【讨论】:

  • 你是对的 - 关键似乎是 VPC 配置。当我解除 Lambda 函数与 VPC 的关联时,它工作正常。但是,最终我需要我的函数来访问我的 VPC 中的资源。所以我想这是问题的症结所在——如何正确配置我的 VPC 以便 SDK 在 Lambda 中工作?我一定在这里做错了什么。我会继续调查。谢谢
  • 要从 Lambda 中调用 SDK,请将其放在具有 NAT 网关或 NAT 实例的私有子网中。
  • 我在提交问题后看到了这样做的建议,并花了很长时间尝试这样做。到目前为止没有运气。
  • 约翰,我已经成功(终于!)解决了所有这些问题。我已经发布了一个答案,其中提供了我努力解决的细节,希望所有这些都能帮助其他人处理这个问题。再次感谢。
【解决方案2】:

为了后人,我接受了 John Rotenstein 的出色建议,并使用 VPC 向导让事情顺利进行。事实证明,关键细节与 NAT 网关的构造有关。

如果您希望您的 Lambda 函数能够访问 AWS 开发工具包和您的 VPC 资源,您至少需要两个子网(一个用于公共资源,一个用于私有资源)。以下是您需要执行的 VPC 命令,这些命令与 VPC 向导所发生的类似:

创建一个新的 VPC,或将这些环境变量设置为您现有的变量

export REGION=us-west-2 #or whatever region you want

export VPC_ID=`aws ec2 create-vpc --cidr-block 10.1.0.0/16 \
    --query Vpc.VpcId --output text`

为您的公共资源创建一个互联网网关

export IGW_ID=`aws ec2 create-internet-gateway \
    --query InternetGateway.InternetGatewayId --output text`

aws ec2 attach-internet-gateway --internet-gateway-id $IGW_ID --vpc-id $VPC_ID

使用上述 IGW 为您的公共子网创建路由表

export ROUTE_TABLE_ID_PUBLIC=`aws ec2 describe-route-tables \
    --filter Name=vpc-id,Values=$VPC_ID --query RouteTables[0].RouteTableId --output text`

export SUBNET_ID_PUBLIC=`aws ec2 create-subnet --vpc-id $VPC_ID \
    --cidr-block 10.1.0.0/24 --availability-zone ${REGION}a \
    --query Subnet.SubnetId --output text`

aws ec2 associate-route-table --subnet-id $SUBNET_ID_PUBLIC \
    --route-table-id $ROUTE_TABLE_ID_PUBLIC

aws ec2 create-route --route-table-id $ROUTE_TABLE_ID_PUBLIC \
    --gateway-id $IGW_ID --destination-cidr-block 0.0.0.0/0

为 NAT 网关创建 IP 分配,用于私有子网

export IP_ALLOCATION_ID=`aws ec2 allocate-address --domain vpc \
    --query AllocationId --output text`

export ROUTE_TABLE_ID_PRIVATE=`aws ec2 create-route-table --vpc-id $VPC_ID \
    --query RouteTable.RouteTableId --output text`

export SUBNET_ID_PRIVATE=`aws ec2 create-subnet --vpc-id $VPC_ID \
    --cidr-block 10.1.1.0/24 --availability-zone ${REGION}b \
    --query Subnet.SubnetId --output text`

aws ec2 associate-route-table --subnet-id $SUBNET_ID_PRIVATE \
    --route-table-id $ROUTE_TABLE_ID_PRIVATE

export NAT_GW_ID=`aws ec2 create-nat-gateway --subnet-id $SUBNET_ID_PUBLIC \
    --allocation-id $IP_ALLOCATION_ID --query NatGateway.NatGatewayId --output text`

在这里稍等片刻 - NAT 网关需要一些时间才能准备好并可用于进一步的命令。

关键细节(我无法在 AWS 文档中找到)在上面的最后一条命令中 - NAT 网关 必须 使用 PUBLIC 创建子网,即使它与 PRIVATE 路由表相关联:

使用 NAT 网关创建私有路由表

aws ec2 create-route --route-table-id $ROUTE_TABLE_ID_PRIVATE \
    --gateway-id $NAT_GW_ID --destination-cidr-block 0.0.0.0/0

创建安全组等......

export SECURITY_GROUP_ID=`aws ec2 create-security-group --vpc-id $VPC_ID \
    --group-name mygroup --description "My SG" \
    --query GroupId --output text`

aws ec2 authorize-security-group-ingress --group-id $SECURITY_GROUP_ID \
    --protocol tcp --port 22 --cidr 0.0.0.0/0

aws ec2 authorize-security-group-ingress --group-id $SECURITY_GROUP_ID \
    --cidr 10.1.0.0/16 --protocol all

此时,您应该能够创建与私有子网关联的 Lambda 函数,这些函数可以访问 VPC 中的资源,还可以调用 Internet(AWS SDK 使用所必需的)。这是一个执行此操作的示例 Lambda 函数:

创建在您的 VPC 中执行 Lambda 函数所需的 IAM 角色

aws iam create-instance-profile --instance-profile-name testRole

testRole_trust_policy.json:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

export TEST_ROLE_ARN=`aws iam create-role --role-name testRole \
  --assume-role-policy-document file://testRole_trust_policy.json \
  --query Role.Arn --output text`


testRole_policy.json:
{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Effect": "Allow",
          "Action": [
              "lambda:*",
              "ec2:CreateNetworkInterface",
              "ec2:DescribeNetworkInterfaces",
              "ec2:DeleteNetworkInterface"
          ],
          "Resource": [
              "*"
          ]
      }
  ]
}

aws iam put-role-policy --role-name testRole \
  --policy-name testRole --policy-document file://testRole_policy.json

aws iam add-role-to-instance-profile \
  --instance-profile-name testRole \
  --role-name testRole

使用此 IAM 角色创建 Lambda 函数

contents of lambda.zip:
- test.js:
    exports.handler = (event, context, callback) => {
        AWS = require('aws-sdk'),
            lambda = new AWS.Lambda({"apiVersion": '2015-03-31'});

        lambda.listFunctions(callback);
    };


aws lambda create-function --function-name testWithVPC \
    --runtime nodejs6.10 --role $TEST_ROLE_ARN \
    --handler test.handler --timeout 10 \
    --zip-file fileb://lambda.zip \
    --vpc-config SubnetIds=$SUBNET_ID_PRIVATE,SecurityGroupIds=$SECURITY_GROUP_ID

执行并查看结果:

aws lambda invoke --function-name testWithVPC with.txt
with.txt:
    {"NextMarker":null,"Functions":[{"FunctionName":"testWithVPC","FunctionArn": ....]}

这足以演示功能。我基于此模式构建的项目可在此处获得,以获得更强大的示例:https://github.com/jakefeasel/sqlfiddle3

【讨论】:

    猜你喜欢
    • 2017-05-28
    • 2019-09-17
    • 1970-01-01
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    • 2021-03-07
    • 1970-01-01
    相关资源
    最近更新 更多