This documentation provides step-by-step instructions on how to containerize a Java application using Docker. Containerization allows you to package your application along with its dependencies into a self-contained unit called a Docker container, which can be easily deployed and run on any system with Docker installed.
I will be containerizing the AWS-Lift-and-Shift-project.
SCENARIO:
We already have a multi tier application stack running on EC2 instances in the cloud environment which has many services to be managed by the operations or DevOps team. We need to run continous changes and regular deployments on the application.
PROBLEMS:
- High Operational Expenditure: In the previous set up in the cloud, We will spend more in procuring the resources as the resources are over provisioned. This will increase the regular operational cost. In the application we deployed in AWS, we provisioned 10GB of RAM on the EC2 instance. The application does not use that much RAM and the remaining RAM is not used and they accumulate bills.
- Human Errors in Deployment: There is a chance of human error in deployment of the resources. Automation can reduce the chance of human error.
- Microservice: The application setup is not compatible with microservice architecture.
- Not Portable: The application setup is not portable and the environment are not in sync. There are chances that it will work on dev envoronment and not work in production environment.
SOLUTION:
- Containers: We can solve the above problems using containers. Containers consume very low resources and suites very well for microservices design. The image is packaged with all the dependencies and libraries so it is portable, reuseable and repeatable and can work across any environment.
Services to be used in containerizing this project:
- Docker - container run time environment.
- Nginx
- Tomcat
- Memcache
- Rabbitmq
- MySQL
STEPS:
- We should already be aware of the steps to setup the stack from the AWS-Lift-and-Shift-project.
- Fork the vprofile-project to my github.
- Find the right base images from dockerhub for all the services that we will be using for this setup
- Write Dockerfiles to customize images (mysql, tomcat and nginx) and build the images.
- Write a docker compose.yml file to run multiple containers.
- Test the image and push to dockerhub.
ARCHITECTURAL DESIGN
First, fetch the source code from github, then write Dockerfiles for the services that needs customization. The base images mentioned in the Dockerfile will be pulled from dockerhub. Then build the images. Once the images are ready, we will mention all the containers with the image name in the docker compose file and test it. If this works well we will then push the images to dockerhub.
Finding the right base images from Dockerhub
First, I will check the requirements we need to set up these services (If there are any customizations to be made) and see how it relates to the images we have on dockerhub.
For our application we need five images for our services:
Tomcat - Customized image will be created from base image.
MySQL - Customized image will be created from base image.
Memcached - Use base image and override settings.
RabbitMQ - Use base image and override settings.
Nginx - Customized image will be created from base image.
N/B: I will be using official images as they have good documentation that will help in decision making on the version given to me by the developers.
Setting up the Docker Engine
We can use Ec2 instance or Vagrant to set this up. If i use EC2 instance i need to use atleast a t2.small to make this setup work efficiently but i will be incuring cost. I will be using vagrant to save cost.
Create a folder
$ mkdir Docker-Engine
$ cd Docker-Engine
Pick an ubuntu box from the Vagrant cloud and run
$ vagrant init <vagrant-box>
Open the Vagrantfile and assign a unique IP address and increase the RAM size for the setup to run faster.
Clone the repository
$ git clone https://github.com/dybran/Containerizing-a-JAVA-Stack-Application.git
Then $ vagrant ssh
to login
Install Docker Engine using the Documentation.
Add the vagrant user to the docker group
$ usermod -aG docker vagrant
Log out and login then run
$ id vagrant
N/B: Make sure it is in the same folder as the vagrantfile.
Create the repositories in Dockerhub
Create repositories in dockerhub for the application, the database and the nginx images.
Write the Dockerfiles for Tomcat, Mysql and Nginx
For the Tomcat: This is where the application will reside. We need to build the artifact before building the image. When building the artifact, so many dependencies are downloaded which will increase the size of the image and we need to make the image as small as possible for portability.
We will introduce a multi stage Dockerfile to help us build the artifacts in another image then copy over just the artifacts to another image and built.
Create a Dockerfile for the app - tomcat
FROM openjdk:11 AS BUILD_ARTIFACT_IMAGE
RUN apt update && apt install maven -y
RUN git clone https://github.com/dybran/vprofile-project.git
RUN cd vprofile-project && git checkout docker && mvn install
FROM tomcat:9-jre11
LABEL "Project"="Vprofile"
LABEL "Author"="Solomon Onwuasoanya"
LABEL "Description"="Building VprofileApp Image"
RUN rm -rf /usr/local/tomcat/webapps/*
COPY --from=BUILD_ARTIFACT_IMAGE vprofile-project/target/vprofile-v2.war /usr/local/tomcat/webapps/ROOT.war
EXPOSE 8080
CMD ["catalina.sh", "run"]
Create a Dockerfile for DB - MySQL
FROM mysql:8.0.33
LABEL "Project"="Vprofile"
LABEL "Author"="Solomon Onwuasoanya"
LABEL "Description"="Building Vprofiledb image"
ENV MYSQL_ROOT_PASSWORD="sa4la2xa"
ENV MYSQL_DATABASE="accounts"
ADD db_backup.sql docker-entrypoint-initdb.d/db_backup.sql
Create a Dockerfile for Web - Nginx
FROM nginx
LABEL "Project"="Vprofile"
LABEL "Author"="Solomon Onwuasoanya"
LABEL "Description"="Building Vprofileweb image"
RUN rm -rf /etc/nginx/conf.d/default.conf
COPY nginvproapp.conf /etc/nginx/conf.d/vproapp.conf
Write a docker-compose.yml file to build and test it together.
Create docker-compose.yml file
Use information from vprofile-project\src\main\resources\application.properties to write the docker-compose.yml file.
version: '3.8'
services:
vprodb:
build:
context: ./Docker-files/web
image: dybran/vprofiledb
ports:
- "3306:3306"
volumes:
- volume-vprodb:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=sa4la2xa
vprocache01:
image: memcached
ports:
- "11211:11211"
vpromq01:
image: rabbitmq
ports:
- "15672:15672"
environment:
- RABBITMQ_DEFAULT_USER=guest
- RABBITMQ_DEFAULT_PASS=guest
vproapp:
build:
context: ./Docker-files/app
image: dybran/vprofileapp
ports:
- "8080:8080"
volumes:
- volume-vproapp:/usr/local/tomcat/webapps
vproweb:
build:
context: ./Docker-files/web
image: dybran/vprofileweb
ports:
- "80:80"
volumes:
volume-vprodb: {}
volume-vproapp: {}
Build and Run
Switch to root user
$ sudo -i
Change into the /vagrant/
$ cd /vagrant/
Change into the vprofile-project
$ cd vprofile-project
Then run
$ docker compose up -d
Check images
$ docker images
Check for running conainers
$ docker ps
Then we can access the app from the webpage using the IP address
Login using the log in details below
Login: admin_vp
Password: admin_vp
Verify our entire setup
Succesful login shows that the database server is connected.
To check if the rabbitMQ is functioning, we click on rabitMQ on the website.
To check the memcached, click on all users and select a user.
Then go back and select the same user
We will find out that the first time we selected the user the data was from the Database and it took longer time to open but the second time it was faster to access and was from the cache.
Push to dockerhub
To push to dockerhub, we login
$ docker login
Provide dockerhub username and password.
Then push images
$ docker push <image-name>
The DevOps Team works with the developement Team to understand the development/setup steps for the application and also understand the different services that is needed for the setup.