Skip to content

Sample application that demonstrates Spring’s Support for GraalVM, Project CraC & Class Data Sharing (CDS)

Notifications You must be signed in to change notification settings

timosalm/going-serverless

Repository files navigation

Going Serverless With Spring’s Support for GraalVM, Project CraC & Class Data Sharing (CDS)

Session recording

Update: Support for extracting an uber JAR to a CDS friendly layout was added in Spring Boot 3.3.0. The demo code is updated. Project Leyden demo is WIP Special thanks to Sébastien Deleuze, who is not only the lead for the topic in the Spring team but also wrote two great blog posts on it: GraalVM, Project CRaC, CDS.

Container image building

Hint: If you want to skip the container image building, you can use my images by running export REGISTRY_HOST=ghcr.io/timosalm/going-serverless:, and jump to the "Running the application on Knative" section.

export REGISTRY_HOST=<your-registry-hostname>(/<project>)/

Without optimizations

./gradlew bootBuildImage --imageName=${REGISTRY_HOST}hello-world
docker push ${REGISTRY_HOST}hello-world

GraalVM Native Image

The required "org.graalvm.buildtools.native" plugin in build.gradle will be enabled based on the custom "graalvm" profile

./gradlew bootBuildImage --imageName=${REGISTRY_HOST}hello-world-native -PbuildProfile=graalvm
docker push ${REGISTRY_HOST}hello-world-native

Project CraC

capsh --print # Check whether required capabilities are available
docker build . -t ${REGISTRY_HOST}hello-world-crac:checkpointer --file crac/Dockerfile
docker run -d --cap-add CHECKPOINT_RESTORE --cap-add SYS_PTRACE --rm --name hello-world-crac-checkpointer ${REGISTRY_HOST}hello-world-crac:checkpointer
# Wait until checkpoint creation succeeded: docker logs $(docker ps -qf "name=hello-world-crac-checkpointer") -f
docker commit --change='ENTRYPOINT ["/opt/app/entrypoint.sh"]' $(docker ps -qf "name=hello-world-crac-checkpointer") ${REGISTRY_HOST}hello-world-crac
docker kill $(docker ps -qf "name=hello-world-crac-checkpointer")
# Test: docker run -d --cap-add CHECKPOINT_RESTORE --cap-add SYS_ADMIN --rm -p 8080:8080 --name hello-world-crac-checkpoint ${REGISTRY_HOST}hello-world-crac
docker push ${REGISTRY_HOST}hello-world-crac

CDS

Option 1 with Cloud Native Buildpack

The required BP_JVM_CDS_ENABLED=true and BP_SPRING_AOT_ENABLED env variables for the Cloud Native Buildpack will be enabled based on the custom "cds" profile in build.gradle.

./gradlew bootBuildImage --imageName=${REGISTRY_HOST}hello-world-cds -PbuildProfile=cds
docker push ${REGISTRY_HOST}hello-world-cds

Option 2 with Dockerfile

docker build . -t ${REGISTRY_HOST}hello-world-cds --file cds/Dockerfile
docker push ${REGISTRY_HOST}hello-world-cds

Running the application on Knative

Without optimizations

kn service create hello-world --image ${REGISTRY_HOST}hello-world
kubectl logs -l app=hello-world-00001 -c user-container | grep "Started HelloWorldApplication"
# Started HelloWorldApplication in 4.558 seconds (process running for 5.214)
watch kubectl get pods
hey -n 1000 -c 1000 -m GET $(kn service describe hello-world -o url)
kubectl top pods -l app=hello-world-00001 --containers
# POD                                            NAME             CPU(cores)   MEMORY(bytes)
# hello-world-00001-deployment-cf846cc5b-bcdr7   user-container   3m           182Mi

GraalVM Native Image

kn service create hello-world-native --image ${REGISTRY_HOST}hello-world-native
kubectl logs -l app=hello-world-native-00001 -c user-container | grep "Started HelloWorldApplication"
# Started HelloWorldApplication in 0.238 seconds (process running for 0.247)
watch kubectl get pods
hey -n 1000 -c 1000 -m GET $(kn service describe hello-world-native -o url)
kubectl top pods -l app=hello-world-native-00001 --containers
# POD                                                  NAME             CPU(cores)   MEMORY(bytes)
# hello-world-native-00001-deployment-5959fc77fd-pbfxv   user-container   1m           40Mi

