Master Docker : How to Successfully Deploy a 3-Tier Application - Zero to Hero Guide (Part 3)
Hi Amigos! Welcome to the 3rd blog in our Docker series. In this post, we’ll dive into how to create our own Docker image and run a container based on it. Let’s get started !
What is Dockerfile ?
The Dockerfile is a simple text file that contains the instructions needed to create a new container image. Once the Dockerfile is ready, we can easily build the image from it
Key Properties
FROM : Specifies the base image to use for the Docker image
FROM node:alpine
RUN : Executes commands in the container during the image build process
RUN npm install
CMD : Provides the default command to run when a container is started from the image
CMD [ "java" , "-jar", "devops-proj.jar"]
ENTRYPOINT : Sets a default application to run every time a container is created from the image
ENTRYPOINT ["python3", "app.py"]
COPY : Copies files or directories from the host system into the image
COPY ./package.json /react-app
ADD : Similar to COPY, but with additional capabilities like extracting tar files and accessing remote URLs
ADD https://example.com/app.tar.gz /app/
WORKDIR : Sets the working directory for any subsequent RUN, CMD, ENTRYPOINT, COPY, and ADD instructions
WORKDIR /react-app
ENV : Sets environment variables
ENV NODE_ENV development
EXPOSE : Informs Docker that the container listens on the specified network ports at runtime
EXPOSE 3306
VOLUME : Creates a mount point with the specified path and marks it as holding externally mounted volumes from the host or other containers
VOLUME /data
ARG : Defines a variable that users can pass at build-time to the builder with the docker build command
ARG VERSION=1.0
Hands-on Demo
Let’s develop a three-tier application using React, Spring, and MySQL, leveraging Dockerfiles to containerize our project
Firstly, we will create a separate network and volume to run our application
# Bridge Network Creation
docker network create tier3-network
4182929d476d8e3db8961850633ecf9bdd5c2fb84bacb7f89827dbac8f52050c
# Named Volume Creation
docker volume create tier3-volume
tier3-volume
We will build our MySQL image and then proceed to run our MySQL container first. Our Dockerfile looks like below
# Use the official MySQL image as the base image
FROM mysql:8.0.34-debian
RUN mkdir /app
WORKDIR /app
# Set environment variables for MySQL (replace with your desired values)
ENV MYSQL_ROOT_PASSWORD=password*
ENV MYSQL_DATABASE=devops
ENV MYSQL_USER=username
ENV MYSQL_PASSWORD=password*
# Copy your custom database initialization script into the container
# Expose the MySQL port
EXPOSE 3306
# Start MySQL when the container runs
CMD ["mysqld"]
Building and running our docker image
docker build -t mysql-image .
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql-image latest fddac1f953eb 5 seconds ago 599MB
docker run -itd --name mysql --network tier3-network -v tier3-volume:/app sql
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
96ef6565100d sql "docker-entrypoint.s…" 9 minutes ago Up 9 minutes 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql
We will build a Docker image for Spring Boot and then run the container based on it
FROM openjdk:17-alpine
WORKDIR /appy
COPY devops-proj.jar .
EXPOSE 8080
CMD [ "java" , "-jar", "devops-proj.jar"]
Let’s build and run our spring container
docker build -t spring .
REPOSITORY TAG IMAGE ID CREATED SIZE
spring latest d8558192033e 16 minutes ago 382MB
docker run -itd --name backend --network tier3-network -v tier3-volume:/appy -e "spring.datasource.url=jdbc:mysql://mysql:3306/devops" -e "spring.datasource.username=root" -e "spring.datasource.password=password*" -e "cors.origin=*" -p 8082:8080 spring
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a8d13f6d62bf spring "java -jar devops-pr…" 6 minutes ago Up 6 minutes 0.0.0.0:8082->8080/tcp, :::8082->8080/tcp backend
Lastly, we will build a Docker image for the React app and then run the container based on it
# Fetching the latest node image on alpine linux
FROM node:alpine AS development
# Declaring env
ENV NODE_ENV development
# Setting up the work directory
WORKDIR /react-app
# Installing dependencies
COPY ./package.json /react-app
RUN npm install
# Copying all the files in our project
COPY . .
EXPOSE 3000
# Starting our application
CMD npm start
Let’s build and run our spring container
docker build -t reacty .
REPOSITORY TAG IMAGE ID CREATED SIZE
reacty latest 8017a5c05ef9 14 minutes ago 961MB
docker run -itd --name reacty --network tier3-network -v tier3-volume:/app -p 5000:3000 -e "REACT_APP_SPRING_BOOT_URL=http://3.109.58.102:8082" reacty
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
08f0f62461ca reacty "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 0.0.0.0:5000->3000/tcp, :::5000->3000/tcp reacty
Also, I will share ideas to minimize the image size in upcoming blogs
I’m using the Spring container’s IP address instead of its name to avoid browser network errors. When using port forwarding for your frontend UI, it can’t resolve the backend by name due to different networking setups between your laptop and Docker
A simple fix is to expose the backend with port forwarding too, and then use the port forwarding IP and port from the frontend
So, for now, go with this setup for testing the app. In Kubernetes, we will use different solutions
Now, all our containers MySQL, Spring, and React are up and running. We’ll test it out now
Home Page :
Adding new user :
User added successfully :
Edit the user :
Deleting the user :
Yay! We’ve successfully deployed our 3-tier application in the Docker environment !
Don’t forget to push the images to the registry for later use. For now, I am using the Docker Hub registry. Create an account here DockerHub
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sql latest 1bf44112ef26 About an hour ago 599MB
# Adding a Tag to our image
docker tag sql userid/sql:v1
root@ip-172-31-15-211:~/springboot-react-Devops# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sql latest 1bf44112ef26 About an hour ago 599MB
userid/sql v1 1bf44112ef26 About an hour ago 599MB
# Don't forgot to remove the older image
docker rmi sql
Untagged: sql:latest
# Docker login
docker login
Log in with your Docker ID or email address to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com/ to create one.
Username: user_id
Password:
Login Succeeded
# Pushing the image to Docker hub
docker push user_id/sql:v1
The push refers to repository [docker.io/user_id/sql]
5f70bf18a086: Pushed
5acdc30643cf: Pushed
4b34c7838eb3: Mounted from library/mysql
e471534b9150: Mounted from library/mysql
v1: digest: sha256:175fe67441b0e0b108c6176b0c003bc1cb65d228c4632b6065ea822c6da40e3f size: 3241
# Docker prune
docker system prune # Removes all unused data, including stopped containers, networks not used by any containers, dangling images, and unused volumes
# Alternatively, you can clean up a specific resource by using "docker resource prune" such as "docker volume prune"
That’s a wrap! Thanks for reading. Loads of love to you and your family ❤️