Using the AWS Cloud Development Kit, deploying a AWS Lambda function using Docker container images is pure gold. The installation of dependencies for Lambda functions always stressed me out. Regardless of using Node.js or Python, managing dependencies for AWS Lambda was never fun.
Dockerfile for Dependencies
I like the idea of using a Dockerfile
for AWS Lambda to manage and install dependencies and persist immutable Artifacts. During Re:Invent 2020, AWS announced Container Image Support for AWS Lambda functions using the AWS Elastic Container Registry. The needed Dockerfile
for Node.js can look like this:
FROM amazon/aws-lambda-nodejs:12
COPY package.json ./
RUN npm install
COPY handler.js ./
CMD [ "handler.run" ]
For Python, it may look like this:
FROM amazon/aws-lambda-python:3.8
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY handler.py ./
CMD [ "handler.run" ]
When orchestrating multiple AWS Lambda functions, just create a Dockerfile
for every function, matching a function’s runtime requirements, and store them in a folder together with all other needed files:
src
├─ lambda-node-example
│ ├─ Dockerfile
│ ├─ handler.js
│ └─ package.json
└─ lambda-python-example
├─ Dockerfile
├─ handler.py
└─ requirements.txt
CDK Construct: Lambda Fleet
Using this pattern for folders and files, everything to deploy all AWS Lambda functions can easily be build using the AWS Cloud Development Kit and published as a generic CDK Construct!
Install the cdk-lambda-fleet
module from NPM
and use it with the AWS Cloud Development Kit in TypeScript:
import * as path from "path";
import * as cdk from "@aws-cdk/core";
import { LambdaFleet } from "cdk-lambda-fleet";
export class FleetStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new LambdaFleet(this, "Fleet", {
path: path.resolve(__dirname, "../src"),
});
}
}
That’s all you need to configure, to build and push your docker images to AWS Elastic Container Registry using the AWS CDK and cdk-lambda-fleet.
Sources
You can find the source code on GitHub. The LambdaFleet
construct looks up all folders in the given path
and deploys a Lambda Function afterward.
import * as cdk from "@aws-cdk/core";
import * as Lambda from "@aws-cdk/aws-lambda";
import * as fs from "fs";
import * as camelcase from "camelcase";
export interface LambdaFleetProps {
path: string;
}
export class LambdaFleet extends cdk.Construct {
public functionList: { [key: string]: Lambda.Function } = {};
constructor(scope: cdk.Construct, id: string, props: LambdaFleetProps) {
super(scope, id);
this.addFunctions(props.path);
}
private addFunctions(path: string) {
const nodes = fs.readdirSync(path);
nodes
.filter((node) => fs.statSync(`${path}/${node}`).isDirectory())
.forEach((name) => {
const id = camelcase(name, { pascalCase: true });
this.functionList[id] = new Lambda.DockerImageFunction(this, id, {
code: Lambda.DockerImageCode.fromImageAsset(`${path}/${name}`),
});
new cdk.CfnOutput(this, `${id}Arn`, {
value: this.functionList[id].functionArn,
});
});
}
}
Of course, deploying a Lambda function involves more than just source code and a Dockerfile
. You could extend this approach by adding a policy.json
file for every function and update the Lambda.DockerImageCode.fromImageAsset
to use it accordingly.