Support Buildpacks in Spring Boot

Roman Ivanov
4 min readJan 27, 2020

A few days ago Spring Boot version 2.3.0.M1 was released. In the first string of the release notes, you can find project CNB mentioned, which is an attempt to simplify developer burden. It can automize build from source code. This is because, in my current project, we use microservices, but in the future, we will move to docker. CNB allows the developer to avoid writing and maintaining docker files.

The developer needs only one command to build a docker image.

Also, CNB supports a lot of interesting and useful technologies, like OCI support, modular structure, and layer caching availability. Pivotal promises that that system is very broad and can be applied to many systems.

So let’s check it out. The first step — download an empty project from the site start.spring.io. That’s it. We can make an image with our application. Just run «./gradlew bootBuildImage» in Gradle or «./mvnw spring-boot:build-image» in Maven.

After starting we will get the process log.

First, the plugin gets the base artifacts:

> Task :bootBuildImage
Building image ‘docker.io/library/cnb:0.0.1-SNAPSHOT’
> Pulling builder image ‘docker.io/cloudfoundry/cnb:0.0.43-bionic’
> Pulled builder image ‘cloudfoundry/cnb@sha256:c983fb9602a7fb95b07d35ef432c04ad61ae8458263e7fb4ce62ca10de367c3b’
> Pulling run image ‘docker.io/cloudfoundry/run:base-cnb’
> Pulled run image ‘cloudfoundry/run@sha256:ba9998ae4bb32ab43a7966c537aa1be153092ab0c7536eeef63bcd6336cbd0db’
> Executing lifecycle version v0.5.0
> Using build cache volume ‘pack-cache-7cfae5296b92.build’

Detectors scan the project and determine which dependencies are required:

> Running detector
[detector] 6 of 13 buildpacks participating
[detector] org.cloudfoundry.openjdk v1.0.80
[detector] org.cloudfoundry.jvmapplication v1.0.113
[detector] org.cloudfoundry.tomcat v1.1.74
[detector] org.cloudfoundry.springboot v1.0.157
[detector] org.cloudfoundry.distzip v1.0.144
[detector] org.cloudfoundry.springautoreconfiguration v1.0.159

Dependencies are downloaded and the assembly starts:

> Running builder
[builder]
[builder] Cloud Foundry OpenJDK Buildpack v1.0.80
[builder] OpenJDK JRE 11.0.5: Contributing to layer
[builder] Downloading from https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.5%2B10/OpenJDK11U-jre_x64_linux_hotspot_11.0.5_10.tar.gz
[builder] Verifying checksum
[builder] Expanding to /layers/org.cloudfoundry.openjdk/openjdk-jre
[builder] Writing JAVA_HOME to shared

[builder] Cloud Foundry Spring Boot Buildpack v1.0.157
[builder] Spring Boot 2.3.0.M1: Contributing to layer
[builder] Writing CLASSPATH to shared
[builder] Process types:
[builder] spring-boot: java -cp $CLASSPATH $JAVA_OPTS dev.ivanov.cnb.CnbApplication
[builder] task: java -cp $CLASSPATH $JAVA_OPTS dev.ivanov.cnb.CnbApplication
[builder] web: java -cp $CLASSPATH $JAVA_OPTS dev.ivanov.cnb.CnbApplication

The image is building:

> Running exporter
[exporter] Adding layer ‘app’
[exporter] Adding layer ‘config’
[exporter] Adding layer ‘launcher’
[exporter] Adding layer ‘org.cloudfoundry.openjdk:openjdk-jre’
[exporter] Adding layer ‘org.cloudfoundry.jvmapplication:executable-jar’
[exporter] Adding layer ‘org.cloudfoundry.springboot:spring-boot’
[exporter] Adding layer ‘org.cloudfoundry.springautoreconfiguration:auto-reconfiguration’
[exporter] *** Images (2757ab54378d):
[exporter] docker.io/library/cnb:0.0.1-SNAPSHOT

The dependencies are caching:

> Running cacher
[cacher] Caching layer ‘org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b’
[cacher] Caching layer ‘org.cloudfoundry.jvmapplication:executable-jar’
[cacher] Caching layer ‘org.cloudfoundry.springboot:spring-boot’
[cacher] Caching layer ‘org.cloudfoundry.springautoreconfiguration:46ab131165317d91fd4ad3186abf755222744e2d277dc413def06f3ad45ab150’
Successfully built image ‘docker.io/library/cnb:0.0.1-SNAPSHOT’BUILD SUCCESSFUL in 25s

We can use docker command images for the generated images. And it can be started with the “run” command.

Let’s add new dependencies and start the assembly again.

Now the system is scanning the caching layers:

