Spring Boot and Multi-Stage dockerized image with MySQL in Docker Compose(Part 3)

Tariqul Islam
10 min readDec 20, 2019

--

In this part of the article, I discuss the multi-stage docker deployment of spring boot application. Provide a clear idea of how to deploy the spring boot with MySQL using docker-compose. In this article, I am using IntelliJ Idea community edition as my IDE.

Prerequisites

  1. Docker Installed in PC
  2. The IntelliJ idea community edition
  3. git installed in the development PC

For Local Development without docker, the development Prerequisites are

  1. Java Installed In PC
  2. MySQL Server 5.7 community edition server

Before you start reading this article, you can read my second part of the article series which includes details of setup the spring boot application with MySQL.

Either you can create spring boot application from scratch or you can download from my GitHub repository from below link

> git clone https://github.com/tariqulislam/spring-boot-docker.git

Then checkout to below branch and don’t use master branch, because it already configured with docker and docker-compose .

> git checkout freature/project-without-docker

After downloading, you must open the application to Intellij Idea and you can find project structure like below

Then create the two file named Dockerfile and docker-compose.yml at the root directory of the application

└── stater
├── src
│ ├── ------
├── Dockerfile
└── docker-compose.yml

Docker Multi-Stage building for java spring boot application

What is multistage build in docker?

This docker feature introduces in the 17.05 version. that allows the developer to create multiple intermediate images using the same Dockerfile. with a multi-stage build, the developer can use multiple FROM statement in the Dockerfile, In each FROM instruction can use different base image source, each of them begins the new stage of build image. The developer can copy the one stage artifacts to another stage, leaving behind everything which is not needed for production or final image. It is very useful to reduce the image size of the docker. We can build the specific stage by using `target` tag in docker if developer wants to.

Now, I am discussing the instruction keyword for docker before writing the instruction code in Dockerfile . If you already know about the instructions of the docker and docker multi-stage build, please leave this part of the article.

FROM -> by this instruction we can specifiy the base image, where to start building image(we can specify the official docker image name which is abailable in docker hub)If we want to specify the build stage or target name in docker just add the build take after image name. the syntax isFROM [base-image-name] as [target-name or stage-name]For ExampleFROM openjdk:8-jdk-alpine as build
-------------------- -----
[base-image-name] [target-name or stage-name]
For multiple stage or target name for build image using multiple stage feature of docker.
------------------------------------------------------------------

The other instructions details elaboration are discussed below

WORKDIR --> It is used for switch to that directory for copy or add files or run some command in that directory in container. It also helps to create the new directory, which are used for building the image, copy the code and settings to it.ENV --> It's use for environment variable of docker images. Docker image contains the lightweight operating system. we can push and override the environment variable from  calling itCOPY --> Helps to copy the file from host machine to docker container.ADD --> Helps to copy the file from host machine to docker image or container. It as like as copy instractions, it has extra functionality, it copy the file from remote alsoRUN --> Run the command inside the docker image at image building time. It support valid operating system command.   executes command(s) in a new layer and creates a new imageCMD -->  sets default command and/or parameters, which can be overwritten from command line when docker container runsENTRYPOINT -->  used for configure the container that will run as an executable. We can add the multiple command shell file .sh file in entrypoint instructions. Also add the multiple command in this instructionEXPOSE --> It will help to expose the port for to in docker to access by other container or images. we also mapping with host computer port during run the docker container.

The Dockerfile , which I write with multiple stage build instruction for spring-boot-docker project.

Discuss each instruction and stage for building process in below

First describing the build stage of spring-boot-docker project:

