menu

Monday, April 30, 2018

A Simple Spring Boot Docker Application


Introduction:

         In this write I will share a simple spring boot application which will build a docker image and push it to docker hub. I will then use the maven to build and push the docker image automatically. I will also mention on how to pass JVM parameters to the app in docker container and debug the application using JAVA_OPTS variable.

 Implementation:

               First we need a docker environment to be able to build and run docker images. For this example I used the Oracle VM VirtualBox which can be downloaded here. Also I used docker toolbox to run docker commands. After running VirtualBox remember to go the Networks-> Port Forwarding section on GUI of VirtualBox and set the 8080:8080 and 5005:5005 port forwardings which we will need in our example.

         We then will simply create a basic spring boot application with a main class as below;
@SpringBootApplication
@RestController
public class DemoApplication {
    @RequestMapping("/")
    public String home() {
        return "Hello Docker";
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

         Next we will create a Dockerfile directly under the project folder as below;


FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT exec java -jar /app.jar

This Dockerfile will create a docker image from the jdk-alpine base image and add our runnable spring boot jar to the image and as an entry point simply run java -jar command to run the application jar.

To be able to run this Dockerfile automatically we will use the spotify's dockerfile-maven plugin, so we need the following plugin definitions in pom.xml.



<build>
 <plugins>
  <plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
   <version>${spring-boot.version}</version>
  </plugin>
  <plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
  <version>1.3.6</version>
<configuration>
 <repository>${docker.image.prefix}/${project.artifactId}</repository>
     <buildArgs>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
     </buildArgs>
</configuration>
<executions>
<execution>
    <id>default</id>
    <phase>install</phase>
     <goals>
       <goal>build</goal>
       <goal>push</goal>
    </goals>
</execution>
</executions>
  </plugin>
 </plugins>
</build>

The dockerfile-maven-plugin will build and push the docker image to the specified docker hub repository. It will add our spring boot runnable jar which is under the target directory to the docker image. Remember to change the repository section which defined using pom properties above by ${docker.image.prefix}/${project.artifactId} which in the end point to agelenler/demo-docker docker hub repo. You need to create your own docker hub repo and change pom file according to that.

By running mvn clean install command we will automatically create and push our spring boot app docker image to the docker hub.

When you run the mvn clean install command you will see the newly created image both on your local docker environment and on the docker hub repo.

 Run using docker compose:

Let's run our image using a docker-compose file. Create a file named demo-compose.yml as below.

#
# Docker Compose for Demo Service
#
version: "2"
services:
  demo:
    image: agelenler/demo-docker
    ports:
      - "8181:8181"
      - "5005:5005"
    environment:
      - "JAVA_OPTS=agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n"

Open your docker toolbox and first see the image that we just build using "docker images" command.

Then run the following command under the folder that includes demo-compose.yml file.

docker-compose.exe -f demo-compose.yml up

This will run our application and we point our browser to http://localhost:8080 we will see the Hello Docker message.

As you see we added a JAVA_OPTS variable to be able to debug our application on 5005 port which use JPDA transport. When you add a breakpoint to your IDE say for example to the home method you will see that debugging is not working even though we passed required environment variable to our docker compose file. 

How to use JAVA_OPTS in Docker:

So what can we do to use JAVA_OPTS in docker environment which can be used to set JVM or JPDA transport variables ?

We need to modify our Dockerfile and use a placeholder for JAVA_OPTS as below.

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT exec java $JAVA_OPTS -jar /app.jar

By this $JAVA_OPTS placeholder we will be able to pass JAVA_OPTS variables to our docker container.

Conclusion:

In this post we examined the steps required to create and run a docker image from a simple spring boot application. We first need a docker environment like VirtualBox and do the required port forwarding operations. Then simply create a spring boot application and create a Dockerfile to use the output of this application. We then used the spotify dockerfile-maven plugin to automatically build and push our docker image to our local docker environment and docker hub, and used a simple docker-compose file to run our docker image. Finally we saw how to enable passing JVM parameters to a docker container. Note that we need to do 8080:8080 and 5005:5005 port forwardings both on VirtualBox and on docker container as the request first goes through VirtualBox and then forwarded through the docker container inside the virtual box and finally our application run inside this container.

You can download a full source code here.

References:
  • https://spring.io/guides/gs/spring-boot-docker/
  • https://docs.oracle.com/javase/8/docs/technotes/guides/jpda/conninv.html#Invocation
  • https://docs.docker.com/toolbox/toolbox_install_windows/
  • https://github.com/spotify/dockerfile-maven