Deploy chartjs-node-canvas to AWS lambda using docker container
š 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
Created and documented with lots of ā¤ļø
Have any question or suggestion for new topics ? Find me on Twitter, LinkedIn, Insta
Happy learning and Namaste! š