Spring Boot Docker Containerization

bhuwanupadhyay

Bhuwan Upadhyay

Posted on June 25, 2020

Spring Boot Docker Containerization

Spring Boot is one of the very popular framework to build the microservices and the docker container is the default choice to run the application in a cloud-native environment.

Docker provides the ability to package and run an application in a loosely isolated environment called a container. So, it's very important to build the right layers of the docker image for your application.

This blog post shows the available options to build a docker image for the spring boot application. Before deep into how to build the docker image, Let's create one very simple spring boot application that will return the given name as a response. After that, we will explore how to build a docker image of this application.

Create a Spring Boot application

To create a Spring Boot application, we'll use Spring Initializr. The application that we'll create uses:
Spring Boot Spring WebFlux Spring Actuator Kotlin

Initialize Project

NAME='Spring Boot Docker Containerization' && \
PRJ=spring-boot-docker-containerization && \
mkdir -p $PRJ && cd $PRJ && \
curl https://start.spring.io/starter.tgz \
    -d dependencies=actuator,webflux \
    -d groupId=io.github.bhuwanupadhyay -d artifactId=$PRJ \
    -d packageName=io.github.bhuwanupadhyay.example \
    -d applicationName=SpringBoot -d name="$NAME" -d description="$NAME" \
    -d language=java -d platformVersion=2.3.1.RELEASE -d javaVersion=11 \
    -o demo.tgz && tar -xzvf demo.tgz && rm -rf demo.tgz

API Example

Create NameManager.kt under package io.github.bhuwanupadhyay.example and add the following text:

@Component
class NameHandler {

    fun findGivenName(req: ServerRequest): Mono<ServerResponse> {
        return Optional.ofNullable(req.pathVariable("given-name"))
                .map { t -> ok().bodyValue("{ \"givenName\": \"$t\"}") }
                .orElseGet { badRequest().build() }
    }

}

@Configuration
class NameRoutes(private val handler: NameHandler) {

    @Bean
    fun router() = router {
        accept(APPLICATION_JSON).nest {
            GET("/names/{given-name}", handler::findGivenName)
        }
    }

}

Docker Containerization

There are four ways to containerize spring boot application. Let's take a look one by one.

Using fat jar

Dockerfile to create a docker image of spring boot application with a fat jar.

FROM amd64/openjdk:14-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar" , "/app.jar"]

A maven profile to build a docker image with plugins: spring-boot-maven-plugin and dockerfile-maven-plugin.

<profile>
  <id>fatJar</id>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>com.spotify</groupId>
        <artifactId>dockerfile-maven-plugin</artifactId>
        <version>1.4.13</version>
        <executions>
          <execution>
            <id>default</id>
            <goals>
              <goal>build</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <repository>docker.io/bhuwanupadhyay/${project.artifactId}-fat-jar</repository>
          <dockerfile>${project.basedir}/src/main/docker/fat-jar.dockerfile</dockerfile>
          <tag>${project.version}</tag>
        </configuration>
      </plugin>
    </plugins>
  </build>
</profile>

To build and test run the following command:

# Build a docker image
mvn clean install -PfatJar

# Run app
docker run -d -p8080:8080 docker.io/bhuwanupadhyay/spring-boot-docker-containerization-fat-jar:0.0.1-SNAPSHOT

# Test API
curl http://localhost:8080/names/hurry

# Output
{ "givenName": "hurry"}

Using classpath in exploded jar

Dockerfile to create a docker image of spring boot application with an exploded jar.

