How to Run a Java Application with CRaC in a Docker Container

CRaC (Coordinated Restore at Checkpoint) is an OpenJDK project that was developed by Azul to solve the problem of “slow” startup times of the Java Virtual Machine in a microservice environment.

When the JVM runs your application code, it does things like interpreting, compiling and optimizing code to make your application run as fast as possible under the given workload. This is great but can take some time and especially when you run short lived microservices you don’t want to wait until the JVM has produced the most optimized code.

The checkpoint-restore mechanism is nothing new and most of you already know and use it daily. If you work on your laptop and close the lid, the operating systems detects that an stores it’s current state to disk. Once you open up the lid again, the operating system restores the saved state from disk.
CRaC provides the same mechanism but for the JVM and your running application.

You start your application, apply some workload to it (to make sure the important parts of your code will be touched and with that optimized by the JVM) and then create a checkpoint. This will close all open resources like open files and socket connections and will then store the state of the JVM including your application to disk.

After that, you can restore the state of the JVM including the state of your application from that stored checkpoint as often as you like.

And with this, CRaC not only is capable of solving the startup time problem but can also solve the application warmup time problem because you can create the checkpoint whenever you like.

More on CRaC

If you want to learn more about the project, here are some blogposts with more explanations

Superfast Application Startup: Java on CRaC

Introducing the OpenJDK “Coordinated Restore at Checkpoint” Project

Videos on CRaC

There are also several recorded sessions from either my colleague Simon Ritter or myself, that you can find on youtube. Here are just two of them

Java on CRaC (Simon Ritter at Devoxx Belgium)

What the CRaC (Gerrit Grunwald at Voxxed Athens)

CRaC GitHub Page

If you would like to know more about the implementation or if you would like to download the OpenJDK build that incl. CRaC support you might want check out the github page we have created

Github Page

Preparations

The idea is to have an application or service that consists of a single executable JAR file.

The plan is to make you use your own application and use it for this test but if you simply would like to play around with CRaC you can also use one of my demos that you will find here.

Note: CRaC is only available for Linux runing on an x64 (Intel/AMD) machine which is the configuration that is mainly used on todays cloud providers.

Create the docker image

You need an application in a runnable JAR file.
Now we need to create a Dockerfile. The following file is the minimum file you need to run your application in a docker container.

FROM ubuntu:20.04

ENV JAVA_HOME /opt/jdk
ENV PATH $JAVA_HOME/bin:$PATH

RUN apt-get update -y

ADD “https://github.com/CRaC/openjdk-builds/releases/download/17-crac%2B3/openjdk-17-crac+3_linux-x64.tar.gz” $JAVA_HOME/openjdk.tar.gz
RUN tar –extract –file $JAVA_HOME/openjdk.tar.gz –directory “$JAVA_HOME” –strip-components 1; rm $JAVA_HOME/openjdk.tar.gz;

RUN mkdir -p /opt/crac-files

COPY build/libs/MY-APP.jar /opt/app/MY-APP.jar

Now we can run docker build -t myapp_on_crac . to build the docker image

Start your application in a docker container

Run your docker container with:
docker run -it –privileged –rm –name my_app_on_crac my_app_on_crac

In the docker container run:
cd /opt/app
java -XX:CRaCCheckpointTo=/opt/crac-files -jar MY-APP.jar

Note the PID of the program.
Leave the shell window open and the application running.

Create the checkpoint

Open another shell window
In this window run:
docker exec -it -u root my_app_on_crac /bin/bash

Now it’s up to you when you would like to create the checkpoint (e.g. apply some workload to your app to make sure everything is compiled and optimized)
Take the PID that you noted from the other shell window and create the checkpoint by executing jcmd PID JDK.checkpoint
If everything is ok, you should see that in the first shell window the checkpoint was created and your application was closed
Now the docker container running in the first shell window contains the checkpoint in the /opt/crac-files folder
Now you can close the second shell window by executing exit to get back to your machine

Commit the current state of the docker container

Now we need to save the current state of the docker container incl. the checkpoint. For this we need the CONTAINER_ID.
Open a new shell window and execute docker ps -a and note the CONTAINER_ID of the container that ran your app
By running
docker commit CONTAINER_ID my_app_on_crac:checkpoint

in the second shell window, we can commit the current state of the container to the state checkpoint.

Now we can stop the docker container by executing exitin the first shell window

Run the docker container from the checkpoint

Run:
docker run -it –privileged –rm –name my_app_on_crac my_app_on_crac:checkpoint java -XX:CRaCRestoreFrom=/opt/crac-files

Your application should now start much faster from the saved checkpoint

Note: You can run the docker container also on MacOS or Windows, as long as the machine you are running it on has a x64 cpu architecture (Intel/AMD)

Final words

If you play around with CRaC and stumble upon problems, PLEASE let us know, we only provide the solution but you are the ones that need to use it and we simply have no idea what your application is doing.

In case of problems the best thing to do is to file an issue over at github

Happy cracing!

The post How to Run a Java Application with CRaC in a Docker Container appeared first on foojay.