Container Images with AWS Lambda

Bhargav Shah
5 min readDec 18, 2020

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:

  • Node.js (Docker Hub: amazon/aws-lambda-nodejs)
  • Python (Docker Hub: amazon/aws-lambda-python)
  • Java (Docker Hub: amazon/aws-lambda-java)
  • .NET (Docker Hub: amazon/aws-lambda-dotnet)
  • Go (Docker Hub: amazon/aws-lambda-go)
  • Ruby (Docker Hub: amazon/aws-lambda-ruby)

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 .
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

  • Test our lambda in console.
  • Same logs can we found in AWS Cloudwatch Logs

Thank you for reading 😃

--

--