[Serverless architecture #2] Strapi v4 with Azure Container Instances (ACI)
This article is the second one of a series of four articles dedicated to the serverless and containerization concepts and how it can be applied in the context of a typical web application involving:
- A Content Management System (CMS) using Strapi.
- A public facing front end application using Gatsby.
- A web analytics tool to measure the behavior of users on our website using Umami.
The serverless/containerization concept that will be concretely implemented throughout these articles will use the above frameworks/tools, though the same principles can be most likely extended to others, let alone the time it takes to adapt them.
Since I started working with Strapi, I always wanted to deploy it in a serverless fashion, That’s actually what I tend to do in most of my projects whenever it is worth it rather than using provisioned servers.
In the past I followed this tutorial on how to deploy Strapi v3 via AWS lambda using the Serverless framework. Great stuff, and amazing tutorial. While it was working pretty well, I thought there was one downside, the variability in response time from the requests (between 3 and 10 seconds), probably due to the cold starts with AWS Lambda. Also I thought that in terms of data transfer, using this solution for my use case was maybe not the best fit.
I could reduce that variability a little by keeping my instance alive longer (by sending periodic requests to API Gateway), but this was not completely satisfying, and one of my customers complained that it was sometimes too slow. My other issue, was that I was using two platforms AWS and Azure for implementing that solution, and I don’t like to juggle with multiple providers for a same project.
The release of the v4 was a nice improvement and I thought it could be good to give it a try as well. I then decided to make it work on Azure only this time via Azure Container Instances (ACI) and automate the deployment with Terraform. Doing things that way would incur a slower start on the first request, but good performance once the ACI is started.
This is what I will share with you below.
The main purpose here is to use a Pay-Per-Use subscription to keep the cost as low as possible while keeping good performances, and most importantly without using a provisioned server.
Below is an overview of what we will deploy via Terraform:
The different entities:
- An ACR to handle our private Docker container images
- A storage account using a file share for our database (SQLite) and a storage container for our media.
- An ACI containing our running instance of Strapi served by a reversed proxy (Caddy).
- A powershell Azure Function that triggers the start/stop of our ACI
The implementation I adopted has a few specificities, which I expose below.
- Strapi does not handle SSL. As as result, serving the request to Strapi via a reverse proxy was here the easiest solution to circumvent the issue. Caddy was then the perfect fit for the task, and takes care of the SSL certificate for us, which is very convenient.
- The user should be able to connect to the Strapi login page as simply as possible via a single URL. In order to do that, I implemented a PowerShell Azure Function that does that on demand, and returns a web page that lets the user know when Strapi is up and running. The user just needs to send a request to the Azure Function.
- The ACI should be stopped after a defined time. The purpose of this project is to deploy Strapi in a serverless fashion with a Pay-per-consumption model. The ACI should then be stopped when it is no longer used/needed. In order to do that, I wrapped Strapi in a node.js application, that will periodically (via cron) verify when the last request to was sent to Strapi. If no request has been sent within the last 10 min, then the container exits and sends a request to the Azure function previously mentioned to stop the container group.
NB: I assume it would have been possible to do a Strapi plugin for that, but I did not dig into that part as of now.
- The deployment should be automated as much as possible to increase the speed and ease of use and reliability. A terraform script is taking care of the deployment of the whole infrastructure. The only part remaining is the actual deployment of the Azure function and build of the Docker container image. No pipeline is included in the solution for that, but this is a common workload depending on your CI/CD provider (GitHub, DevOps..) and should also be automated when pushing to the repo.
In order to make it easy for the user to know when Strapi is ready, I created a small dashboard that will query the status of the container instances deployment, and followed by a 200 response from Strapi. When the application is ready, a button will be displayed that the user can click to open Strapi in a new tab.
See the screenshots below:
Try it out
If you want to try it out, the code is available here and the README contains the necessary steps and instructions in order to deploy the solution.
Most of the work is done by the Terraform scripts including the set up for most of the environment variables for the different resources. However a few manual steps are needed and require running a few edits or command lines:
- Edit variables for Strapi configuration (tokens, keys, secret..) according to your installation
- Build Strapi Docker image and deployment to container registry
- Convert SQLite database to WAL mode
- Deploy the Azure Function code to the Function App
The results are quite satisfying overall.
Start: The ACI takes about ~2 to 3 minutes to start when calling the Azure function until we get redirected to the Strapi login page.
NB: As a side note, it may be possible to gain some time on the startup by reducing the size of the Docker image, and using a Docker registry with a higher SKU than the one I have (Basic).
Speed: Strapi is running on an ACI with 0.5 CPU cores and 0.5 GiB and performs as fast as on my local computer, which is very satisfying.
Cost: Since the ACI is stopped most of the time, if a user is using the CMS for a few hours every day, the cost of running this solution will be considerably low (count a few dollars per month).
I hope you enjoyed this short note, and don’t hesitate to let me know what you think in the comments.