Tuesday, July 23, 2019

How to dockerize a spring boot application

Greetings!

Spring boot helps us to create application very quickly. Docker provides us a way to "build, ship and run" our applications. In a world of Microservices, combining these two gives us powerful ways to create and distribute our Java applications.

I assume you have enough Spring boot and Docker knowledge and want study further to dockerize Spring boot applications.

https://github.com/slmanju/springtime/tree/master/spring-boot-docker

Let's create a simple rest service and dockerize it.

Step 1: Create Spring boot application

Go to https://start.spring.io and fill it as you want. Then add Spring Web Starter as a dependency. This is enough for us create a simple rest service. Download and extract the application. Then add below controller (or anything you like).
package com.slmanju.springbootdocker;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {

    @GetMapping(value = { "", "/"})
    public String index() {
        return "Welcome to spring boot docker integration";
    }

    @GetMapping(value = "/hello")
    public String hello() {
        return "Hello world";
    }

}

Update the application.properties with port number.
server.port = 8081
Create another property file named application-container.properties and update it as below. This is to demonstrate Spring profile use in docker container.
server.port = 8080

You can test the application by runninig;
mvn clean spring-boot:run
curl http://localhost:8081/

Similarly we can run the jar file separately. This is what we need in our dockerfile.
mvn clean package
java -jar target/spring-boot-docker-0.0.1-SNAPSHOT.jar

Step 2: Create Dockerfile

I have used Maven as my build tool. Maven uses target/ path as the build location. For Gradle it is build/libs.
FROM openjdk:8-jdk-alpine
ADD target/spring-boot-docker-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=container", "app.jar"]

What is this file doing?

  • Get openjdk alpine image.
  • Add our spring boot application as app.jar
  • Expose 8080 port (this is port we used in application-container.properties)
  • Entrypoint for the application. I'm passing spring profile to match the exposed port.

Step 3: Create a docker image using our Dockerfile

To create the docker image we need to create jar file first.
mvn clean package
Now we can create the docker image using that jar file.
docker build -f Dockerfile -t hello-docker .


Step 4: Verify image

Verify whether your image is created.
docker image ls

Step 5: Create a container

Our docker image is created. We can create a container using it now.
docker container run -p 8082:8080 hello-docker

Step 6: Test it

Go to web browser and go to localhost.
curl http://localhost:8082/
curl http://localhost:8082/hello

Great! We have containerized our spring boot application.

There is more. We can improve this.

Uppack the generated fat jar into target/extracted (or your desired location).
mkdir target/extracted
cd target/extracted
jar -xf ../*.jar

As you can see, Spring boot fat jar is packaged as layers to separate external dependencies and classes. As we know docker containers are alos layered. We can use thsi to improve our containers.
BOOT-INF/lib
BOOT-INF/classes
META-INF
org

External dependencies will not change often. So, we can add in as the first layer. We can add META-INF into another layer. We add classes as the last layer since it will change over time. Why this is important?

Docker will cache layers. Since our lib (and META-INF) layers do not change very often our build will be faster. Also when we use dockerhub to push and pull our image, that will also faster since we only need to pull our classes layer.
Also in the Dockerfile, I have hard coded the main class boost the start up.
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG APP=target/extracted
COPY ${APP}/BOOT-INF/lib /app/lib
COPY ${APP}/META-INF /app/META-INF
COPY ${APP}/BOOT-INF/classes /app
ENTRYPOINT ["java", "-cp", "app:app/lib/*", "-Dspring.profiles.active=container", "com.slmanju.springbootdocker.SpringBootDockerApplication"]

Now you can create a container (docker container run -p 8082:8080 hello-docker) and test it.

Conclusion

In this article I have discussed how to containerized a spring boot application. There are Maven and Gradle plugin to help you on this. But creating containers in this way is better for learning. If you are like me, want to explore docker (and spring boot) this will help you to get started. So what are waiting for? go and create your container!

References

https://spring.io/guides/gs/spring-boot-docker/
https://spring.io/guides/topicals/spring-boot-docker

1 comment:

  1. Hello Manjula,

    Nice blog! I am editor at Java Code Geeks (www.javacodegeeks.com). We have the JCG program (see www.javacodegeeks.com/join-us/jcg/), that I think you’d be perfect for.

    If you’re interested, send me an email to eleftheria.drosopoulou@javacodegeeks.com and we can discuss further.

    Best regards,
    Eleftheria Drosopoulou

    ReplyDelete