Deploy a containerized Node app to Azure
Fullstack Developer and Tech Author
Containers have become a widely used DevOps technology. Containers give DevOps professionals the ability to define infrastructure as code, making it possible to create isolated environments for testing, deploying, and running applications. In this article, we will learn how to containerize a Node.js application and use CI/CD to automatically deploy to Azure Web Apps via Azure Container Registry.
Prerequisites
To complete this tutorial, you will need:
- Node.js installed on your system (version >= 18.0.0)
- Azure account
- Free CircleCI account
- GitHub account
- Azure CLI installed
- Docker Desktop installed on your local machine. You can follow Docker’s tutorial for Windows or macOS.
With all these installed and set up, you can begin the tutorial.
Creating a container registry on Azure
Our first step is creating a container registry on Azure to store and build a Docker container. Go to your Azure portal home page and click Create a resource. Then select Containers > Container Registry from the Containers menu.
On the registry creation page, fill in the information, including the name for your registry.
Click Review + Create to open the review page. Confirm your registry information, then click Create to trigger the registry creation process.
If you prefer, you can use the Azure CLI to create the registry by running:
az acr create --name nodedockerregistry --resource-group demoRG --sku standard --admin-enabled true
I have named my registry nodedockerregistry
, but you might want to use a different name. If you do, be sure to substitute the name you choose instead.
Getting an access key from the registry
Here, you will enable Docker access in the Azure Container Registry. This is crucial to the deployment process because it lets you remotely log into the Azure container registry through the CLI and push images to it.
To enable Docker access, open the registry, go to the Settings section and click Access Keys.
This will show you the registry name and login server. Enable the Admin user using the toggle button. Then, copy the username and any of the passwords, preferably the first one. Keep this handy because you will need it later in the tutorial.
Cloning the demo project
The next step is to clone the Node.js project to deploy to Azure using a Docker container. To clone the project, run:
git clone https://github.com/CIRCLECI-GWP/new-node-azure-docker.git
Next, use cd new-node-azure-docker
go into the root of the project, then install dependencies using npm install
.
This project is a basic Node.js API with a single endpoint for returning an array of todo
objects. It also contains a test suite for testing the endpoint.
Run the application using the npm start
command. The application will start at the address http://localhost:5000
. Load the following route http://localhost:5000/todos
in your browser to show the list of todos
.
Creating and building a Docker image on Azure Container Registry
The next step is creating a Dockerfile so the custom Docker image can run the project. It will then be pushed to and built on the Azure Container Registry (ACR).
At the root of the project, open the Dockerfile
(no extension) and ensure that its content is the same as shown below:
FROM node:current-alpine
COPY . /app
WORKDIR /app
RUN npm install
ENTRYPOINT ["npm", "start"]
This file uses a node
image as its base with the tag current-alpine
. It then copies the contents of the project into an app
folder inside the image, sets app
as the working directory and runs npm install
to install dependencies. The application is booted up by running npm start
.
At the root of the project, build the image using Dockerfile
. Be sure to replace nodedockerregistry
with the name you chose earlier. Run:
docker build -t nodedockerregistry.azurecr.io/mynodeimage:latest .
Based on the content of the application and the Dockerfile, this command will build the container image. Because it uses the registry name and login server you created earlier, it will easily map the container image with the Azure Container Registry.
Note the dot (.
) at the end of the command. Make sure to include it; it defines the build context as the current directory.
This image has been given the name mynodeimage
, which will appear in ACR
once its been pushed.
Next, log into the Azure Container Registry and push the container image to it by issuing the following command from the terminal:
docker login -u DOCKER_USER -p DOCKER_PASS nodedockerregistry.azurecr.io
Replace nodedockerregistry.azurecr.io
with your registry URL. Also, replace the following placeholders with appropriate values:
DOCKER_USER
is the username for the container registry.DOCKER_PASS
is the password to the container registry.
After you log in, push the image to the Azure registry by running:
docker push nodedockerregistry.azurecr.io/mynodeimage:latest
This command sends the folder’s contents to Azure Container Registry, which uses the instructions in the Dockerfile to build the image and store it.
To view the image you created, go to your Azure resources page in the portal. Click your registry then click Repositories from the Services section.
Helpfully, every newly built image is tagged latest
. The Docker image that contains the Node.js application is now available in your registry for deployment to the Azure App Service.
Creating an Azure Web App
Next, create an Azure Web App. From the Azure portal, click Home > Create a resource, then select Containers > Web App for Containers to create a new web app service instance.
You will be redirected to the Create Web App page. Select an Azure subscription, a resource group and enter the web app name. Create a new resource group if you don’t have one. Make sure the default, Docker container
and Linux
operating system are selected.
From the Docker tab, select the image source and its Docker image.
On the Docker page:
- Select the Single Container option
- Choose Azure Container Registry as the Image Source
- Enter your registry name in the Registry field (I am using
nodedockerregistry
in this tutorial) - Use
mynodeimage
for the Image field - For Tag, select
latest
- Leave
Startup Command
empty; your Dockerfile already has anENTRYPOINT
command
Now we are ready to click Review + Create to review the details. Then click Create.
After you have configured the web app, the Docker image is pulled and run. The first time your app loads is a “cold start”. That means your app will take some time before it loads. There is a “cold start” each time a fresh deployment is made from the Docker
image. After that, the app is available immediately.
Enabling continuous deployment on the web app
You are not here for a one-time deployment, right? You want continuous deployment. To set it up, click the web app name, then go to the Deployment section. Click Deployment Center then scroll down the Settings tab. Turn on continuous deployment by selecting the radio button for it. Click Save.
With continuous deployment selected, the web app will trigger a new deployment of the Node.js application each time the Docker image is rebuilt on Azure Container Registry .
Automating deployment to Azure using CircleCI
Your next step is to add the pipeline configuration for CircleCI. This configuration will automate testing and run the commands to build and push the container image to the Azure Container Registry.
At the root of your project, create a new folder named .circleci
and add a new file config.yml
within it. Open the new file and add the following content:
version: 2.1
orbs:
docker: circleci/docker@2.2.0
jobs:
build:
working_directory: ~/repo
docker:
- image: cimg/node:18.0.0
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: install-packages
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
paths:
- ./node_modules
- run:
name: Run tests
command: npm run test
build-and-deploy-docker-image:
executor:
name: docker/docker
tag: "3.6"
steps:
- checkout
- docker/install-docker-tools
- setup_remote_docker:
docker_layer_caching: true
- run:
name: Build and push Docker image
command: |
docker build -t nodedockerregistry.azurecr.io/mynodeimage:latest .
echo $DOCKER_PASS | docker login nodedockerregistry.azurecr.io -u $DOCKER_USER --password-stdin
docker push nodedockerregistry.azurecr.io/mynodeimage:latest
workflows:
version: 2.1
build:
jobs:
- build
- build-and-deploy-docker-image:
requires:
- build # only deploy once build job has completed
These scripts pull in the Docker orb from CircleCI. Then use its executor to install the tools required for Docker to build and push the image to the Azure Container Registry.
It also defines a workflow with two jobs: build
and build-and-deploy-docker-image
.
The build
job checks out the code from the remote repository, installs dependencies, and runs the tests to make sure there are no bugs in the code. Once the build
job is complete, the build-and-deploy-docker-image
job takes over. It checks out a clean copy of the code, builds the docker image and push to Azure container registry.
The build-and-deploy-docker-image
job has a dependency on the build
job so it will not run until the build
job is complete.
Set up a repository on GitHub and link the project to CircleCI. Review pushing your project to GitHub for instructions.
Connecting with CircleCI
Log into your CircleCI account. If you signed up with your GitHub account, all your repositories will be available on your project’s dashboard. Search for the deploy-dotnet-azure-instance
project.
Click the Set Up Project button. You will be prompted about whether you have already defined the configuration file for CircleCI within your project. Enter the branch name (for the tutorial, we are using main
). Click the Set Up Project button to complete the process.
This build job will pass indicating that the test was successful:
While the build-and-deploy-docker-image
job will fail because it requires the Azure Container Registry credentials, and you haven’t added them yet.
Creating environment variables
To fix the credential issue, click the Project Settings button, then click Environment Variables. Add these two new variables:
DOCKER_USER
is the username for the container registryDOCKER_PASS
is the password for the container registry.
Click Rerun Workflow from Start.
Your workflow will run successfully. You can make some changes to the codebase locally and push it to GitHub to further test the continuous deployment process.
That is it!
Go to the URL to review the live app: https://<APPLICATION_NAME>.azurewebsites.net/todos
.
Note: Please note that your URL on Microsoft Azure should be different from the one shown above.
To see how the deployment changes after running both jobs, we need to add one more task to todo.js
:
module.exports = [
...{
id: 4,
task: "Make Dinner",
},
];
Adding a task means we need to update the test suite in __tests__/todos.test.js
to check for four todo
objects. Change line 15 to:
expect(res.body.length).toBe(4);
Save changes to the project and commit to your main branch in the remote repository.
Now head over to your browser where the /todos
endpoint was previously loaded. When you reload the page, you will find a new todo
object.
Conclusion
Containers give application developers and DevOps architects the ability to bridge gaps between development and production environments that may lead to bugs or apps not running as expected. In this tutorial, we have demonstrated how to use containers to make seamless deployments to Azure Web Apps. I hope you can use what you have learned to improve your own applications. The complete source code can be found here on GitHub.
Happy coding!