使用JavaScript SDK的AWS Lambda函数始终会引发超时错误

时间:2017-04-30 20:38:44

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

我正在尝试在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射线追踪,但是从各方面来看似乎没有任何东西离开Lambda执行环境--X-Ray报告没有活动到AWS的其他部分(例如ECS)。

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

2 个答案:

答案 0 :(得分:3)

你的代码对我来说非常好,但这里有一些我必须先做的事情:

以上是因为Lambda函数需要Internet访问来调用AWS API端点。附加到VPC的Lambda函数仅具有专用IP地址,因此它们需要NAT网关或NAT实例才能访问Internet。请参阅:Internet Access for Lambda Functions

答案 1 :(得分:0)

对于后人,我采用了John Rotenstein的优秀建议并使用了VPC向导来使事情顺利进行。结果显示关键细节与NAT网关的构建有关。

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

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

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文档中找不到)在上面的最后一个命令中 - 必须使用 PUBLIC 创建NAT网关 子网,即使它与 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