This article outlines the steps involved in building a node js lambda using lambda layers for library dependencies, using AWS CLI.
The example will build a lambda function that will return current time using momentjs library. The lambda will not bundle momentjs via package.json
, node_modules
, but will use momentjs via lambda layers.
Following are assumed to be available on your computer.
What is a lambda layer ? (Source: AWS Docs) : A layer is a ZIP archive that contains libraries, a custom runtime, or other dependencies. With layers, you can use libraries in your function without needing to include them in your deployment package.
> cd ~
> mkdir momentjs-lambda-layer
> cd momentjs-lambda-layer
> npm init -y
> yarn add moment
- The packaging should use the folder structure shown below for nodejs lambda layers. This folder structure is a requirement from lambda layers.
momentjs-lambda-layer.zip
└ nodejs/node_modules/moment
- Move the
node_modules
into nodejs subdirectory and package them as a zip archive file. Note: The packaged zip file is moved to/tmp
folder.
> mkdir -p dist/nodejs
> cp package.json dist/nodejs
> cd dist/nodejs
> yarn --production
> cd ..
> zip -r /tmp/momentjs-lambda-layer.zip nodejs
Note : Your AWS CLI is assumed to be setup. You can execute aws lambda list-layers
to get a response listing the layers.
e.g., response
{ "Layers": [] }
Publish the lambda layer under MIT license. We are not using S3, but directly pushing the zip file that was created earlier.
> aws lambda publish-layer-version --layer-name "momentjs-lambda-layer" \
--description "NodeJS module of Moment JS library" \
--license-info "MIT" \
--compatible-runtimes "nodejs8.10" \
--zip-file "fileb:////tmp/momentjs-lambda-layer.zip"
If the publishing was successful, you should see a response similar to the one below, returning the LayerVersionArn
.
Note :
- You can publish as many times a layer and they will be automatically published to the next version.
{
"LayerVersionArn": "arn:aws:lambda:<AWS_REGION>:<AWS_ACCOUNT_ID>:layer:momentjs-lambda-layer:1",
"Description": "NodeJS module of Moment JS library",
"CreatedDate": "2018-12-05T10:31:36.211+0000",
"LayerArn": "arn:aws:lambda:<AWS_REGION>:<AWS_ACCOUNT_ID>:layer:momentjs-lambda-layer",
"Content": {
"CodeSize": 743941,
"CodeSha256": "nakCb8UDFbyzPrbcCde4xRK9K5EZ5MiPZxW49kl3AGI=",
"Location": "https://awslambda-ap-se-1-layers.s3.<AWS_REGION>.amazonaws.com/snapshots/<AWS_ACCOUNT_ID>/momentjs-lambda-layer-..."
},
"Version": 1,
"CompatibleRuntimes": [
"nodejs8.10"
],
"LicenseInfo": "MIT"
}
Note: This example intentionally avoids ES6 import
.
> cd ~
> mkdir current-time-lambda
> cd current-time-lambda
> npm init -y
Copy the simple lambda code, that returns the current time using momentjs library, into currentTimeLambda.js
file.
> echo "const moment = require('moment');
exports.handler = (event, context, callback) => {
const time = moment().format('MMMM Do YYYY, h:mm:ss a');
callback(null, { time });
};" > currentTimeLambda.js
Note :
- The lambda source has reference to moment.js, but moment.js is not included in package.json.
- We will attach the lambda layer that was created earlier to this lambda in following steps.
Here, we only need the single js file containing the lambda function. The essential purpose of lambda layers - simplify the bundling of lambda and separate the node_modules (dependencies) concern.
> zip -r /tmp/currentTimeLambda.js.zip currentTimeLambda.js
Note :
- Region, IAM role and AWS profile are specific to the user.
AWS_ROLE_FOR_LAMBDA
is used as the IAM role that has lambda management permissions.AWS_PROFILE
used here is the value configured in~/.aws/config
file, while setting up AWS CLI.- The packaged file
currentTimeLambda.js.zip
is directly uploaded and S3 is not used.
aws lambda create-function \
--region <AWS_REGION> \
--function-name currentTimeLambda \
--handler 'currentTimeLambda.handler' \
--runtime nodejs8.10 \
--role "arn:aws:iam::<AWS_ACCOUNT_ID>:role/AWS_ROLE_FOR_LAMBDA" \
--zip-file 'fileb:///tmp/currentTimeLambda.js.zip' \
--profile <AWS_PROFILE>
On successful creation of lambda, you should see a response similar to the one below.
{
"TracingConfig": {
"Mode": "PassThrough"
},
"CodeSha256": "9Bi4aWpD8XRFI7udqYo1NTy0WlYAkUVXw6iQKK0NYBo=",
"FunctionName": "currentTimeLambda",
"CodeSize": 14236,
"RevisionId": "3e64f4fb-c146-47fd-bfb0-d3a14486b001",
"MemorySize": 128,
"FunctionArn": "arn:aws:lambda:<AWS_REGION>:<AWS_ACCOUNT_ID>:function:currentTimeLambda",
"Version": "$LATEST",
"Role": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/AWS_ROLE_FOR_LAMBDA",
"Timeout": 3,
"LastModified": "2018-12-05T10:48:10.171+0000",
"Handler": "currentTimeLambda.handler",
"Runtime": "nodejs8.10",
"Description": ""
}
Note : If you try executing the lambda function, it will throw error mentioning momentjs
library missing.
- For this we need the
LayerVersionArn
that was returned by AWS when the lambda layer was created. Refer to step 1.4 above. - Layers and lambda runtimes compatibility should be checked when this association is done.
> aws lambda update-function-configuration \
--function-name currentTimeLambda \
--layers arn:aws:lambda:<AWS_REGION>:<AWS_ACCOUNT_ID>:layer:momentjs-lambda-layer:1
If the above command of lambda layer association was successful, you should see the layer listed in the lambda details as below.
{
"Layers": [
{
"CodeSize": 743941,
"Arn": "arn:aws:lambda:<AWS_REGION>:<AWS_ACCOUNT_ID>:layer:momentjs-lambda-layer:1"
}
],
"FunctionName": "currentTimeLambda",
"LastModified": "2018-12-05T10:51:28.445+0000",
"RevisionId": "2ca09f6f-e85a-4cbd-a5ed-4f52db28bb07",
"MemorySize": 128,
"Version": "$LATEST",
"Role": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/AWS_ROLE_FOR_LAMBDA",
"Timeout": 3,
"Runtime": "nodejs8.10",
"TracingConfig": {
"Mode": "PassThrough"
},
"CodeSha256": "9Bi4aWpD8XRFI7udqYo1NTy0WlYAkUVXw6iQKK0NYBo=",
"Description": "",
"CodeSize": 14236,
"FunctionArn": "arn:aws:lambda:<AWS_REGION>:<AWS_ACCOUNT_ID>:function:currentTimeLambda",
"Handler": "currentTimeLambda.handler"
}
- Now, we will invoke the lambda (that was deployed without momentjs node_module) to verify that it uses lambda layer for momentjs dependency.
> aws lambda invoke --function-name currentTimeLambda --log-type Tail --payload '{}' outputfile.txt
Successful invocation should list a response similar to the following.
{
"LogResult": "U1RBUlQgUmVxdWVzdElkOiAwZ...HVyYXRpb246IDEwMCBtcyAJTWVtb3J5IFNpemU6IDEyOCBNQglNYXggTWVtb3J...E1CCQo=",
"ExecutedVersion": "$LATEST",
"StatusCode": 200
}
- To ensure the current time is responded by lambda, view the contents of
output.txt
file. You should see the time.
> cat outputfile.txt
{"time":"December 5th 2018, 10:53:55 am"}%
🏁 If you have seen the timestamp, Congrats ! You got your first lambda layer working 🏁