Sunday, April 18, 2021

Writing Cloud Native Spring Applications with Docker, GraalVM and Spring Native

Cloud Computing has taken the IT world by storm because of its versatility, low initial capital required to get started and low operational costs. Many new programming languages/platforms such as Go, Node JS, etc. have been tailormade to work on Cloud nodes 

But many Enterprise level Software applications still run on Java and have failed to harness full capabilities of the Cloud due to its high memory footprint and high latency due to its Just in Time (JIT) compilation. These contribute to slower start up times and cold starts.

In order to tackle problems Oracle Labs Team introduced a VM called GraalVM which enabled language agnostic method to produce Cloud Native applications using Java (not only Java) along with capabilities to integrate Spring as well.

In order to harness the capabilities of GraalVM and easy development Spring introduced a developer tool named Spring Native which enables to convert most of the Spring Applications to Docker Container Images with GraalVM image as base which converts the Spring Application to a Native application runs it.

This enables faster startup time, low latency and low memory usage. GraalVM achieves this by using its Ahead of Time (AOT) compilation and building capabilities. 

You can use this developer tool directly from Spring Initializr as below;


Using this tool will add a new plugin and a Spring Native dependency to the maven pom.xml of the Spring Application. This plugin is available for Gradle as well if you fancy that.

If you have a Simple Spring Application like below;
@SpringBootApplication
@RestController
@RequestMapping("/native")
public class SpringNativeDemoApplication {

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

@PostConstruct
public void init() {
System.out.println("Initialized...");
}

@GetMapping("/greet")
public String greet() {
return "Hello";
}


}
Which has an endpoint /native/greet you will see the dependency in the pom.xml following;

<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>${spring-native.version}</version>
</dependency>

And plugin under plugins;
<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>
You can build the Docker container using the following command;

./mvnw spring-boot:build-image

This will generate the native image with ahead of time compilation. The generated executable will have all the necessary classes and statically linked native code from the SDK. Substrate VM which is a docker image provided by GraalVM is used here as the base of the image.

 
 After a long build process which is one of the downsides of this approach for the time being. Following command be used to list down the newly built image in Docker.

./docker images | grep spring-native-demo

Then the image can be run using the following command to be listening at port 8088.

./docker run -p 8088:8080 spring-native-demo:0.0.1-SNAPSHOT

This will start up the Spring Application very quickly due to the Ahead of Time compilation capability of GraalVM. 

You can hit the endpoint using following command or by visiting the url in a browser;

curl localhost:8088/native/greet --silent

As of now not all Spring Eco System modules are supported in Spring Native. But it is a work in progress.

There are numerous samples provided by Spring Team on how to use Spring Native in with other Spring Modules. 

More details can be found at Spring Native documentation. You can find the Source code at Github.