Container Images with AWS Lambda
By: Bhargav Shah
Tags: Lambda, AWS, Docker, Containerization, Serverless

Container images for your serverless functions

Lambda Support Container Images

You can package your code and dependencies as a container image using tools such as the Docker CLI. You can then upload the image to a container registry such as Amazon Elastic Container Registry. You can now package and deploy Lambda functions as container images of up to 10 GB in size. In this way, you can also easily build and deploy larger workloads that rely on sizable dependencies, such as machine learning or data intensive workloads.

AWS provides a set of open-source base images that you can use to build the container image for your function code. Lambda provides base images for the following runtimes:

You can also use alternative base images from other container registries. AWS also provides an open-source runtime client that you add to your alternative base image to make it compatible with the Lambda service.

Base images for custom runtimes

AWS provides base images that contain the required Lambda components and the Amazon Linux or Amazon Linux2 operating system. You can add your preferred runtime, dependencies and code to these images.

Docker Hub: amazon/aws-lambda-provided

Before going hands on with Container Image with Lambda, Let’s clear some basic understanding about Lambda.

AWS Lambda execution environment

Lambda invokes your function in an execution environment, which provides a secure and isolated runtime environment. The execution environment manages the resources required to run your function. The function’s runtime communicates with Lambda using the Runtime API. Extensions communicate with Lambda using the Extensions API. Extensions can also receive log messages from the function by subscribing to logs using the Logs API.

Lambda execution environment lifecycle

Init — In this phase, Lambda creates or unfreezes an execution environment with the configured resources, downloads the code for the function and all layers, initializes any extensions, initializes the runtime, and then runs the function’s initialization code (the code outside the main handler). The Init phase happens either during the first invocation, or in advance of function invocations if you have enabled provisioned concurrency.

Invoke — In this phase, Lambda invokes the function handler. After the function runs to completion, Lambda prepares to handle another function invocation.

Shutdown — This phase is triggered if the Lambda function does not receive any invocations for a period of time. In the Shutdown phase, Lambda terminates the runtime, alerts the extensions to let them stop cleanly, and then removes the environment. Lambda sends a Shutdown event to each extension, which tells the extension that the environment is about to be shut down.

Why we need Runtime Interface Client?

“To implement the Lambda Runtime API”

The runtime interface client in your container image manages the interaction between Lambda and your function code. AWS have open-sourced a set of software packages, Runtime Interface Clients (RIC), that implement the Lambda Runtime API, allowing you to seamlessly extend your preferred base images to be Lambda compatible. The Lambda Runtime Interface Client is a lightweight interface that allows your runtime to receive requests from and send requests to the Lambda service.

Why we need Runtime interface emulator?

“For local testing of container image and Lambda”

Lambda provides a runtime interface emulator (RIE) for you to test your function locally. The AWS base images for Lambda include the RIE. For other base images, you can download the Runtime interface emulator from the AWS GitHub repository.

mkdir -p ~/.aws-lambda-rie curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie chmod +x ~/.aws-lambda-rie/aws-lambda-rie

Let’s build custom image for Node.js

our app.js (save under “./myFunction” folder)

"use strict"; exports.handler = async (event, context) => { return 'Hello World!'+'Event:'+JSON.stringify(event)+'Context:'+JSON.stringify(context); }

our Dockerfile

# Define custom function directory

ARG FUNCTION_DIR="/function" FROM node:12-buster as build-image # Include global arg in this stage of the build

ARG FUNCTION_DIR # Install aws-lambda-cpp build dependencies

RUN apt-get update && \

apt-get install -y \

g++ \

make \

cmake \

unzip \

libcurl4-openssl-dev # Copy function code

RUN mkdir -p ${FUNCTION_DIR}

COPY myFunction/* ${FUNCTION_DIR} WORKDIR ${FUNCTION_DIR} # If the dependency is not in package.json uncomment the following line

RUN npm install aws-lambda-ric RUN npm install # Grab a fresh slim copy of the image to reduce the final size

FROM node:12-buster-slim # Include global arg in this stage of the build

ARG FUNCTION_DIR # Set working directory to function root directory

WORKDIR ${FUNCTION_DIR} # Copy in the built dependencies

COPY --from=build-image ${FUNCTION_DIR} ${FUNCTION_DIR} ENTRYPOINT ["/usr/local/bin/npx", "aws-lambda-ric"]

CMD ["app.handler"]

Now let’s build image or download from dockerhub

docker build -t myfunction:latest .

You can download from dockerhub bhargavshah86/myfunction:latest

docker pull bhargavshah86/myfunction:latest

Install RIE in local for local testing

mkdir -p ~/.aws-lambda-rie && \

curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && \

chmod +x ~/.aws-lambda-rie/aws-lambda-rie

Run our container image in local

docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \

--entrypoint /aws-lambda/aws-lambda-rie \

myfunction:latest \

/usr/local/bin/npx aws-lambda-ric app.handler

Test our function using curl

curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'

We have succesfully created image which support Lambda.

Now lets use same container image with Lambda using AWS console

Create Function and Specify ECR Image URI (Do not forget to push image to your Elastic Container Registry)

Test our lambda in console.

Same logs can we found in AWS Cloudwatch Logs

Thank you for reading 😃