Simplify Your Node.js/Nest.js Deployment: Step-by-Step Guide with Docker and Google Cloud
Effortlessly deploy and scale your Node.js/Nest.js applications using Docker containers and Google Cloud’s powerful infrastructure.
Deploying a Nest.js microservice using Docker and Google Cloud offers a simple yet powerful solution for building and deploying applications.
In this guide, we will go through the process of deploying the microservice we created previously. But whether you’re working with Nest.js or Node.js, I have written a generic approach to building your application with Docker and deploying it to Google Cloud Run. Hopefully, by the end, you will have a solid understanding of the deployment process.
To embark on this deployment journey, it’s essential to have Docker installed on your system. Docker provides a platform for packaging, distributing, and running applications in a consistent and isolated environment. It streamlines the deployment process by eliminating dependency issues and ensuring your microservice runs smoothly across different environments. If you are new to Docker, don’t worry! This guide will help you get started and harness the power of Docker for your deployment needs.
Prerequisites:
- You must have docker installed on your system. Download Docker.
- If you are on Windows you’ll need WSL (Windows Subsystem for Linux). Install WSL.
- If you get the error in Figure 0 when installing WSL then refer to this article (This is a common error).
Table of Contents:
- Stage 1: Building Project.
- A: Creating a Docker Image.
- B: Creating a Docker Container. - Stage 2: Setting up CI (Continuous Integration).
- A: Set up a Github workflow.
- B: Running the workflow. - Stage 3: Deploying to Google Cloud.
- A: Deploy Docker Container.
- B: Setting up CD (Continuous Deployment). - Summary.
Stage 1: Building Project.
A: Creating a Docker Image.
We’ll use the Dockerfile to create a Docker Image.
Create two new files: Dockerfile and .dockerignore in the root directory as shown in Figure 1.
Paste the following code in the Dockerfile.
# Base image.
FROM node:18-alpine
# Set the Enviournment to production
ENV NODE_ENV=production
# Create app directory.
WORKDIR /usr/src/app
# A wildcard is used to copy package.json AND package-lock.json.
COPY package*.json ./
# Install nestjs which is required for bulding the Nest.js project.
# (Skip for Node.js Projects)
RUN npm install -g @nestjs/cli
# Installs only the dependencies and skips devDependencies.
RUN npm install --omit=dev
# Copy all the files to the container.
COPY . .
# Create a "dist" folder with the production build.
#(Skip for Node.js Projects)
RUN npm run build
# Start the server using the production build for:
# Nest.js:
CMD [ "node", "dist/main.js" ]
# ___OR___
# Node.js:
# CMD ["node", "index.js"]
# Expose port 8080 of your microservice / server.
EXPOSE 8080
The reason we had to include the command RUN npm install -g @nestjs/cli
because if we look at our package.json (Figure 2) we can see in scripts that the build command is nest build
.
And since we are including the command RUN npm run build
to build the application and then run the production build with CMD [ “node”, “dist/main.js” ]
, We are required to install nest. Without it, our docker build command will fail throwing the error message: nest: not found.
Paste the following code in .dockerignore.
Dockerfile
.dockerignore
node_modules
npm-debug.log
dist
We exclude the above files and directories from the Docker image to keep it lightweight.
Build the Docker Image.
docker build --tag nest-microservice .
Check your image.
docker images
B: Creating a Docker Container.
Create and run our container based on the docker image we created earlier.
docker run -d --publish 5000:8080 --name nest-container nest-microservice
-d flag will “detach” the terminal from the container.
We expose port 8080 inside the container to port 5000 outside the container.
View the running containers.
docker ps
We can also view all of our containers using the --all flag.
docker ps --all
Stop your container.
docker stop nest-container
Push the Dockerfile and .dockerignore to your GitHub repository.
Stage 2: Setting up CI (Continuous Integration).
Prerequisites:
- You must have a Github repository with your Nest.js / Node.js source code.
- You must have a DockerHub account.
A: Set up a Github workflow.
Go to your GitHub repository and click on the highlighted button in the order shown in Figure 5.
Setting > Secrets and variables > Actions > New repository secret
Create a new secret named: DOCKERHUB_USERNAME
and your Docker username as a secret value as shown in Figure 6.
Create a PAT (Personal Access Token) on Docker Hub.
Sign in to Docker Hub and click on the highlighted buttons in the order shown in Figure 7.
Account Settings > Security > New Access Token
Click Generate and copy that token and paste it into another repository secret named: DOCKERHUB_TOKEN
.
Set up a GitHub workflow.
Go to the Actions tab in your repository and click on “set up a workflow yourself” which will create a main.yml file.
Paste the following code in main.yml.
name: ci
on:
push:
branches:
- "main"
jobs:
build:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/nest-microserivce:latest
Learn more about the YML syntax.
B: Running the workflow.
- Save the workflow file.
- Select Commit Changes and push the changes to the
main
branch. - After pushing the commit, the workflow will start automatically.
- Verify your build was successful as shown in Figure 11.
All of the jobs should run successfully as shown in Figure 12. If you had any problems, you can check which of the jobs failed and debug your way from there.
When the workflow is complete, go to your repositories on Docker Hub. your repository should appear there as shown in Figure 13.
Stage 3: Deploying to Google Cloud.
Go to google cloud console and log in with your Google account.
Create a new project by clicking the highlighted buttons in the order shown in Figure 14.
Give a name to your project.
When the project is created click the hamburger icon and click on Cloud Run in the menu.
A: Deploy Docker Container.
Note: This step is optional if you want to set up Continuous Deployment (CD). However, I thought it best to include this if you have trouble setting up CD. But keep in mind you’ll manualy have to deploy a new revision when you want changes in production.
Before we create a service or a job we need the link to our Docker Hub repository.
The link is usually: <DockerHub_Username>/<DockerHub_Repository>:latest
So in my case: abdullahraheel/nest-microservice:latest
If you provided a different tag during the build then your link may change as the “latest” tag will change.
TAG
is a custom human-readable manifest identifier that is typically a specific version or variant of an image. It is “latest” by default.
If you have a different tag you need to replace “latest” with that tag or if you have multiple tags and you want to use a specific tag.
Checking Your Tags.
- Go to DockerHub and click on your repository.
- Go to tags and select the appropriate tag.
Once we have the link we can proceed forward.
Click on Create Service.
And paste the link in the Container Image URL, select your appropriate region and tweak any other settings if you want then click Create.
Note: If you have a cron job that is always running like in our case then you’ll have to set “Minimum number of instances” to 1 otherwise the application scales down to 0 instances if there is no traffic so your cron job will not run if your server has no traffic, but please note it will significantly increase your server bill.
In future, I’ll publish an article explaining how to use Google App Engine through Cloud SDK and Scheduling jobs with cron.yaml, which is a better way of scheduling jobs in my opinion.
B: Setting up CD (Continuous Deployment).
Note: You can only set up CD for a service and not a Job in Google Cloud Run. Also make sure to delete the service you just created otherwise you’ll have 2 different services running the same application.
Click on Create Service then to set up continuous deployment you need to check the second radio button and then click on the button labelled “Set up with cloud build” as shown in Figure 17:
Select your Repository Provider and Repository then click Next.
Select the branch on which you want the CD to exist. I’ll leave it default as “main”.
Click on Dockerfile in “Build Type” and provide the path for the Dockerfile. Since we created the docker file in our root directory the default location is correct.
Click Save and edit any more settings you want then finally click Create and voila!
You have successfully set up CD for your project. Now any changes you make or merge in your main branch will automatically redeploy the project.
Summary:
- Stage 1: We built our Nest.js project using Docker.
- Stage 2: We set up CI for our project using Github Actions and Docker Hub.
- Stage 3: We deploy our project to Google Cloud and set up CD.
That’s All folks!
Thank you for taking the time to read this article. If you found it helpful, don’t hesitate to show your appreciation by clapping. Feel free to leave any questions or comments, and I will be glad to assist you. Stay tuned for future articles.