AWS Lambda packaging with dependencies

筅森魡賤 提交于 2021-02-11 17:06:17

问题


Further outlining is in the context of NodeJS and Monorepo (based on Lerna).

I have AWS stack with several AWS Lambda inside deployed by means of AWS CloudFormation. Some of the lambdas are simple (the single small module) and could be inlined:

https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.Code.html#static-from-wbr-inlinecode

const someLambda = new Function(this, 'some-lambda', {
  code: Code.fromInline(fs.readFileSync(require.resolve(<relative path to lambda module>), 'utf-8')),
  handler: 'index.handler',
  runtime: Runtime.NODEJS_12_X
});

Some have no dependencies and packaged as follows:

const someLambda = new Function(this, 'some-lambda', {
  code: Code.fromAsset(<relative path to folder with lambda>),
  handler: 'index.handler',
  runtime: Runtime.NODEJS_12_X
});

But in case of relatively huge lambdas with dependencies, as I understand, we only way to package (proposed by API) is @aws-cdk/aws-lambda-nodejs:

import * as lambdaNJS from "@aws-cdk/aws-lambda-nodejs";

export function createNodeJSFunction(
  scope: cdk.Construct, id: string, nodejsFunctionProps: Partial<NodejsFunctionProps>
) {
  const params: NodejsFunctionProps = Object.assign({
    parcelEnvironment: { NODE_ENV: 'production' },
  }, nodejsFunctionProps);

  return new lambdaNJS.NodejsFunction(scope, id, params);
}

For standalone packages, it works well, but in case of the monorepo it just hangs on synth of the stack. I just looking for alternatives, cause I believe it is not a good idea to bundle (parcel) BE sources.


回答1:


I've created the following primitive library to zip only required node_modules despite packages hoisting.

https://github.com/redneckz/slice-node-modules

Usage (from monorepo root):

$ npx @redneckz/slice-node-modules \
  -e packages/some-lambda/lib/index.js \
  --exclude 'aws-*' \
  --zip some-lambda.zip

--exclude 'aws-*' - AWS runtime is included by default, so no need to package it.




回答2:


Here is an example if using cloudformation and template.yml.

Create a make file: Makefile with following targets

# Application
APPLICATION=applicatin-name

# AWS
PROFILE=your-profile
REGION=us-east-1
S3_BUCKET=${APPLICATION}-deploy

install:
    rm -rf node_modules
    npm install

clean:
    rm -rf build

build: clean
    mkdir build
    zip -qr build/package.zip src node_modules
    ls -lah build/package.*

deploy:
    sam package \
        --profile ${PROFILE} \
        --region ${REGION} \
        --template-file template.yaml \
        --s3-bucket ${S3_BUCKET} \
        --output-template-file ./build/package.yaml

    sam deploy \
    --profile ${PROFILE} \
    --region ${REGION} \
    --template-file ./build/package.yaml \
    --stack-name ${APPLICATION}-lambda \
    --capabilities CAPABILITY_NAMED_IAM

Make sure the s3 bucket is created, you could add this step as another target in the Makefile.

How to build and deploy on AWS ?

make build
make deploy



回答3:


I have struggled with this as well, and I was using your slice-node-modules successfully for a while. As I have consolidated more of my projects into monorepos and begun using shared dependencies which reside as siblings rather than being externally published, I ran into shortcomings with that approach.

I've created a new tool called lerna-to-lambda which was specifically tailored to my use case. I published it publicly with minimal documentation, hopefully enough to help others in similar situations. The gist of it is that you run l2l in your bundling step, after you've installed all of your dependencies, and it copies what is needed into an output directory which is then ready to deploy to Lambda using SAM or whatever.

For example, from the README, something like this might be in your Lambda function's package.json:

"scripts": {
  ...
  "clean": "rimraf build lambda",
  "compile": "tsc -p tsconfig.build.json",
  "package": "l2l -i build -o lambda",
  "build": "yarn run clean && yarn run compile && yarn run package"
},

In this case, the compile step is compiling TypeScript files from a source directory into JavaScript files in the build directory. Then the package step bundles up all the code from build along with all of the Lambda's dependencies (except aws-sdk) into the directory lambda, which is what you'd deploy to AWS. If someone were using plain JavaScript rather than TypeScript, they could just copy the necessary .js files into the build directory before packaging.

It's likely that your solution is still working fine for your needs, but I thought I would share this here as an alternative in case others are in a similar situation and have trouble using slice-node-modules.



来源:https://stackoverflow.com/questions/63462705/aws-lambda-packaging-with-dependencies

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!