September 4, 2018

Spring Sweets: Dockerize Spring Boot Application With Jib

Jib is an open-source Java library from Google for creating Docker images for Java applications. Jib can be used as Maven or Gradle plugin in our Spring Boot project. One of the nice feature of Jib is that it adds layers with our classes, resources and dependency libraries for the Docker image. This means that when only class files have changed, the classes layer is rebuild, but the others remain the same. Therefore the creation of a Docker image with our Spring Boot application is also very fast (after the first creation). Also the Maven and Gradle plugins have sensible defaults, like using the project name and version as image name, so we don't have to configure anything in our build tool. Although Jib provides options to configure other values for the defaults, for example to change the JVM options passed on to the application.

Let's see Jib in action for a simple Spring Boot application. In our example we use Gradle as build tool with the following Spring Boot application:

// File: src/main/java/mrhaki/spring/sample/SampleApplication.java
package mrhaki.spring.sample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
@SpringBootApplication
public class SampleApplication {

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

    @GetMapping("/message")
    Mono<String> message() {
        return Mono.just("Spring Boot sample application.");
    }
    
}

Next we add the Jib plugin to our Gradle build file:

// File: build.gradle
...
plugins {
    id 'com.google.cloud.tools.jib' version '0.9.10'
}
...

With the Gradle task jibDockerBuild we can create a Docker image for our local Docker. Our project is called springboot-sample with version 1.0.0-SNAPSHOT, so we get the Docker image springboot-sample:1.0.0-SNAPSHOT:

$ ./gradlew jibDockerBuild
Tagging image with generated image reference springboot-sample:1.0.0-SNAPSHOT. If you'd like to specify a different tag, you can set the jib.to.image parameter in your build.gradle, or use the --image=<MY IMAGE> commandline flag.
warning: Base image 'gcr.io/distroless/java' does not use a specific image digest - build may not be reproducible

Containerizing application to Docker daemon as springboot-sample:1.0.0-SNAPSHOT...

Getting base image gcr.io/distroless/java...
Building dependencies layer...
Building resources layer...
Building classes layer...
Finalizing...
Loading to Docker daemon...

Container entrypoint set to [java, -cp, /app/resources/:/app/classes/:/app/libs/*, mrhaki.spring.sample.SampleApplication]

Built image to Docker daemon as springboot-sample:1.0.0-SNAPSHOT

BUILD SUCCESSFUL in 3s
3 actionable tasks: 1 executed, 2 up-to-date
$

Notice that the default image our Docker image is build on is gcr.io/distroless/java, but we can change that in our Gradle build file via the jib configuration block.

Our image is available so we can run a Docker container based on our image and check the URL of our application:

$ docker run -d --name springboot-sample -p 8080:8080 springboot-sample:1.0.0-SNAPSHOT
5d288cbe4ed606760a51157734349135d4d4562072e1024f4585dff370ac6f99
$ curl -X GET http://localhost:8080/message
Spring Boot sample application.
$

In the following example we add some configuration for Jib in our Gradle build file:

// File: build.gradle
...
jib {
    to {
        image = "springboot-mrhaki:${project.version}"
    }

    container {
        // Set JVM options.
        jvmFlags = ['-XX:+UnlockExperimentalVMOptions', '-XX:+UseCGroupMemoryLimitForHeap', '-Dserver.port=9000']
        // Expose different port.
        ports = ['9000']
        // Add labels.
        labels = [maintainer: 'mrhaki']
    }

}
...

When we run the jibDockerBuild task a new Docker image is build:

$ warning: Base image 'gcr.io/distroless/java' does not use a specific image digest - build may not be reproducible

Containerizing application to Docker daemon as springboot-mrhaki:1.0.0-SNAPSHOT...

Getting base image gcr.io/distroless/java...
Building dependencies layer...
Building resources layer...
Building classes layer...
Finalizing...
Loading to Docker daemon...

Container entrypoint set to [java, -XX:+UnlockExperimentalVMOptions, -XX:+UseCGroupMemoryLimitForHeap, -Dserver.port=9000, -cp, /app/resources/:/app/classes/:/app/libs/*, mrhaki.spring.sample.SampleApplication]

Built image to Docker daemon as springboot-mrhaki:1.0.0-SNAPSHOT

BUILD SUCCESSFUL in 3s
3 actionable tasks: 1 executed, 2 up-to-date
$ docker run -d --name springboot-mrhaki -p 9000:9000 springboot-mrhaki:1.0.0-SNAPSHOT
6966ff3b5ca8dae658d59e39c0f26c11d22af7e04e02ad423516572eb5e0e0bd
$ curl -X GET http://localhost:9000/message
Spring Boot sample application.
$

Jib also adds the jib task to deploy a Docker image to a registry instead of the local Docker daemon. Jib can use several command-line applications to authenticate with registries or we can set authentication information in the Gradle build file. Check Jib on Github for more documentation of the options and features.

Written with Spring Boot 2.0.4.RELEASE and Jib Gradle plugin 0.9.10.