How to run Java application with CRaC in Docker container

CRaC (Coordinated Restore at Checkpoint) is OpenJDK projectdeveloped Azul to solve the problem of “slow” start of the Java virtual machine in a microservice environment.

When the JVM runs your application’s code, it performs activities such as interpreting, compiling, and optimizing the code to make your application run as fast as possible for a given workload. This is great, but it can take a while, and especially when running short-lived microservices, you don’t want to wait for the JVM to generate the most optimized code.

The checkpoint-restore mechanism is nothing new, and most of you already know and use it on a daily basis.

If you are working on a laptop and close the lid, the operating system detects this and saves the current state to disk. As soon as you open 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 important parts of your code are affected and optimized by the JVM) and then create a checkpoint. This will close all open resources like open files and socket connections and then save the state of the JVM including your application to disk.

You can then restore the state of the JVM, including the state of your application, from this saved checkpoint as often as you like.

So CRaC not only solves the startup time problem, but it can also solve the application warm-up time problem, since you can create a breakpoint at any time.

More about CRAC

If you want to know more about the project, here are some blog posts with more detailed explanations:

Video about CRaC

You can also find several recordings of my sessions and those of my colleague Simon Ritter on YouTube. Here are just two of them:

CRaC GitHub Page

If you want to learn more about the implementation or download an OpenJDK build that includes CRaC support, you can take a look at the one we created. page on GitHub.

Preparation

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

The plan is to have you use your own application for this test, but if you just want to play around with CRaC you can also use one of my demos which can be found Here.

Note. CRaC is only available for Linux running on an x64 (Intel/AMD) machine, which is the configuration that is mostly used by modern cloud providers.

Create a docker image

  1. You need an application in the form of an executable JAR file.

  2. Then you need to create a Dockerfile. The following file is the minimum file required 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
  1. Now we can run the command docker build -t myapp_on_crac . creating a docker image.

Run your application in a docker container

  1. Run your docker container with the command:

docker run -it --privileged --rm --name my_app_on_crac my_app_on_crac
  1. Run the commands in the docker container:

cd /opt/app
java -XX:CRaCCheckpointTo=/opt/crac-files -jar MY-APP.jar
  1. Write down the PID of the program.

  2. Leave the shell window open and the application running.

Create a breakpoint

  1. Open another shell window.

  2. In this window do:

docker exec -it -u root my_app_on_crac /bin/bash
  1. Now it’s up to you to decide when you want to create a checkpoint (for example, apply some workload to your application to make sure everything is compiled and optimized).

  2. Take the PID you noted in another shell window and create a breakpoint by issuing the command: jcmd PID JDK.checkpoint

  3. If everything is in order, you should see that a checkpoint was created in the first shell window and your application was closed.

  4. Now the docker container launched in the first shell window contains a checkpoint in the folder /opt/crac-files.

  5. You can now close the second shell window by issuing the command exitto get back to your car.

Capture the current state of the docker container

  1. Now we need to save the current state of the docker container, including the checkpoint. To do this, we need the CONTAINER_ID.

  2. Open a new shell window, run the command docker ps -a and note down the CONTAINER_ID of the container your application is running in.

  3. By executing the command

    docker commit CONTAINER_ID my_app_on_crac:checkpoint

    in the second shell window, we can capture the current state of the container in a state checkpoint.

  4. Now we can stop the docker container by running the command exit in the first shell window.

Start a docker container from a checkpoint

  1. Run:

docker run -it --privileged --rm --name my_app_on_crac my_app_on_crac:checkpoint java -XX:CRaCRestoreFrom=/opt/crac-files
  1. Your application should now start much faster from the saved checkpoint.

Note. You can also run a Docker container on MacOS or Windows, as long as the machine you’re running it on has an x64 processor architecture (Intel/AMD).

Final words

If you are experimenting with CRaC and run into problems, PLEASE, let us know. We only provide a solution, but you need to use it, and we just have no idea what your application does.

In case of problems, it is best to write about the problem on github.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *