Skip to content

Latest commit

 

History

History
368 lines (300 loc) · 11 KB

getting-started-buildpacks.adoc

File metadata and controls

368 lines (300 loc) · 11 KB

Getting started with Buildpacks

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.

System Requirements

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.

Sample Project Setup

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
Validate Spring Boot version
Important
Spring Native {version} only supports Spring Boot {spring-boot-version}, so change the version if necessary.
Maven
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>{spring-boot-version}</version>
    <relativePath/>
</parent>
Gradle Groovy
plugins {
    // ...
    id 'org.springframework.boot' version '{spring-boot-version}'
}
Gradle Kotlin
plugins {
    // ...
    id("org.springframework.boot") version "{spring-boot-version}"
}
Add the Spring Native dependency

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.

Maven
<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>org.springframework.experimental</groupId>
        <artifactId>spring-native</artifactId>
        <version>{version}</version>
    </dependency>
</dependencies>
Gradle Groovy
// No need to add the spring-native dependency explicitly with Gradle, the Spring AOT plugin will add it automatically.
Gradle Kotlin
// No need to add the spring-native dependency explicitly with Gradle, the Spring AOT plugin will add it automatically.
Add the Spring AOT plugin

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.
Maven
<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>
Gradle Groovy
plugins {
    // ...
    id 'org.springframework.experimental.aot' version '{version}'
}
Gradle Kotlin
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.

Enable native image support

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:

Maven
<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>
Gradle Groovy
bootBuildImage {
    builder = "paketobuildpacks/builder:tiny"
    environment = [
        "BP_NATIVE_IMAGE" : "true"
    ]
}
Gradle Kotlin
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.
Freeze GraalVM version

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:

Maven
<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>
Gradle Groovy
bootBuildImage {
    // ...
    buildpacks = ["gcr.io/paketo-buildpacks/java-native-image:{buildpacks-java-native-image-version}"]
}
Gradle Kotlin
tasks.getByName<BootBuildImage>("bootBuildImage") {
    // ...
    buildpacks = listOf("gcr.io/paketo-buildpacks/java-native-image:{buildpacks-java-native-image-version}")
}
Maven Repository

Configure your build to include the required repository for the spring-native dependency, as follows:

Maven
<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>
Gradle Groovy
repositories {
    // ...
    maven { url 'https://repo.spring.io/{spring-native-repo}' }
}
Gradle Kotlin
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.

Maven
<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>
Gradle Groovy
pluginManagement {
    repositories {
        // ...
        maven { url 'https://repo.spring.io/{spring-native-repo}' }
    }
}
Gradle Kotlin
pluginManagement {
    repositories {
        // ...
        maven { url = uri("https://repo.spring.io/{spring-native-repo}") }
    }
}

Build the native application

The native application can be built as follows:

Maven
$ mvn spring-boot:build-image
Gradle Groovy
$ gradle bootBuildImage
Gradle Kotlin
$ 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.

Run the native application

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!"}