This section gives you a practical overview of building a Spring Boot native application using {spring-boot-docs}/html/features.html#features.container-images.building.buildpacks[Cloud Native Buildpacks]. This is a practical guide that uses the RESTful Web Service getting started guide.
Docker should be installed, see Get Docker for more details. Configure it to allow non-root user if you are on Linux.
Note
|
You can run docker run hello-world (without sudo ) to check the Docker daemon is reachable as expected.
Check the {spring-boot-maven-plugin-docs}/#build-image-docker-daemon[Maven] or {spring-boot-gradle-plugin-docs}/#build-image-docker-daemon[Gradle] Spring Boot plugin documentation for more details.
|
Tip
|
On MacOS, it is recommended to increase the memory allocated to Docker to at least 8GB , and potentially add more CPUs as well.
See this Stackoverflow answer for more details.
On Microsoft Windows, make sure to enable the Docker WSL 2 backend for better performance.
|
The completed "RESTful Web Service" guide can be retrieved using the following commands:
git clone https://github.com/spring-guides/gs-rest-service
cd gs-rest-service/complete
Important
|
Spring Native {version} only supports Spring Boot {spring-boot-version}, so change the version if necessary. |
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>{spring-boot-version}</version>
<relativePath/>
</parent>
plugins {
// ...
id 'org.springframework.boot' version '{spring-boot-version}'
}
plugins {
// ...
id("org.springframework.boot") version "{spring-boot-version}"
}
org.springframework.experimental:spring-native
provides native configuration APIs like @NativeHint
as well as other mandatory classes required to run a Spring application as a native image. You need to specify it explicitly only with Maven.
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>{version}</version>
</dependency>
</dependencies>
// No need to add the spring-native dependency explicitly with Gradle, the Spring AOT plugin will add it automatically.
// No need to add the spring-native dependency explicitly with Gradle, the Spring AOT plugin will add it automatically.
The Spring AOT plugin performs ahead-of-time transformations required to improve native image compatibility and footprint.
Tip
|
The transformations also apply to the JVM so this can be applied regardless. |
<build>
<plugins>
<!-- ... -->
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>{version}</version>
<executions>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
plugins {
// ...
id 'org.springframework.experimental.aot' version '{version}'
}
plugins {
// ...
id("org.springframework.experimental.aot") version "{version}"
}
The plugin provides a number of options to customize the transformations, see [spring-aot-configuration] for more details.
Spring Boot’s {spring-boot-docs}/html/spring-boot-features.html#boot-features-container-images-buildpacks[Cloud Native Buildpacks support] lets you build a container for your Spring Boot application.
The native image buildpack can be enabled using the BP_NATIVE_IMAGE
environment variable as follows:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder:tiny</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
</env>
</image>
</configuration>
</plugin>
bootBuildImage {
builder = "paketobuildpacks/builder:tiny"
environment = [
"BP_NATIVE_IMAGE" : "true"
]
}
tasks.getByName<BootBuildImage>("bootBuildImage") {
builder = "paketobuildpacks/builder:tiny"
environment = mapOf(
"BP_NATIVE_IMAGE" to "true"
)
}
Note
|
tiny builder allows small footprint and reduced surface attack, you can also use base (the default) or full builders to have more tools available in the image for an improved developer experience.
|
Tip
|
Additional native-image arguments can be added using the BP_NATIVE_IMAGE_BUILD_ARGUMENTS environment variable.
|
By default, GraalVM versions will be upgraded automatically by Buildpacks to the latest release.
You can explicitly configure Spring Boot {spring-boot-maven-plugin-docs}#build-image-example-buildpacks[Maven] or {spring-boot-gradle-plugin-docs}#build-image-example-buildpacks[Gradle] plugins with a specific version of java-native-image
buildpack which will freeze GraalVM version, see {paketo-docs}/howto/java/#configure-the-graalvm-version[related versions mapping].
For example, if you want to force using GraalVM {graalvm-version}
, you can configure:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- ... -->
<image>
<buildpacks>
<buildpack>gcr.io/paketo-buildpacks/java-native-image:{buildpacks-java-native-image-version}</buildpack>
</buildpacks>
</image>
</configuration>
</plugin>
bootBuildImage {
// ...
buildpacks = ["gcr.io/paketo-buildpacks/java-native-image:{buildpacks-java-native-image-version}"]
}
tasks.getByName<BootBuildImage>("bootBuildImage") {
// ...
buildpacks = listOf("gcr.io/paketo-buildpacks/java-native-image:{buildpacks-java-native-image-version}")
}
Configure your build to include the required repository for the spring-native
dependency, as follows:
<repositories>
<!-- ... -->
<repository>
<id>spring-{spring-native-repo}</id>
<name>Spring {spring-native-repo}</name>
<url>https://repo.spring.io/{spring-native-repo}</url>
</repository>
</repositories>
repositories {
// ...
maven { url 'https://repo.spring.io/{spring-native-repo}' }
}
repositories {
// ...
maven { url = uri("https://repo.spring.io/{spring-native-repo}") }
}
The Spring AOT plugin also requires a dedicated plugin repository in the pom.xml
file for Maven and in the in the settings.gradle(.kts)
for Gradle.
<pluginRepositories>
<!-- ... -->
<pluginRepository>
<id>spring-{spring-native-repo}</id>
<name>Spring {spring-native-repo}</name>
<url>https://repo.spring.io/{spring-native-repo}</url>
</pluginRepository>
</pluginRepositories>
pluginManagement {
repositories {
// ...
maven { url 'https://repo.spring.io/{spring-native-repo}' }
}
}
pluginManagement {
repositories {
// ...
maven { url = uri("https://repo.spring.io/{spring-native-repo}") }
}
}
The native application can be built as follows:
$ mvn spring-boot:build-image
$ gradle bootBuildImage
$ gradle bootBuildImage
Note
|
During the native compilation, you will see a lot of WARNING: Could not register reflection metadata messages. They are expected and will be removed in a future version, see #502 for more details.
|
This creates a Linux container to build the native application using the GraalVM native image compiler. By default, the container image is installed locally.
To run the application, you can use docker
the usual way as shown in the following example:
$ docker run --rm -p 8080:8080 rest-service-complete:0.0.1-SNAPSHOT
If you prefer docker-compose
, you can write a docker-compose.yml
at the root of the project with the following content:
version: '3.1'
services:
rest-service:
image: rest-service-complete:0.0.1-SNAPSHOT
ports:
- "8080:8080"
And then run
$ docker-compose up
The startup time should be less than 100ms
, compared to the roughly 1500ms
when starting the application on the JVM.
Now that the service is up, visit http://localhost:8080/greeting
, where you should see:
{"id":1,"content":"Hello, World!"}