Dockerize monolithic apps — Part 1
Recently, I wanted to implement docker for an app, which I developed using Node.js and Angular. But the catch here is, both the frontend and backend resides in the same folder. Of course, it would have been easier to divide them in separate repositories and then dockerize each of them. But I decided not to change the structure as the app itself was a very basic one — with limited functionalities and I didn’t want to maintain two different repositories and host them on different servers. You might have also encountered such problems especially when working with a legacy monolithic app. So, let’s try to implement docker together!
PREREQUISITES
- Node.js and NPM— https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
- Postgresql — https://www.postgresql.org/download
- Docker — https://docs.docker.com/get-docker
I developed a simple node-angular app, which pulls fake employee data from a json and shows it in a tabular format. You can pull the code from the Github repository link. Create a .env
file in the root directory of the project. The contents of this file are —
PORT=3000
ENV=development
URL=http://api.jsoneditoronline.org/v1/docs/088b6155ffc749e9a8ace0721f2cc384/data
SESSION_SECRET=psyduck
DB_USER=postgres
DB_PASSWORD=root
Note: Replace the values of
DB_USER
andDB_PASSWORD
as per your system values.
Follow these commands to run the project in your system —
npm install
npm run migrate
npm start
Note:
npm run migrate
will create the database in your system. More information on how this command works will be added in the next part of this series — Coming Soon!
Open localhost:3000
in your browser to see the project up and running.
Note: If you want to see the live changes for the frontend, run the command
npm run serve
in a separate terminal and openlocalhost:4200
.
Adding docker to the app
Note: To add docker, make sure that docker is installed in your system. If not, follow this link to install it.
Add Dockerfile
to the root directory of the project. This contains all the commands to be called on the command line to assemble an image.
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
CMD [ "npm", "run", "start" ]
EXPOSE 3000
This will host our server on 3000
port, but the main challenge is to run the frontend of the app as well to see live changes made in the code. To make this work, let’s add another Dockerfile named as Dockerfileclient
and rename the above file as Dockerfileserver
.
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4200
CMD [ "npm", "run", "serve" ]
This hosts the client on 4200
port. docker-compose.yaml
looks like this —
services:
server:
build:
context: ./
dockerfile: Dockerfileserver
volumes:
- .:/app
- /app/node_modules
restart: always
ports:
- "3000:3000"
depends_on:
- client
client:
build:
context: ./
dockerfile: Dockerfileclient
volumes:
- .:/app
- /app/node_modules
restart: always
ports:
- "4200:4200"
Here, we clarify which Dockerfile to use while starting the server or client service. Run these commands to have the docker image up and running.
Note: Make sure to run docker desktop before running these commands.
docker-compose build
docker-compose up
Now, if you go to localhost:4200
to run the app, we see the front end is loaded but we are not able to get any data from the backend. This is weird. Let’s debug it further!
Before dockers, the frontend and backend of the apps were running on different ports — 4200
and 3000
respectively. But how were they communicating with each other? The answer lies in the file — proxy.conf.json
. This tells the frontend that all the api requests to the server should be proxied to localhost:3000
.
{
"/api": {
"target": "http://localhost:3000/",
"secure": false
}
}
But, while using dockers, we have two containers — server and client. Instead of proxying our requests to localhost:3000
, we need to proxy it through the server container. So just make this change to the proxy.conf.json
file and build the image again.
{
"/api": {
"target": "http://server:3000/",
"secure": false
}
}
Voila! Our app is back to running again.
There is one problem here — if we make any change to the frontend, it reflects in the app on a reload. But changes in the server need a restart of the app. To tackle this, lets use nodemon.
Install the library using — npm install nodemon
. Now, add the script in package.json
to start the app using nodemon —
"dev": "nodemon --ignore 'dist/*' server.js"
Update the Dockerfileserver to use npm run dev
instead of npm run start
. Now the app should reload on any changes to the server.
To stop the docker container, use the command docker-compose down
.
In the next part of this blog, we will discuss on how to add database to this app and make it work with dockers! Check the Part 2 of the series here.
I hope you found this post useful and please do let me know in case of any question or query. Leave a comment here and I will surely reply!