简体   繁体   English

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

[英]AWS Lambda function using JavaScript SDK always throws timeout error

I am attempting to use the AWS SDK for JavaScript within my AWS Lambda function (NodeJS 6.10 runtime). 我正在尝试在AWS Lambda函数(NodeJS 6.10运行时)中使用AWS SDK for JavaScript My ultimate goal is to use this to manage my ECS instances, but for now I'm simply trying to use any part of the API and am failing with each attempt. 我的最终目标是使用它来管理我的ECS实例,但是现在我只是尝试使用API​​的任何部分,并且每次尝试都失败了。 I have reduced the function to the simplest possible; 我已将功能简化为最简单的功能; take a look: 看一看:

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");
    })
};

I have given this function an IAM role that has this definition: 我给这个函数一个具有这个定义的IAM角色:

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

I have set this function to run within my existing VPC, subnets and security groups. 我已将此功能设置为在我现有的VPC,子网和安全组中运行。 I have increased the timeouts and memory caps beyond all possible needs. 我已经增加了超出所有可能需求的超时和内存上限。

Every execution of this function fails with a timeout exception. 每次执行此函数都会因超时异常而失败。 I have tried using many different API calls, and even different service APIs, but every attempt to invoke an API within my function always results in a timeout. 我尝试过使用许多不同的API调用,甚至是不同的服务API,但是每次尝试在我的函数中调用API总会导致超时。

I even enabled X-Ray tracing for this function, but by all appearances it seems that nothing leaves the Lambda execution environment - X-Ray reports no activity out to other parts of AWS (ECS, for example). 我甚至为这个功能启用了X射线追踪,但从各方面来看,似乎没有任何东西离开Lambda执行环境--X-Ray报告没有活动到AWS的其他部分(例如ECS)。

What have I missed? 我错过了什么? Why can't I use any of the JS SDK within Lambda? 为什么我不能在Lambda中使用任何JS SDK?

Your code worked perfectly fine for me, but here's some things I had to do first: 你的代码对我来说非常好,但这里有一些我必须先做的事情:

The above is because the Lambda function requires Internet access to cal the AWS API endpoints. 以上是因为Lambda函数需要Internet访问来调用AWS API端点。 Lambda functions attached to a VPC only have a Private IP address, so they require a NAT Gateway or NAT Instance to have Internet access. 附加到VPC的Lambda函数仅具有专用IP地址,因此它们需要NAT网关或NAT实例才能访问Internet。 See: Internet Access for Lambda Functions 请参阅: Lambda函数的Internet访问

For posterity, I took John Rotenstein's excellent advise and used the VPC Wizard to get things working. 对于后人,我采用了John Rotenstein的优秀建议并使用了VPC向导来使事情发挥作用。 Turns out the pivotal detail has to do with the construction of the NAT gateway. 结果显示关键细节与NAT网关的构建有关。

If you want your Lambda function to have access to the AWS SDK and your VPC resources, you need at least two subnets (one for public and one for private resources). 如果您希望Lambda函数可以访问AWS SDK和VPC资源,则至少需要两个子网(一个用于公共资源,一个用于私有资源)。 Here are the VPC commands you need to execute, which are similar to what happens with the VPC Wizard: 以下是您需要执行的VPC命令,这些命令与VPC向导类似:

create a new VPC, or set these env variables to your existing ones 创建一个新的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`

create an internet gateway for your public resources 为您的公共资源创建一个互联网网关

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

create route tables for your public subnet, using the above IGW 使用上面的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

create an IP Allocation for a NAT Gateway, for use with a private subnet 为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`

Wait here a few moments - it takes some time before the NAT Gateway is ready and usable for further commands. 稍等片刻 - NAT网关准备好并可用于进一步命令需要一些时间。

The key detail (that I was unable to find in the AWS documentation) is within that last command, above - that the NAT Gateway must be created with the PUBLIC subnet, even though it is associated with the PRIVATE route table: 关键细节(我在AWS文档中找不到)在上面的最后一个命令中 - 必须使用PUBLIC子网创建NAT网关,即使它与PRIVATE路由表关联:

create private route table with NAT Gateway 使用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

create security groups, etc.... 创建安全组等....

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

At this point you should be able to create Lambda functions which are associated with the private subnet, which have access to resources in the VPC and also can make calls out to the Internet (necessary for AWS SDK usage). 此时,您应该能够创建与私有子网关联的Lambda函数,这些函数可以访问VPC中的资源,还可以调用Internet(这是AWS SDK使用所必需的)。 Here's an example Lambda function which does exactly this: 这是Lambda函数的一个示例,它正是这样做的:

create IAM roles necessary to execute Lambda functions within your VPC 创建在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

create a Lambda function using this IAM role 使用此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

execute it and see the results: 执行它并查看结果:

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

This is enough to demonstrate the functionality. 这足以证明功能。 My project building upon this pattern is available here, for more robust samples: https://github.com/jakefeasel/sqlfiddle3 我可以在这里找到基于这种模式的项目,以获得更强大的样本: https//github.com/jakefeasel/sqlfiddle3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM