DEV Community

Jayvee Ramos
Jayvee Ramos

Posted on

6. Dockerized app

Introduction:

In this tutorial, we'll take an important step in our journey toward microservices architecture by Dockerizing our existing NestJS reservations application. Dockerizing your application allows you to create a portable and reproducible environment, which is crucial when running multiple microservices together. We will also set up Docker Compose to manage the orchestration of multiple containers. Let's dive in.

Prerequisites:

Before you begin, make sure you have Docker installed on your machine. If you haven't already installed Docker, you can research how to install it and then come back to this tutorial

Dockerizing Your NestJS Application:

Dockerizing your NestJS application involves creating a Dockerfile, which provides instructions on how to build and run a container. This Dockerfile serves as a template for your application.

Step 1: Create a Dockerfile:

In your NestJS reservations project directory, create a new file named Dockerfile. This file will define how your application container should be built.
to do this run the following command on your terminal
touch apps/reservations/Dockerfile
then inside this file just put the following code which I'm going to mention below in sequence

Step 2: Specify the Base Image:

In the Dockerfile, use the FROM keyword to specify the base image. For a Node.js application, it's common to use the Node Alpine image, which already includes Node.js.

# Use Node Alpine as the base image for development
FROM node:alpine as development
Enter fullscreen mode Exit fullscreen mode

Step 3: Set the Working Directory:
Use the WORKDIR command to set the working directory inside the container.

WORKDIR /usr/src/app
Enter fullscreen mode Exit fullscreen mode

Step 4: Copy Package Files and Install Dependencies:
Copy the package.json and package-lock.json files into the container and then install the dependencies.

COPY package.json package-lock.json ./
RUN npm install -g && npm install

Enter fullscreen mode Exit fullscreen mode

Step 5: Copy the Application Code:
Copy your entire application code into the container.

COPY . .
Enter fullscreen mode Exit fullscreen mode

Step 6: Build the Application:
Run the build command to build your NestJS application. This will compile the TypeScript code into JavaScript.

RUN npm run build

Step 7: Create a Production Image:
Now, create a production image using a multi-stage build. This will create a smaller image with only production dependencies.

# Create a production image
FROM node:alpine as production
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
WORKDIR /usr/src/app
COPY package.json package-lock.json ./
RUN npm install --only=production

Enter fullscreen mode Exit fullscreen mode

Step 8: Copy Files from the Development Stage:

Copy the files from the development stage to the production stage. This includes the application code and the build output.

COPY --from=development /usr/src/app/dist ./dist
COPY --from=development /usr/src/app/package.json ./package.json

Enter fullscreen mode Exit fullscreen mode

Step 9: Expose the Port and Set the Command:

CMD [ "node", "dist/apps/reservations/main" ]
Enter fullscreen mode Exit fullscreen mode

your Dockerfile should look like this

# Use Node Alpine as the base image for development
FROM node:alpine as development

WORKDIR /usr/src/app

COPY package.json package-lock.json ./
RUN npm install -g && npm install

COPY . .

RUN npm run build

# Create a production image
FROM node:alpine as production

ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}

WORKDIR /usr/src/app

COPY package.json package-lock.json ./
RUN npm install --only=production

COPY --from=development /usr/src/app/dist ./dist
COPY --from=development /usr/src/app/package.json ./package.json

CMD [ "node", "dist/apps/reservations/main" ]
Enter fullscreen mode Exit fullscreen mode

Step 8: Creation docker ignore
The ".dockerignore" file is used to specify which files and directories should be excluded when building a Docker image. It serves several important purposes:

  • Reducing Image Size
  • Faster Build Process
  • Security
  • Avoiding Conflicts

let's create docker ignore at the root of our directory to do this run the following code on your terminal:

touch .dockerignore

then populate it with the following code

node_modules
dist
npm-debug.log
Dockerfile
.git
.gitignore
*.md
Enter fullscreen mode Exit fullscreen mode