# Stage 0, "builder", extract fat jar
FROM amd64/openjdk:14-alpine as builder
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} /target/app.jar
RUN mkdir -p /target/dependency && (cd /target/dependency; jar -xf ../*.jar)

# Stage 1, "boot-app"
FROM amd64/openjdk:14-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
COPY --from=builder /target/dependency/BOOT-INF/lib /app/lib
COPY --from=builder /target/dependency/BOOT-INF/classes /app
COPY --from=builder /target/dependency/META-INF /app
ENTRYPOINT ["java", "-cp" , "app:app/lib/*", "io.github.bhuwanupadhyay.example.SpringBoot"]

A maven profile to build a docker image with plugins: spring-boot-maven-plugin and dockerfile-maven-plugin.

<profile>
  <id>flatClasspath</id>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>com.spotify</groupId>
        <artifactId>dockerfile-maven-plugin</artifactId>
        <version>1.4.13</version>
        <executions>
          <execution>
            <id>default</id>
            <goals>
              <goal>build</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <repository>docker.io/bhuwanupadhyay/${project.artifactId}-flat-classpath</repository>
          <dockerfile>${project.basedir}/src/main/docker/flat-classpath.dockerfile</dockerfile>
          <tag>${project.version}</tag>
        </configuration>
      </plugin>
    </plugins>
  </build>
</profile>

To build and test run the following command:

# Build a docker image
mvn clean install -PflatClasspath

# Run app
docker run -d -p8081:8080 docker.io/bhuwanupadhyay/spring-boot-docker-containerization-flat-classpath:0.0.1-SNAPSHOT

# Test API
curl http://localhost:8081/names/hurry

# Output
{ "givenName": "hurry"}

Using layertools

Dockerfile to create a docker image of spring boot application with a layertools.

FROM adoptopenjdk:11.0.7_10-jre-hotspot as builder
WORKDIR /app
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
RUN java -Djarmode=layertools -jar app.jar extract

FROM adoptopenjdk:11.0.7_10-jre-hotspot
WORKDIR /app
COPY --from=builder app/dependencies/ ./
COPY --from=builder app/spring-boot-loader/ ./
COPY --from=builder app/snapshot-dependencies/ ./
COPY --from=builder app/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

A maven profile to build a docker image with plugins: spring-boot-maven-plugin and dockerfile-maven-plugin.

<profile>
  <id>layertools</id>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <layers>
            <enabled>true</enabled>
          </layers>
        </configuration>
      </plugin>
      <plugin>
        <groupId>com.spotify</groupId>
        <artifactId>dockerfile-maven-plugin</artifactId>
        <version>1.4.13</version>
        <executions>
          <execution>
            <id>default</id>
            <goals>
              <goal>build</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <repository>docker.io/bhuwanupadhyay/${project.artifactId}-layertools</repository>
          <dockerfile>${project.basedir}/src/main/docker/layertools.dockerfile</dockerfile>
          <tag>${project.version}</tag>
        </configuration>
      </plugin>
    </plugins>
  </build>
</profile>

To build and test run the following command:

# Build a docker image
mvn clean install -Playertools

# Run app
docker run -d -p8082:8080 docker.io/bhuwanupadhyay/spring-boot-docker-containerization-layertools:0.0.1-SNAPSHOT

# Test API
curl http://localhost:8082/names/hurry

# Output
{ "givenName": "hurry"}

Using Buildpacks.

A maven profile to build docker image with plugins: spring-boot-maven-plugin.

<profile>
  <id>buildpacks</id>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>build-image</goal>
            </goals>
            <configuration>
              <imageName>docker.io/bhuwanupadhyay/${project.artifactId}-buildpacks:${project.version}</imageName>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</profile>

To build and test run the following command:

# Build a docker image
mvn clean install -Pbuildpacks

# Run app
docker run -d -p8083:8080 docker.io/bhuwanupadhyay/spring-boot-docker-containerization-buildpacks:0.0.1-SNAPSHOT

# Test API
curl http://localhost:8083/names/hurry

# Output
{ "givenName": "hurry"}

We are done, Thanks for reading! Github

References

💖 💪 🙅 🚩
bhuwanupadhyay
Bhuwan Upadhyay

Posted on June 25, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Spring Boot Docker Containerization
springboot Spring Boot Docker Containerization

June 25, 2020