Sharing node_modules folder between lambda using Lambda Layers + Cloud Formation - node.js

I have a project that uses serverless-framework (this) to define the AWS resources to be used. I have the various .yml files that describe each resource that the project needs to run.
Recently, I've had to install several NPM packages for my lambdas and they've become very large in megabytes (>3MB), so the code is no longer viewable from the console.
Since including node_modules in each lambda is not a best practice and they are very heavy this way, I was wondering about using a Lambda Layer to share node_modules between lambdas.
As .yml I have a shared structure between all of them called provider.yml, something like:
name: aws
runtime: nodejs14.x
lambdaHashingVersion: 20201221
region: ${opt:region, "eu-central-1"}
stage: ${opt:stage, "dev"}
profile: ${self:custom.profiles.${self:provider.stage}}
deploymentBucket:
name: avatar-${self:provider.stage}-deploymentbucket
versioning: true
blockPublicAccess: true
environment:
EXAMPLE_ENV_VAR: ${self:custom.baseResource}-envvar
USERPOOL:
'Fn::ImportValue': ${self:custom.baseResource}-userPoolId
APP_CLIENT_ID:
'Fn::ImportValue': ${self:custom.baseResource}-userPoolClientId
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- ...
Resource: "*"
Then I have a file that includes the provider.yml and all the lambdas (called serverless.yml) and is the file that I use to deploy:
service: listOfLambdas
app: appname
frameworkVersion: '2'
projectDir: ../../
provider: ${file(../../resources/serverless-shared-config/provider.yml)}
package:
individually: true
exclude:
- "**/*"
functions:
- ${file(./serverless-functions.yml)}
Finally, I have the serverless-functions.yml that contains the Lambdas structure:
lambdaName:
handler: src/handlers/auth/example.run
name: ${self:custom.base}lambdaName
description: Lambda description
events:
- httpApi:
method: POST
path: /api/v1/example
package:
include:
- ../../node_modules/**
This includes the node_modules folder in the Lambda.
How can I create a resource with a YML template managed by Cloud Formation to create a Lambda Layer to which I can assign all my lambdas so that they share the node_modules folder. I expect to have to create a new serveless.yml inside the resources folder with the CloudFormation YML template to bind I guess somehow to my lambdas.
I need it to be managed by CloudFormation, so I can have a common stack with all the resources used by the project and so I can deploy it at start-up.
Where should the node_modules then be foldered in the project? Now the project is something like:
Root
|_ lib
|_ node_modules
|_ resources
|_serverless-shared-config
|_provider.yml
|_s3
|_serverless.yml
|_apigateay
|_ ...
|_ services
|_ lambda
|_ src
|_ index.js
|_ ...
|_ serverless.yml
|_ serverless-functions.yml
Where can I find a template in order to solve this problem? Or what is the best practice in order to share the node_modules between Lambdas?

Generally speaking, using Lambda Layers is a good approach to store shared code as you can reduce redundancy and keep the size of deployment packages small.
Layers can be created using the Serverless framework as well, so you can include them into your existing templates and have them managed by Serverless/CloudFormation. Here is a link to the documentation with configuration examples and explanations. Here is one example of how to add a layer to your serverless.yaml file:
layers:
NodeModules:
path: layer
package:
artifact: layer.zip
name: layer
compatibleRuntimes:
- nodejs12.x
- nodejs14.x
functions:
- ${file(./serverless-functions.yml)}
And then from each Lambda function in your serverless-functions.yaml file, you can refer to the layer as follows:
lambdaName:
handler: src/handlers/auth/example.run
name: ${self:custom.base}lambdaName
layers:
- { Ref: NodeModulesLambdaLayer }

Related

AWS lambda can't find lambda layer path

I am having a tough time setting up the lambda layers with lambdas. I am using node 14.
My folder structure for lambda layer
layer/
nodejs/
node14/
node_modules/
hey.js
I have also tried having only the nodejs directory as below
layer/
nodejs/
hey.js
But in both cases I get cannot found module error in the lambda.
The paths I tried for accessing layers in lambda are as below
'/opt/nodejs/node14/node_modules/hey.js' (for first folder structure)
'/opt/nodejs/hey.js' (for folder structure with only the nodejs directory in the layer)
'hey.js' (trying to access the file directly)
But I had no luck. What am I doing wrong?
I am using AWS sam to deploy lambda and layers. I could see the layer getting attached to lambda on the console.
Here is my SAM template
layer1:
Type: AWS::Serverless::LayerVersion
Properties:
ContentUri: ./src/lambda-layers/layer1
CompatibleRuntimes:
- nodejs14.x
Metadata:
BuildMethod: nodejs14.x
lambda1:
Type: 'AWS::Serverless::Function'
Properties:
CodeUri: ./src/lambdas/lambda1
Handler: index.handler
Role: !GetAtt LambdaExecutionRole.Arn
Layers:
- !Ref layer1
Events:
AwsIoTMetadata:
Type: Api
Properties:
RestApiId: !Ref CrApi
Path: /user
Method: GET
How to access the layer in lambda?
Please Help. Thanks in advance

Serverless: Layers No file matches include / exclude patterns

My serverless.yml file works fine as soon as I add layers I starting getting this error
Serverless Error ----------------------------------------
No file matches include / exclude patterns
service: foundation
useDotenv: true
custom:
name: foundation
provider:
name: aws
stackName: ${self:service}-${self:provider.stage}
region: us-east-1
stage: ${opt:stage, 'dev'}
environment:
REGION: ${self:provider.region}
STAGE: ${self:provider.stage}
layers:
certificates:
path: certificate
plugins:
- serverless-deployment-bucket
- serverless-pseudo-parameters
- serverless-plugin-typescript
functions:
- ${file(./src/handler/function.yml)}
resources:
- ${file(./resources/outputs.yml)}
runtime: Node.js
Note: using layers to add certificates to lambda
Serverless version: 2.31.0
You may be getting this error if you execlude the directory where the layer is running through the plugins you use or in another part of the yml file. Maybe editing it this way will solve the problem
this link has an explanation about it
layers:
certificates:
package:
include:
- ./your/layer/path/**

Warned - no cfnRole set and unnecessary files was created after deploy

No cfnRole warned and unnecessary files was created after deploy
Serverless: Safeguards Processing...
Serverless: Safeguards Results:
Summary --------------------------------------------------
passed - no-unsafe-wildcard-iam-permissions
passed - framework-version
warned - require-cfn-role
passed - allowed-runtimes
passed - no-secret-env-vars
passed - allowed-regions
passed - allowed-stages
Details --------------------------------------------------
1) Warned - no cfnRole set
details: http://slss.io/sg-require-cfn-role
Require the cfnRole option, which specifies a particular role for CloudFormation to assume while deploying.
I had been go to the site that write in details.
details: http://slss.io/sg-require-cfn-role
Anyway, I don't know how to fix it.
s_hello.py & s_hello2.py always generated after deploy.
This is my serverless.yaml file
service: myapp
app: sample-app
org: xxx
provider:
name: aws
runtime: python3.7
stage: dev
region: us-east-1
package:
individually: true
functions:
hello:
handler: src/handler/handler.hello
hello2:
handler: src/handler2/handler2.hello2
It's always happen although follow this site .
My Lambda-function will create "s_xxx.py (Where xxx is handler.py file.
I solved this issue creating a cfn-role in AWS IAM following these steps:
Roles -> Create Role -> AWS Service -> Select Cloud Formation from the list
Next: Permisions
You need to choose all the policies you need to deploy your lambda function (S3FullAccess, SQSFullAccess, LambdaFullAccess...)
There's one that it's mandatory AWSConfigRole who allows to serverless framework to get this role.
After setting the role, you need to copy its arn and create behind provider level cfnRole like this:
provider:
name: aws
runtime: python3.7
stage: ${opt:stage, 'dev'}
profile: ${self:custom.deploy-profile.${opt:stage, 'dev'}}
region: us-west-2
environment:
TEMP: "/tmp"
cfnRole: arn:aws:iam::xxxxxxxxxx:role/cfn-Role
That's work for me, I hope to help!

How to add a custom folder and file via YAML in Serverless app

I am writing a serverless app by using SAM. I created a config folder to keep some table information and some other info. then I load it in my app.js.
when I deploy the app.js locally by using SAM deploy, I observe that the config folder will not include. would you mind advise me how to add config folder in final build folder in .aws-sam\build folder?
my Yaml file
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Sample SAM Template for test
Globals:
Function:
Timeout: 120
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs10.x
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Also when I run the project in debug mode I am getting this error:
{
"errorType": "Runtime.ImportModuleError",
"errorMessage": "Error: Cannot find module '../config/config.js'"
}
I load the js file as below:
"use strict";
let response;
const AWS = require('aws-sdk');
const config = require('../config/config.js');
To include custom files that you need to re-use in multiple functions like the config file in your case. Then you can use lambda layers.
In your template.yml you would include a layer as follow:
ConfigLayer:
Type: "AWS::Serverless::LayerVersion"
Properties:
CompatibleRuntimes:
- nodejs10.x
ContentUri: ./config/
and then add it to your lambda function definition:
Type: AWS::Serverless::Function
Properties:
Handler: cmd/lambdas/hello-world/app.lambdaHandler
CodeUri: src/
Runtime: nodejs10.x
Layers:
- Ref: ConfigLayer
Events:
CatchAll:
Type: Api
Properties:
Path: /hello-world
Method: GET
The contents of the config/ directory will be available in the /opt/ path.
that means the full path to your config.js will be /opt/config.js and you can access it from any lambda that uses that layer.

Serverless not including my node_modules

I have a nodejs serverless project that has this structure:
-node_modules
-package.json
-serverless.yml
-funcitons
-medium
-mediumHandler.js
my serverless.yml:
service: googleAnalytic
provider:
name: aws
runtime: nodejs6.10
stage: dev
region: us-east-1
package:
include:
- node_modules/**
functions:
mediumHandler:
handler: functions/medium/mediumHandler.mediumHandler
events:
- schedule:
name: MediumSourceData
description: 'Captures data between set dates'
rate: rate(2 minutes)
- cloudwatchEvent:
event:
source:
- "Lambda"
detail-type:
- ""
- cloudwatchLog: '/aws/lambda/mediumHandler'
my sls info shows:
Service Information
service: googleAnalytic
stage: dev
region: us-east-1
stack: googleAnalytic-dev
api keys:
None
endpoints:
None
functions:
mediumHandler: googleAnalytic-dev-mediumHandler
When I run sls:
serverless invoke local -f mediumHandler
it works and my script where I included googleapis and aws-sdk work. But when I deploy, those functions are skipped and show no error.
When debugging serverless's packaging process, use sls package (or sls deploy --noDeploy (for old versions). You'll get a .serverless directory that you can inspect to see what's inside the deployment package.
From there, you can see if node_modules is included or not and make changes to your serverless.yml correspondingly without needing to deploy every time you make a change.
Serverless will exclude development packages by default. Check your package.json and ensure your required packages are in the dependencies object, as devDependencies will be excluded.
I was dumb to put this in my serverless.yml which caused me the same issue you're facing.
package:
patterns:
- '!node_modules/**'

Resources