Testing the Dockerized Application:
You can now build and run your Docker container.
to to that let's first go to the reservation directory

To do this, use the following commands:

cd apps/reservations
Enter fullscreen mode Exit fullscreen mode

then type the command

ls
Enter fullscreen mode Exit fullscreen mode

hit enter if you see a Dockerfile then we are good to go

you can now build your Dockerfile by running the following command

docker build ../../ -f Dockerfile -t reservations

Enter fullscreen mode Exit fullscreen mode

Code Explanation

docker build ../../: This part of the command specifies the build context. The ../../ path points to the directory where Docker will look for the Dockerfile and other files needed for building the image. The build context includes all the files and directories in that location.

-f Dockerfile: This part of the command specifies the Dockerfile to use for building the image. The -f flag is followed by the name of the Dockerfile (in this case, "Dockerfile"). This is useful when you have multiple Dockerfiles in your project, and you want to specify which one to use.

-t reservations: This part of the command specifies the name and optionally a tag for the Docker image that will be created. In this case, the image will be named "reservations."

if you run now the following command:
docker run reservations

it will throw an error like this

Error: Config validation error: "MONGODB_URI" is required
Enter fullscreen mode Exit fullscreen mode

we got this error because the Docker container isn't going to be able to access MongoDB, which is running locally on our machine.
let's fix it in the next section (Docker Compose)

Docker Compose for Microservices Orchestration:
When you start working with multiple microservices, managing them individually can be cumbersome. Docker Compose simplifies this task by allowing you to define and run multi-container Docker applications. Here's how to set it up:

Step 1: Create a Docker Compose File:
In your project's root directory, create a docker-compose.yaml file. This file will define the services and their configurations.

first, make sure that you are back to your root directory, remember that a while ago we went to apps/reservations
if you already in the root directory then just run the following command in your terminal:

touch docker-compose.yaml
Enter fullscreen mode Exit fullscreen mode

Step 2: Define Services:
In the docker-compose.yaml file, define the services you want to run.
populate it with the following code:

services:
  reservations:
    build:
      context: .
      dockerfile: apps/reservations/Dockerfile
      target: development
    command: npm run start:dev
    ports:
      - "3000:3000"
    volumes:
      - .:/usr/src/app

Enter fullscreen mode Exit fullscreen mode

in this code, we specify that the reservations service should use the Dockerfile from the reservations app. We use the target argument to build from the development stage. We also set environment variables and volumes to enable hot-reloading.

Step 3: Define a MongoDB Service:
For a complete microservices setup, you'll likely have a database like MongoDB. You can define a MongoDB service (update your docker-compose.yaml file as follows):

services:
  reservations:
    build:
      context: .
      dockerfile: apps/reservations/Dockerfile
      target: development
    command: npm run start:dev
    ports:
      - '3000:3000'
    volumes:
      - .:/usr/src/app
  mongo:
    image: mongo

Enter fullscreen mode Exit fullscreen mode

we just added a mongo image as our database service

Update .env MONGODB_URI
Open your .env file and change the value of MONGODB_URI. It should now point to our newly created service, and it should look like this:

MONGODB_URI=mongodb://mongo:27017/reservation
Enter fullscreen mode Exit fullscreen mode

We have updated 'localhost' to 'mongo' since we are now using the Docker image for the MongoDB service."

Step 5: Running the Microservices:

To build and start your microservices using Docker Compose, execute the following command in the root of your project:

docker-compose up
Enter fullscreen mode Exit fullscreen mode

This command will build and start all the services defined in your docker-compose.yaml file.

Conclusion:

By Dockerizing your NestJS application and using Docker Compose, you have set the foundation for creating and running microservices. You can easily scale and manage your microservices environment while ensuring that your applications run consistently across different environments. This is a crucial step as you begin to develop and deploy more complex applications using a microservices architecture.

In the next tutorial, we'll dive into creating your first microservice, starting with an authentication service. Stay tuned for more exciting developments!

Top comments (0)