Project CraC

envsubst < crac/kservice.yaml | kubectl apply -f -
kubectl logs -l app=hello-world-crac-00001 -c user-container | grep "Spring-managed lifecycle restart completed"
# Spring-managed lifecycle restart completed (restored JVM running for 276 ms)
watch kubectl get pods
hey -n 1000 -c 1000 -m GET $(kn service describe hello-world-crac -o url)
kubectl top pods -l app=hello-world-crac-00001 --containers
# POD                                                  NAME             CPU(cores)   MEMORY(bytes)
# hello-world-crac-00001-deployment-5d48647675-c7qs4   user-container   2m           38Mi

CDS

kn service create hello-world-cds --image ${REGISTRY_HOST}hello-world-cds
kubectl logs -l app=hello-world-cds-00001 -c user-container | grep "Started HelloWorldApplication"
# Started HelloWorldApplication in 2.769 seconds
watch kubectl get pods
hey -n 1000 -c 1000 -m GET $(kn service describe hello-world-cds -o url)
kubectl top pods -l app=hello-world-cds-00001 --containers
# POD                                                 NAME             CPU(cores)   MEMORY(bytes)
# hello-world-cds-00001-deployment-5fb784757f-vtzdh   user-container   2m           10Mi

Running the application on Azure Container Apps

Without optimizations

az containerapp up --name hello-world --image harbor.main.emea.end2end.link/going-serverless/hello-world --ingress external --target-port 8080
az containerapp logs show -n hello-world -g DefaultResourceGroup-DEWC | grep "Started HelloWorldApplication"
watch az containerapp replica list -n hello-world -g DefaultResourceGroup-DEWC --query "[].[name,properties.runningState]"
hey -n 1000 -c 1000 -m GET $(echo "https://$(az containerapp show -n hello-world -g DefaultResourceGroup-DEWC --query properties.configuration.ingress.fqdn --only-show-errors -o yaml)")

GraalVM Native Image

az containerapp up --name hello-world-native --image harbor.main.emea.end2end.link/going-serverless/hello-world-native --ingress external --target-port 8080
az containerapp logs show -n hello-world-native -g DefaultResourceGroup-DEWC | grep "Started HelloWorldApplication"
watch az containerapp replica list -n hello-world-native -g DefaultResourceGroup-DEWC --query "[].[name,properties.runningState]"
hey -n 1000 -c 1000 -m GET $(echo "https://$(az containerapp show -n hello-world-native -g DefaultResourceGroup-DEWC --query properties.configuration.ingress.fqdn --only-show-errors -o yaml)")

Project CraC

az containerapp up --name hello-world-crac --image harbor.main.emea.end2end.link/going-serverless/hello-world-crac --ingress external --target-port 8080
az containerapp logs show -n hello-world-crac -g DefaultResourceGroup-DEWC | grep "Spring-managed lifecycle restart completed"
watch az containerapp replica list -n hello-world-crac -g DefaultResourceGroup-DEWC --query "[].[name,properties.runningState]"
hey -n 1000 -c 1000 -m GET $(echo "https://$(az containerapp show -n hello-world-crac -g DefaultResourceGroup-DEWC --query properties.configuration.ingress.fqdn --only-show-errors -o yaml)")

CDS

az containerapp up --name hello-world-cds --image harbor.main.emea.end2end.link/going-serverless/hello-world-cds --ingress external --target-port 8080
az containerapp logs show -n hello-world-cds -g DefaultResourceGroup-DEWC | grep "Started HelloWorldApplication"
watch az containerapp replica list -n hello-world-cds -g DefaultResourceGroup-DEWC --query "[].[name,properties.runningState]"
hey -n 1000 -c 1000 -m GET $(echo "https://$(az containerapp show -n hello-world-cds -g DefaultResourceGroup-DEWC --query properties.configuration.ingress.fqdn --only-show-errors -o yaml)")

About

Sample application that demonstrates Spring’s Support for GraalVM, Project CraC & Class Data Sharing (CDS)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages