Docker for dummies… Part-III 🐳 🧠💡

Multi-container Apps 🐳🐳

Momal Ijaz
AIGuys
7 min readOct 31, 2022

--

This is the third article of the “Docker for dummies… 🐳 🧠💡” series, in which I will explain docker’s what, why, and how in simpler steps. By the end of reading this series, you will be able to know what docker is, why you need to dockerize your projects and how you can do that!

Each article is structured in a stand-alone intuitive way, so that you can read just this article only, to find what you might be looking for.

Credits: Maximilian Schwarzmüller, Docker Udemy Guide’22

Docker in Action!!

Understanding the problemo first 🚩

We covered the basics of what is docker, why it is worth dockerizing your projects, and how to dockerize a dummy web app. But most of the apps do not comprise a single service, we have backends, frontends, databases, live servers, and many components to a real-world application, that should not be baked in one single image, as these components interact with each other, and if any of the services goes down, the entire app container would stop working and it would be super hard to debug which component was the root cause of the issue.

Besides this obvious reason for better maintainability, a distributed containerized application is beneficial in a lot of other ways, like debugging, security, scaling, shipping, etc. Hence, it’s a good practice that if you are building a real-world app with multiple components, you should dockerize it in multiple containers. For example, if we have a web app, which typically comprises a front end, a backend, and a database. Your dockerized app should have a separate container for the front end, back end, and database, and all three containers should be communicating with each other.

What you will learn? 🤓

In this article, we will dockerize a simple node web app, which communicates with a dummy MySQL database and a simple single-page front end written in React.

No prior knowledge of Node, React or MongoDB is needed to understand how to dockerize this app. The code for the app can be downloaded from this link.

We will follow these steps to dockerize our app’s 3 main components, frontend, backend, and database:

a. Spin up each service in a separate container.

b. Put all containers in one network.

c. Make containers communicate with each other.

Dummy Web App 🎢

This is a dummy web app that lets users set a goal, which is saved in a database, and by clicking on the goal, it is deleted from the database.

The data flow is similar to how it is in a conventional web app, the front end and database don’t communicate directly. The data goes from the front end to the back end, which then communicates with the database, to update the records.

Our dummy web app has the following folders:

We don’t need to dive deeper into all the folders, but in the backend folder, we have the app.js file, which contains the main backend logic in nodeJS. On line 87, the backend node app is connecting with MongoDB, using mongoose.connect(), in which we need to provide the IP address of the MongoDB server.

In the frontend folder, the src folder contains the App.js file which makes API calls to backend services by this line, at a couple of locations.

Dockerizing our app 🐳

Let’s dockerize our multi-container app.

We will spin up a separate container for each service and put them all in one container for allowing communication between them.

Start the docker engine via the docker desktop app.

a. Create Network 🕸️

First, we need to create a network using the docker network command, in which we will add our app containers and let them communicate.

Let’s spin up containers for each service now:

b. Dockerizing the MongoDB service:

Now, We can create a fresh instance of MongoDB, by using the already available MongoDB image from the docker hub. Just enter the following command in the terminal and this will spin up a container with a fresh instance of MongoDB service.

The bold and italicized comments explain different parts of the above command, in the below image.

Now if you run “docker ps”, you would be able to see an up-and-running container with the name MongoDB.

b. Dockerizing the backend:

For dockerizing our backend node app, we need to follow the following steps:

a. Create a docker file.

We start by creating a file named Dockerfile in the backend folder and putting this content in it:

backend/Dockerfile

In the above file, we simply install the node packages, copy the source code into the container, expose port 80, and define two environment variables with a default value, which can be overwritten by the docker run command.

Finally, the CMD, command is used to spin up the backend service in the up-and-running container, for allowing the bridging between front and database services.

b. Build image

Now let’s build an image for our backend service by the following command.

c. Spin up the container in the goals-net network

Let’s spin up the container using our backend goals-node image, with the following command:

Note: If you are lost with the usage of bind mounts. or anonymous volumes, I recommend checking out my last article, here.

Note that we are exposing our backend service at localhost port 80, of the host machine from the container.

d. Connect with MongoDB container.

Finally, now our MongoDB and backend service containers are up-and-running in our defined network and you can make that sure by running the “docker ps” command in the terminal, if you see two up containers, you are doing great so far!

One last change to the service is made by updating the line in backend/app.js, where it is connecting with MongoDB (approx. line 87)

Here we are telling our backend service running in the container, to connect with a MongoDB instance, available at port 27017 (default MongoDB port), in our current network with the name of “mongodb” (which is our MongoDB container).

c. Dockerizing the frontend:

Finally, we repeat the same steps to dockerize our front end. Let’s create a file named Dockerfile in the frontend folder.

Frontend/Dockerfile

Everything for our React Single Page Application (SPA)front end’s Dockerfile is similar to the backend’s dockerfile, except for the port. Here we are exposing port 3000 from the front-end’s container on the host machine, which is the default port for the Reacts’ services but you can choose any port of course!

Let’s build our frontend image and name it goals-react, with :

Let’s spin up our last container using the command given below:

Here we are spinning up our frontend service container with name goals-frontend, and exposing port 3000 from the container onto localhost’s 3000 port, using the image we just built.

Optional nerd fact: Notice we have not added this container in our goals-net network, because the React SPA code is compiled in the browser, not in the container, and for it to be able to access the backend service we have to connect it with the backend not via goals-net but by exposing the services’ ports on localhost. So frontend and backend actually communicate via localhost like:

[backend-container :80] — — — -localhost — — — [3000: frontend]

Now, all three containers are up-and-running.

a. The backend and MongoDB container are communicating, by the mongoose.connect() in the backend/app.js file.

b. For allowing the front end and back end to communicate, we need to make some changes in the frontend/src/App.js file. Simply look for the API calls the front end is making to the backend and update that await fetch () call’s URL appropriately, to point it to our backend service running in the container.

The backend service is available at localhost:80, so we will simply update all await fetch calls to the backend in frontend/src/App.js file to:

And… we are done!

If you visit localhost:3000, you should be able to see a multi-container dockerized app up and running. Go ahead, add goals, delete them, and shut down and restart containers, to see your data survive container shutdowns and how each component is communicating with each other in our app.

Up-and-Running Docker App on localhost

Happy Learning! ❤️

--

--

Momal Ijaz
AIGuys

Machine Learning Engineer @ Super.ai | ML Reseacher | Fulbright scholar'22 | Sitar Player