FROM openjdk:8-jdk-alpine as build---------------------------------------------------------------
I am taking the base image as `openjdk:8-jdk-alpine` add the target or stage name as `build`
---------------------------------------------------------------
WORKDIR /app----------------------------------------------------------------Specify the work directory and switch to the directory for base image named app
----------------------------------------------------------------
COPY mvnw .
COPY .mvn .mvn
---------------------------------------------------------------
Copy the manw maven executable wrapper file and .mvn maven repository and configuration folder to /app directory.
---------------------------------------------------------------
COPY pom.xml .RUN chmod +x ./mvnw---------------------------------------------------------------
Then Copy the pom.xml file to /app directory in docker image
---------------------------------------------------------------
RUN ./mvnw dependency:go-offline -B--------------------------------------------------------------------
It will download the maven dependency from server make it offline or local.
:go-offline --> ensure you have all of your dependencies installed locally before you begin to work offline
--------------------------------------------------------------------
RUN ./mvnw package -DskipTests--------------------------------------------------------------------
This will create the fat jar for my application, -DskipTests will skip the test for application during the building package
--------------------------------------------------------------------
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)--------------------------------------------------------------------
Create the `target/dependency` directory and go to that directory. And `jar -xf ../*.jar` will extract the fat jar, create seperate jar for every dependency and create the small jar for application.
--------------------------------------------------------------------

Production stage docker image building Instructions for spring-boot-docker project:

FROM openjdk:8-jre-alpine as production--------------------------------------------------------------------
I am taking the base image as `openjdk:8-jdk-alpine` add the target or stage name as `production`
--------------------------------------------------------------------
ARG DEPENDENCY=/app/target/dependency--------------------------------------------------------------------
Create the argument so we can use it as argument for any instruction for docker.
--------------------------------------------------------------------
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib--------------------------------------------------------------------
copy the BOOT-INF/lib file from `build` stage artifact to `production` stage /app/lib folder
--------------------------------------------------------------------
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF--------------------------------------------------------------------
copy the BOOT-INF file from `build` stage artifact to `production` stage /app/META-INF folder
--------------------------------------------------------------------
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app--------------------------------------------------------------------
copy the all the java classes from `build` stage artifact to `production` stage /app folder
--------------------------------------------------------------------
ENTRYPOINT ["java", "-cp", "app:app/lib/*","com.example.starter.StaterApplication"]-------------------------------------------------------------------
To run the spring boot application use command `java -cp 'app:<app jar folder>' <main entry class name of application>`, ENTRYPOINT takes the command and command sequence and argument with array so we need to declare as like as provide in code snappit
--------------------------------------------------------------------

Add services in the docker-compose file, which contains the mysql-service service and web service

Before adding the service to docker-compose.yml file for spring-boot-docker project, I just provide a brief description of all keywords related to the docker-compose file, which are discussed below, if you already know about the keywords or Instructions, please escape the section.

version: contains docker compose file format version. There is several file format available form 1.x to 3.x.services: services blocks contains all the applications or services definition which is needed for run the applications successfully. for example if we want to run the web apps, we need a database and other services. so we can add multiple service or app definitions in services blocks.build: there is a lot of option for build instruction. we can build image from another image. we can provide the target folder to build in `build` block. build has some sub blocks, those are

context: which will takes to a directory, which contains a Dockerfile, or a url to a git repository. where the value supplied with relative path, it will send to docker deamon to building process.
build:
context: ./<dir-name>


dockerfile: if we want to specify or use different docker file to build docker image by docker compose file. we need to use it.

build:
context: ./<dir-name>
dockerfile: <docker-filename>
command: Run the command in docker container from the docker-compose.yml file. it can run any valid os and os application command inside the container.volumes: we can mount the specific host path or named volume to docker contains which contains the media files and other files needed for run the application. for my apps i have to mount the /app folder to container.expose: provide port access to docker system so other container can access the service by this port. expose only publish the port within docker system, so host computer service or app can not get this port to access service.ports: it use for mapping the ports between HOST:DOCKERCONTAINER. other docker container service and host computer service can get access to that port, to get service information.if we run the our django application in 8000 port in DOCKERCONTAINER, we want to map 4000 port with host computer just follow the rules
ports:
- HOST-PORT:DOCKERCONTAINER-PORT
(e.g)
ports:
- 4000:8000
environment: it will add and override the environment variable to docker container or image. which is then you can use it as environment variable to the specified docker container image.env_file: we can add environment variable file there. we can use it also define the or override the environment variable of the docker container or image. we can add the multiple env file in env_file block.depends_on: developer can add the dependency service, which is needed for run the or build image in this section. it will ensure all the dependency service are available before start build the docker images or service.networks: it use in two context. one is in the service definition. other is used for create the network in docker environment. network in side the service definition will assing the network to the service.

The docker-compose file for spring-boot-docker projects are below

