Deploy chartjs-node-canvas to AWS lambda using docker container

Mithun Das
4 min readJun 29, 2023

--

šŸ‘‹ Hello! I am working on a project where I need to send some line and bar charts on a weekly basis via emails and slack.

When I start designing an application, I start with Serverless architecture unless there is a compelling reason not to use that.

Serverless architecture is a cloud computing model where the cloud provider dynamically manages the allocation and scaling of resources, allowing developers to focus on writing and deploying code without the need to manage underlying infrastructure.

I have used chartjs-node-canvas in the past to generate line charts and bar charts from inside express servers running on EC2 container. This module has upstream dependency on node-canvas module. When we install node-canvas via npm install, binaries are automatically downloaded for supported OS. This is all good but when you need to package your code and run in a lambda function, things get complicated.

In a usually way when you package your code and node_modules in a zip file and upload to AWS lambda, you choose Nodejs runtime and architecture ( arm64 or x86_64) but you donā€™t have control on the underlying OS. As a result when you package up from your Mac for an example, the binaries are not compatible when running on lambda which are basically Amazon Linux 2 instances under the hood.

The workaround is, as you guessed, build the module on an Amazon Linux 2 EC2 instance, zip up the code and deploy to lambda. Yeah, that would work but thatā€™s a time taking process and also depends on having EC2 instance which defeats the purpose of serverless architecture. So what would you do?

The first thing that comes to your mind is probably ā€œDockerā€ šŸ³ And we are fortunate that AWS lambda supports running your code from docker image. You just need to follow 3 simple steps

  • Build the docker image
  • Push the image to Amazon ECR ( I think only private repos are supported)
  • Create lambda function from the image

Prerequisite

I am assuming you are familiar with AWS lambda and AWS CLI and I also assume you have AWS CLI configured on your computer.

Build from source

You start by cloning my github repo

git clone https://github.com/just4give/chartjs-node-canvas-lambda
cd chartjs-node-canvas-lambda

You will find the dockerfile

ARG ARCH=arm64
FROM public.ecr.aws/lambda/nodejs:18-${ARCH}

#architecture -x86_64 or arm64
# copy function code
COPY . ${LAMBDA_TASK_ROOT}


RUN yum update -y
RUN yum groupinstall "Development Tools" -y
RUN yum install gcc-c++ cairo-devel pango-devel libjpeg-turbo-devel giflib-devel librsvg2-devel pango-devel bzip2-devel jq python3 -y
RUN mkdir ${LAMBDA_TASK_ROOT}/lib

RUN npm install canvas --build-from-source
RUN npm install

# set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "index.handler" ]

Before you start buiding the image, update .env file

AWS_ACCOUNT_ID="your_aws_account_id"
REGION="your_aws_region"
ARCH="arm64"

Then simply run the make build which will build the image and tag based on the version in package.json and architecture you choose.

make build ENVFILE=.env

You can run the container on your local to test out the lambda function. Read the documentation on my github repo for more information.

All right, time to take off cloud journey, before you run make publish command to publish the image to your ECR ( Elastic Container Registry ) , create a repo named ā€œnode-chartā€ through AWS console or your favorite IaC tool.

make publish ENVFILE=.env

After image is published, create a lambda function using that image with tag. Make sure to choose right architecture.

Thatā€™s it! Itā€™s time to test. Run the command below. Change the function name if you have used different name.

 aws lambda invoke --function-name node-chart-runtime --cli-binary-format raw-in-base64-out \
--payload '{"type":"line","labels":["2017","2018","2019"],"data":[{"label":"Bears","data":[90,60,120]},{"label":"Dolphins","data":[60,80,100]},{"label":"Whales","data":[70,90,100]}],"title":"Wildlife Population"}' output.txt
cat output.txt

You should see output like

{"statusCode":200,"body":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlgAAAGQCAYAAAByNR6YAAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdeXhU9fU/8Pe5d5IQIoR9FavWHS1awB23ulSr1laDdQEhyx2k0tZaRSHY+UmCWqtYUUluFhD0i5KirQvWpYsLruCOta64VCqL7IQkcz/n90cmGELCZJnkzvJ+PU8fzdw7n3vmTjOezPvOGYCIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiKhTid8FEFHnCwaDF6mqZGVlPT579uzqpj833d9xnCMAHGJZ1sclJSVvtnaNSZMmDVDVEarayxjzcnl5+Vdd/FBjJto56up1iCixBPwugIhaLxgMjlLV/Ywx/ykvL38HAAoKCoaLyGEi8k1paenzADBp0qShxpjjAWxyXfdpVV0EILBly5ZhAL5q+nMzh7oUwPXGmLsBTAGAaGsEg8GTjDFLAWQBgG3bOQD+3JbHV1BQcK6IZAKAiHie532dmZn5/pw5cza39Vx1VCvO0S4KCgpOF

If you paste the entire body string as source of an image element in html, you should see output like below

Example of a bar chart below

Github repo

Created and documented with lots of ā¤ļø

Have any question or suggestion for new topics ? Find me on Twitter, LinkedIn, Insta

Happy learning and Namaste! šŸ™

--

--

Mithun Das

Sr Principal Engineer | Designing & Building Softwares for 20 years