Spring Cloud Gateway 4.0.0-RC2 native example with Testcontainers

Featured image

Spring Cloud Gateway 4.0.0-RC2 native example with Testcontainers

This example repository is on GitHub

There are 3 goals I have for this example:

  • Deliver Spring Cloud Gateway as a graalvm-native-image with Spring Boot 3
  • Get feedback on the multi-architecture buildpack that includes ARM64 support
  • Demonstrate a pattern for building and testing buildpack images using Testcontainers

Spring Cloud Gateway

From the Spring.io website:

Spring Cloud Gateway aims to provide a simple,
yet effective way to route to APIs and provide
cross cutting concerns to them such as:
security, monitoring/metrics, and resiliency.

It is very popular, and even more valuable than it is popular!

Spring Framework 6 and Spring Boot 3.0.0 have recently gone GA. My favorite feature of these new releases is hands down the AOT processing. The AOT processing, provided by GraalVM, creates statically linked binary images. These images are optimized, ahead of time, for the operating system and architecture. For many modern workloads they can provide smaller image sizes, smaller memory footprint, and a much faster startup time.

In a recent experiment, I was able to expose Spring Cloud Gateway to the public, and have it route to other Spring Boot 3 native image workloads.

In that experiment Spring Cloud Gateway provided:

  • Single service for SSL and ingress integration
  • YAML configured routing
  • Circuit Breaker capabilities with Resilience4j
  • Request Rate Limiting with Redis

In the experiment, I left the Spring Cloud Gateway running, by setting the min-scale value to 1.

metadata:
  name: spring-cloud-gateway
  namespace: default
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/initial-scale: "1"
        autoscaling.knative.dev/min-scale: "1"
        autoscaling.knative.dev/max-scale: "2"
    spec:
      containers:
        - image: dashaun/dev.dashaun.service.gateway:latest

The other Spring Boot applications, being routed to by the gateway, were allowed to scale-to-zero when they weren’t used.

The experiment was on a cluster of Raspberry Pi devices which are ARM64 cpus.

The Spring Cloud Gateway, native image, typically starts up between 0.600s and 0.650s, on a Raspberry Pi.

SCG native image Raspberry Pi startup

Today, the buildpack delivered by Spring Initializr at start.spring.io, is unable to create native images on ARM64.

Multi-architecture buildpack

Earlier this year, I started working on a buildpack to use with the experimental project, Spring Native, and ARM64, for my Raspberry Pi devices. Later the M1/M2 Apple Silicon community, also ARM64, began showing interest in using this buildpack. I have delivered a few versions to date. I’ve also created some automation to improve my process for creating these buildpacks.

Recently, I created a multi-architecture buildpack that can be used with Spring Boot 3, to create native images on both Intel/AMD64 and ARM64 architectures. This example repository is configured with this multi-architecture buildpack.

I’m still working to improve this builder, in an effort to get ARM64 support upstream to Paketo. I have a long way to go, but with your help, I can discover gaps and continue this effort. One of the known, and frustrating gaps today, is that UPX compression does not work on ARM64, it does work with AMD64. I really want to get this figured out, and it is a high priority for me.

Building and testing native images

One of the most common concerns I have heard from the community this year has been around validating that the native image artifacts are delivering the same results as the JIT/JVM based artifacts. This is a very valid concern. This is also why I feel, that organizations with mature testing capabilities today, will be able to move forward with native images faster.

This repository provides a BuildImageTest that uses the buildpack to create a native image. It then tests the native image, using Testcontainers and JUnit. Building the native image with AOT processing, as part of a test, takes minutes not seconds, and should not be part of normal “inner loop” development. So the BuildImageTest is in a separate sourceSet and can be executed independently. This is a very powerful pattern, that I’m just getting started with. I would love to hear your thoughts on this pattern or other alternatives to it.

To build and validate the native image:

./gradlew integrationTest

You can also execute the BuildImageTest directly in your IDE.

Thanks

I am having so much fun with all of these new capabilities and challenges. I want to thank the Spring team, for all of their amazing work bring Spring Framework 6 and Spring Boot 3 to GA. I also want to thank Daniel Mikusa and the Paketo community for being amazing to work with. I want to thank Sergei Egorov for sharing the testing pattern and supporting my Testcontainers journey. Finally, I would like to thank Dan Vega, my Spring Office Hours cohost, for being an amazing teammate, and having the JUICE.

Keep Learning