> Running restorer[restorer] Restoring cached layer ‘org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b’
[restorer] Restoring cached layer ‘org.cloudfoundry.jvmapplication:executable-jar’
[restorer] Restoring cached layer ‘org.cloudfoundry.springboot:spring-boot’
[restorer] Restoring cached layer ‘org.cloudfoundry.springautoreconfiguration:46ab131165317d91fd4ad3186abf755222744e2d277dc413def06f3ad45ab150’
> Running analyzer[analyzer] Using cached layer ‘org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b’
[analyzer] Writing metadata for uncached layer ‘org.cloudfoundry.openjdk:openjdk-jre’
[analyzer] Using cached launch layer ‘org.cloudfoundry.jvmapplication:executable-jar’
[analyzer] Rewriting metadata for layer ‘org.cloudfoundry.jvmapplication:executable-jar’
[analyzer] Using cached launch layer ‘org.cloudfoundry.springboot:spring-boot’
[analyzer] Rewriting metadata for layer ‘org.cloudfoundry.springboot:spring-boot’
[analyzer] Using cached layer ‘org.cloudfoundry.springautoreconfiguration:46ab131165317d91fd4ad3186abf755222744e2d277dc413def06f3ad45ab150’
[analyzer] Writing metadata for uncached layer ‘org.cloudfoundry.springautoreconfiguration:auto-reconfiguration’

The assembly of the project starts in a similar way to the last step:

> Running builder[builder]
[builder] Cloud Foundry OpenJDK Buildpack v1.0.80
[builder] OpenJDK JRE 11.0.5: Reusing cached layer
[builder]
[builder] Cloud Foundry JVM Application Buildpack v1.0.113
[builder] Executable JAR: Reusing cached layer
[builder] Process types:
[builder] executable-jar: java -cp $CLASSPATH $JAVA_OPTS org.springframework.boot.loader.JarLauncher
[builder] task: java -cp $CLASSPATH $JAVA_OPTS org.springframework.boot.loader.JarLauncher
[builder] web: java -cp $CLASSPATH $JAVA_OPTS org.springframework.boot.loader.JarLauncher
[builder] Cloud Foundry Spring Boot Buildpack v1.0.157
[builder] Spring Boot 2.3.0.M1: Contributing to layer
[builder] Writing CLASSPATH to shared
[builder] Process types:
[builder] spring-boot: java -cp $CLASSPATH $JAVA_OPTS dev.ivanov.cnb.CnbApplication
[builder] task: java -cp $CLASSPATH $JAVA_OPTS dev.ivanov.cnb.CnbApplication
[builder] web: java -cp $CLASSPATH $JAVA_OPTS dev.ivanov.cnb.CnbApplication
[builder] Cloud Foundry Spring Auto-reconfiguration Buildpack v1.0.159
[builder] Spring Auto-reconfiguration 2.11.0: Reusing cached layer
> Running exporter[exporter] Adding layer ‘app’
[exporter] Adding layer ‘config’
[exporter] Reusing layer ‘launcher’
[exporter] Reusing layer ‘org.cloudfoundry.openjdk:openjdk-jre’
[exporter] Reusing layer ‘org.cloudfoundry.jvmapplication:executable-jar’
[exporter] Adding layer ‘org.cloudfoundry.springboot:spring-boot’
[exporter] Reusing layer ‘org.cloudfoundry.springautoreconfiguration:auto-reconfiguration’
[exporter] *** Images (7a83fadad1ce):
[exporter] docker.io/library/cnb:0.0.1-SNAPSHOT
> Running cacher[cacher] Reusing layer ‘org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b’
[cacher] Reusing layer ‘org.cloudfoundry.jvmapplication:executable-jar’
[cacher] Caching layer ‘org.cloudfoundry.springboot:spring-boot’
[cacher] Reusing layer ‘org.cloudfoundry.springautoreconfiguration:46ab131165317d91fd4ad3186abf755222744e2d277dc413def06f3ad45ab150’
Successfully built image ‘docker.io/library/cnb:0.0.1-SNAPSHOT’
BUILD SUCCESSFUL in 20s

This image was built faster than the previous one, despite new dependencies in the classpath, because of system reuse container layers.

We can inspect the build by using a program pack. You can download it here.

This metadata specifies what is contained in the image, what is in each layer, and how it was created. This is a clear security advantage since the structure of each image is known and prevents “leftover” libraries from being produced.

Cloud Native Buildpacks provides quite interesting functionality for microservice developers, first of all, on Spring Boot, as the development is carried out by Pivotal, so everything will work correctly. But it’s also worth mentioning that CNB is compatible with other frameworks and languages.

--

--