Dockerize Your Spring Boot Application
It has been nearly a year since my last posting on Docker. The popularity of Docker has since grown by leaps and bounds. In this posting, I will show you how to create a Docker image of a Spring Boot application using a Maven Docker plugin.
It’s the continuation of my Book API example. The book REST service provides functionality to create, search, update, and delete a book item. To keep the example simple, the persistence layer is an in-memory Java HashMap. The full example can be found here. Here is the example book controller.
Maven Docker Plugin
Spotify’s Maven plugin has simplified the creation of Docker image from a Maven project artifacts. The example Maven project consists of two child projects (model and service). Here are the changes to the parent POM.
Parent POM
The skipDockerBuild
tag is set to true in order to skip the Docker build process in the parent project when your run the Docker build from the parent project folder.
Service POM
The skipDockerBuild
tag is overridden in the service POM by setting it to false
. Here is the snippet of the service POM.
Configuration Tags
imageName
: specifies the name of the example Docker image, e.g., docker-example.dockerDirectory
: specifies the location of theDockerfile
. In our example, the location is/src/main/docker
folder. The contents of the dockerDirectory will be copied into the${project.build.directory}/docker
folder.resources
: includes a list ofresource
elements. Each resource describes the files/resources be included with the Docker image.targetPath
: specifies the directory structure to place the build resources. In our example, it defaults to the base project (docker-example-service) directory.directory
: specifies the location of resource. In our example, it’s the ${project.build.directory}.include
: specifies the resources to be included in the Docker image, which in this case is the docker-example-service-1.0.jar.
Dockerfile
A Dockerfile
specifies all the instructions to be read by a Docker engine while building the image.
Instruction
FROM
instruction sets the Base Image for subsequent instructions. FROM must be the first non-comment instruction in the Dockerfile.VOLUME
instruction creates a mount point with the specified name.ADD
instruction copies from and adds them to the filesystem of the image at the path.RUN
instruction executes the command on top of the current image.EXPOSE
instruction informs Docker that the container listens on the specified network ports at runtime.ENV
instruction sets the environment variableENTRYPOINT
allows you to configure a container that will run as an executable.LABEL
instruction adds metadata to an image.
You can find more about Docker instructions here
Docker Build
Assuming you have already installed Docker in your computer. If you haven’t, here is the instruction for installing Docker for Mac. Once you have Docker installed and running, it should show up as a whale
in the top status bar. Execute the following maven command from the directory of the parent project, docker-example
:
mvn clean package docker:build
This should create a Docker image named docker-example
. You will notice maven executing the instructions specified in the Dockerfile.
ibasa$ mvn clean package docker:build
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Docker Example Service 1.0
[INFO] ------------------------------------------------------------------------
…
[INFO] Building image docker-example
Step 1 : FROM frolvlad/alpine-oraclejdk8:slim
---> 00d8610f052e
Step 2 : VOLUME /tmp
---> Using cache
---> 73a7ca0c26ba
Step 3 : ADD docker-example-service-1.0.jar app.jar
---> a18050579c6d
Removing intermediate container 620985e0927e
Step 4 : RUN sh -c 'touch /app.jar'
---> Running in 6a41b2b6aa83
---> 20331fa49094
Removing intermediate container 6a41b2b6aa83
Step 5 : EXPOSE 8080
---> Running in 82ed746dc381
---> 7d4464042f2a
Removing intermediate container 82ed746dc381
Step 6 : ENV JAVA_OPTS ""
---> Running in cc6469042859
---> e08d3943dbfe
Removing intermediate container cc6469042859
Step 7 : ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dapp.port=${app.port} -jar /app.jar
---> Running in 14f08b15e3c7
---> 01e87692e479
Removing intermediate container 14f08b15e3c7
Step 8 : LABEL maintainer "Indra Basak"
---> Running in b017e600dc45
---> dbbcbdfaecf9
Removing intermediate container b017e600dc45
Successfully built dbbcbdfaecf9
[INFO] Built docker-example
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Docker Examples .................................... SUCCESS [ 1.837 s]
[INFO] Docker Example Model ............................... SUCCESS [ 2.002 s]
[INFO] Docker Example Service ............................. SUCCESS [ 8.970 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16.138 s
[INFO] Finished at: 2017-03-17T14:01:01-07:00
[INFO] Final Memory: 50M/486M
[INFO] ------------------------------------------------------------------------
Docker Run
Run (deploy) the newly created Docker image, docker-example
, by executing the docker run
command from the terminal:
docker run --rm -p 8080:8080 --name=cheetos docker-example
Options
--rm
option automatically clean up the container and remove the file system when the container exit.--name
option names the Docker container ascheetos
. In absence of the--name
option, the Docker generates a random name for your container.-p 8080:8080
option publishes all exposed ports to the host interfaces. In this example, port8080
is bothhostPort
andcontainerPort
This should start up the example application and it can be accessed at http://localhost:8080
basak$ docker run --rm -p 8080:8080 --name=cheetos docker-example
..
:: Spring Boot :: (v1.4.5.RELEASE)
…
2017-03-17 21:15:28.493 INFO 1 --- [ main] c.b.example.docker.boot.BookApplication : Starting BookApplication on 233c21816a84 with PID 1 (/app.jar started by root in /)
…
2017-03-17 21:20:26.510 INFO 1 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/info || /info.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-03-17 21:20:26.580 INFO 1 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-03-17 21:20:28.441 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@41975e01: startup date [Fri Mar 17 21:15:28 GMT 2017]; root of context hierarchy
2017-03-17 21:20:29.758 INFO 1 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-03-17 21:20:29.758 INFO 1 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-03-17 21:20:30.070 INFO 1 --- [ main] .m.m.a.ExceptionHandlerExceptionResolver : Detected @ExceptionHandler methods in exceptionProcessor
2017-03-17 21:20:30.766 INFO 1 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-03-17 21:26:19.385 INFO 1 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-03-17 21:26:19.400 INFO 1 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
2017-03-17 21:26:19.514 INFO 1 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147483647
2017-03-17 21:26:19.516 INFO 1 --- [ main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed
2017-03-17 21:26:19.573 INFO 1 --- [ main] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
2017-03-17 21:26:19.636 INFO 1 --- [ main] s.d.s.w.s.ApiListingReferenceScanner : Scanning for api listing references
2017-03-17 21:26:21.079 INFO 1 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-03-17 21:26:21.124 INFO 1 --- [ main] c.b.example.docker.boot.BookApplication : Started BookApplication in 654.396 seconds (JVM running for 309.048)
Docker Commands
Here is a list Docker commands which will come handy.
List Container
Run the docker ps
to list
all the containers. To see all running containers, execute the following command:
bash-3.2$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d03854fb7779 docker-example "java -Djava.security" 7 seconds ago Up 6 seconds 0.0.0.0:8080->8080/tcp cheetos
To see all running containers including the non-running ones, execute the following command:
bash-3.2$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d03854fb7779 docker-example "java -Djava.security" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp cheetos
28b2cff9e7e6 docker-example "java -Djava.security" About an hour ago Exited (0) About an hour ago indra1
d2720676c932 nginx "nginx -g 'daemon off" 4 months ago Exited (0) 4 months ago webserver
Remove Container
To remove a Docker container, execute docker rm
command. This will remove a non-running container.
bash-3.2$ docker rm indra1
indra1
To forcefully remove a running container,
bash-3.2$ docker rm -f cheetos
cheetos
Stop Container
To stop a container, execute docker stop
command:
bash-3.2$ docker stop cheetos
cheetos