Posts

How to use CRaC with Spring Boot apps in a Docker container

Mar 14, 2024
Dmitry Chuyko
6.6

Alpaquita Containers with Alpaquita Linux, Liberica JDK, and support for the Coordinated Restore at Checkpoint (CRaC) offer the developers the “plug-and-play” experience with the OpenJDK CRaC API. Without the need to adjust the JDK or OS, they can conveniently perform the checkpoint and restore the warmed up state of their containerized Java workloads.

This article will guide you through using CRaC with Spring Boot applications running in a Docker container.

Prerequisites

  • Alpaquita Containers with CRaC support (You can download containers with CRaC on our Docker Hub repository under the ‘crac’ tag)
  • Spring Boot 3.2+
  • Docker

Prepare a Spring Boot app with CRaC

For our tutorial, we will use a Spring Boot reference application, Spring Petclinic.

First of all, clone the Spring Petclinic's repository:

git clone https://github.com/spring-projects/spring-petclinic.git

To use CRaC with the new Spring Boot and Spring Framework, we need to add the dependency for the org.crac/crac package to pom.xml:

<dependency>
    <groupId>org.crac</groupId>
    <artifactId>crac</artifactId>
    <version>1.4.0</version>
</dependency>

Let's check the difference of our updated pom.xml:

git diff
diff --git a/pom.xml b/pom.xml
index 287a08a..f403155 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,6 +36,11 @@
   </properties>

   <dependencies>
+    <dependency>
+        <groupId>org.crac</groupId>
+        <artifactId>crac</artifactId>
+        <version>1.4.0</version>
+    </dependency>
     <!-- Spring and Spring Boot dependencies -->
     <dependency>
       <groupId>org.springframework.boot</groupId>

Prepare a Dockerfile to checkpoint the application in a container

CRaC enables the developers to save the exact state of a running Java application (together with the information about the heap, JIT-compiled code, and so on). 

We will use Docker multi-stage builds functionality to containerize the application and checkpoint the running app in a container.

Copy the following Dockerfile:

FROM bellsoft/liberica-runtime-container:jdk-21-crac-musl as builder
WORKDIR /home/app
ADD spring-petclinic-main /home/app/spring-petclinic-main
RUN cd spring-petclinic-main && ./mvnw -Dmaven.test.skip=true clean package
 
FROM bellsoft/liberica-runtime-container:jre-21-crac-slim-musl as optimizer

WORKDIR /app
COPY --from=builder /home/app/spring-petclinic-main/target/spring-petclinic-3.3.0-SNAPSHOT.jar /app/app.jar

RUN java -Djarmode=tools -jar app.jar extract --layers --launcher

FROM bellsoft/liberica-runtime-container:jre-21-crac-slim-musl

# We stay root in a container to use CRaC

VOLUME /tmp
EXPOSE 8080

COPY --from=optimizer /app/app/dependencies/ ./
COPY --from=optimizer /app/app/spring-boot-loader/ ./
COPY --from=optimizer /app/app/snapshot-dependencies/ ./
COPY --from=optimizer /app/app/application/ ./

ENTRYPOINT ["java", "-Dspring.context.checkpoint=onRefresh", "-XX:CRaCCheckpointTo=/checkpoint", "-XX:MaxRAMPercentage=80.0", "org.springframework.boot.loader.launch.JarLauncher"]

At the first stage, we build the application. 

At the second stage, we make use of Spring Boot layered jars. This functionality enables us to store application classes and dependencies in different layers. Note that we need a special JarLauncher class later to work with layered images.

At the third stage, we run the application and perform the automatic checkpoint using Spring’s -Dspring.context.checkpoint=onRefresh option to start and immediately exit the application after non-lazy beans have been instantiated and InitializingBean#afterPropertiesSet callbacks have been invoked.

Containerize and start the application in a Docker container

We use the bellsoft/liberica-runtime-container:jdk-21-crac-musl image to start Petclinic and get the application dump for further restore. Note that BellSoft also provides images with glibc libc.

Another important prerequisite is the kernel version of the underlying Linux distribution, which should be at least 5.9. Linux kernel 5.9 introduces the CAP_CHECKPOINT_RESTORE option, which separates the checkpoint/restore functionality from the CAP_SYS_ADMIN option and enables the developers to steer clear of running containers with elevated permissions.

The CAP_CHECKPOINT_RESTORE (and SYS_PTRACE, which is also required for checkpoint-restore) options are enabled with the --cap-add flag added to newer Docker versions, so make sure you have the latest available Docker version.

Use the following command to build a Docker container:

$ docker build . -t petclinic-advanced-crac-checkpoint -f Dockerfile-advanced-crac

Now, let’s run the application in a container.

If you use a Linux distribution with an older kernel version, you can use the --priviledged flag instead of CAP_CHECKPOINT_RESTORE and SYS_PTRACE (however, this is not the best practice to run containers with elevated permissions).

docker run --cap-add CHECKPOINT_RESTORE --cap-add SYS_PTRACE --name petclinic-crac petclinic-advanced-crac-checkpoint

Note that there’s no --rm option as we'll remove the container manually later.

After that, the application will be automatically stopped and checkpointed.

Transfer the checkpointed app to a new container

Let’s create a fresh image with the checkpointed application:

$ docker commit --change='ENTRYPOINT ["java", "-XX:CRaCRestoreFrom=/checkpoint"]' petclinic-crac petclinic-advanced-crac-restore

Now, remove the previous container manually:

$ docker rm -f petclinic-crac

You can now start the application by restoring it:

$ docker run -it --rm -p 8080:8080 --cap-add CHECKPOINT_RESTORE --cap-add SYS_PTRACE --name petclinic-crac petclinic-advanced-crac-restore

Please note that the -p 8080:8080 option is added to expose the port for the running application.

It is also possible to start other Petclinic instances in separate containers — just use another port on the host system and another container name. 

To verify that the app is working correctly, open http://localhost:8080. If there are other instances started, you can open Spring Petclinic using a different port, for example, http://localhost:8081 if the port is opened as in the previous command.

Conclusion

As you can see, CRaC API can be conveniently used with your containerized workloads thanks to ready-to-use Alpaquita Containers.

If you have any questions regarding this feature, feel free to reach out, and our engineers will be happy to help!

Contact Us

Subcribe to our newsletter

figure

Read the industry news, receive solutions to your problems, and find the ways to save money.

Further reading