# spring-boot-docker/docker-compose.ymlversion: "3.7"services:
mysql-service:
image: mysql:5.7
networks:
- spring-boot-mysql-network
restart: always
environment:
- MYSQL_ROOT_PASSWORD=ronnie01
- MYSQL_DATABASE=testapp
web-service:
build:
context: ./
dockerfile: Dockerfile
ports:
- "4000:4000"
networks
:
- spring-boot-mysql-network
depends_on:
- mysql-service
networks:
spring-boot-mysql-network:
driver: bridge

Now, I elaborate each definition for every service in the below section

mysql-service:
image: mysql:5.7
networks:
- spring-boot-mysql-network
restart: always
environment:
- MYSQL_ROOT_PASSWORD=ronnie01
- MYSQL_DATABASE=testapp
------------------------------------------------------------------
This yml configuration meaning is there is service named `mysql-service`, it will download the base image named `mysql:5.7` from docker hub to build the service.
It will bind with network name `spring-boot-mysql-network`, the service will restart time docker is run.It has some environment variable in `environment` section. which helps the mysql to create password for `root` user. create the database for `testapp` at mysql-service image container-----------------------------------------------------------------
web-service:
build:
context: ./
dockerfile: Dockerfile
ports:
- "4000:4000"
networks
:
- spring-boot-mysql-network
depends_on:
- mysql-service
-------------------------------------------------------------------
This is `web-service`, it build context is ./ means it build the image take the root path of the application where the compose file is created.
it takes the `Dockerfile` from ./ root directory to build the image. container will run at 4000 port as tcp connection in docker environment, expose the 4000 tcp port to host access the service.it use the network named `sprint-boot-mysql-network`. it depends on `mysql-service` service, without it service or docker container will not build properly in docker environment.
--------------------------------------------------------------------
networks:
spring-boot-mysql-network:
driver: bridge
------------------------------------------------------------------
This definition create the network name `spring-boot-mysql-network`, the two service named `web-service` and `mysql-service` will bind together with that network.
Bridge: The bridge driver creates a private network internal to the host so containers on this network can communicate. External access is granted by exposing ports to containers. Docker secures the network by managing rules that block connectivity between different Docker networks.
-------------------------------------------------------------------

Change the MySQL server name in application.properties file.

# Modified application.properties file
# mysql datasoruce string for jdbc driver connection
# For Docker and Docker compose, database name must be `mysql-service` environment variable `MYSQL_DATABASE` value.
spring.datasource.url =jdbc:mysql://mysql-service:3306/testapp?useSSL=false# username and password for mysql server spring.datasource.username =root# password will be empty is root has not password.(e.g 'pass1234'), if you use docker-compose or docker. password should be same as `mysql-service` environment variable `MYSQL_ROOT_PASSWORD` value.spring.datasource.password =# Important for jdbc driver to understand version of mysql serverspring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect# DDL operation run by this parameter. it will update the database fields and update changes in database spring.jpa.hibernate.ddl-auto = update
# Logged in command line to see sql error and jdbc exception
logging.level.org.hibernate.SQL= DEBUG
logging.level.org.hibernate.type
=TRACE
# Expose the embdded server port to 4000server.port = 4000

Notes: if you are using MySQL5.7 use MySQL57Dialect or If you want to configure the spring boot with MySQL 8, You need to add MySQL8Dialect to application.properties file. Read all the comment in application.properties file to understand how configuration of data in spring boot application work with docker.

I change the MySQLserver-name at spring.datasource.url properties, which is localhost to mysql-service . Which is a service container name in docker-compose yml file.

# spring-boot-docker/docker-compose.ymlversion: "3.7"services:
mysql-service:
image: mysql:5.7
networks:

Run the application using docker-compose

Go to terminal and from the root folder this section you can run the following command

> docker-compose up --build

We can see the 2 stage docker image build in command line window

Now the Application is running and connect with MySQL server, I am using postman tool for testing

I already share the repository and code in the first part of my articles. I share it again for friendly access

--

--

Tariqul Islam
Tariqul Islam

Written by Tariqul Islam

Tariqul Islam have 10+ years of software development experience. He knows Python, Java, C#,PHP and other Computer languages.

Responses (5)