Build a docker image with a JDK and an Ubuntu base image:
docker build . -t nameservice1:latest -f Dockerfile_Ubuntu_JDK
Build a docker image with a JRE and an Ubuntu base image:
docker build . -t nameservice2:latest -f Dockerfile_Ubuntu_JRE
Build a docker image with a JLink created JRE and an Alpine Linux image:
docker build . -t nameservice3:latest -f Dockerfile_Alpine_JLink
The nameservice is using SpringBoot to offer a REST endpoint that returns a given number of random names for either girls, boys or both.
The endpoint can be called as follows:
http://localhost:8080/names/?gender=boy&amount=5
The expected result for this call will look like this:
{ "names":[{"name":"Abe","gender":"male"},{"name":"Baxter","gender":"male"},{"name":"Dylan","gender":"male"},{"name":"Lafayette","gender":"male"},{"name":"Marlin","gender":"male" ],"response_time":"701 ms"}
The response will also tell you how long it took to respond to the request. Because all names will be loaded the first time you request the service, it will take longer for the first call.
We added a dependency to the org.crac
library that makes it possible to code
against the CRaC api without the need for a JVM that supports CRaC. With this your
code can make use of the Resource interface and in case you run the application on a
CRaC enabled JVM, the CRaC related code will be called.
In this demo we will load all names in the beforeCheckpoint()
method which means
we do not even have to use the service but at the time of a checkpoint all names will
be loaded in the allNames list. That means after a restore all names already have been
loaded and we can directly start at full speed.
ATTENTION: THIS IS A DEMO!!! I know that you usually don't do this but it helps to understand.
The nameservice needs to load around 258 000 names from a json file the first time the service is called. The service provides methods to create a number of random names for boys, girls or both.
- Download a JDK with support for CRaC e.g.
- Unpack the tar.gz file and copy the JDK in a folder
sudo tar zxvf zulu21.30.19-ca-crac-jdk21.0.1-linux_x64.tar.gz
mv zulu21.30.19-ca-crac-jdk21.0.1-linux_x64 zulu-21.jdk
sudo mv zulu-21.jdk /usr/lib/jvm
- Set the $JAVA_HOME environment variable to the JDK with CRaC support e.g.
export JAVA_HOME=/usr/lib/jvm/zulu-21.jdk
- Make sure you have the permissions to run CRIU
sudo chown root:root $JAVA_HOME/lib/criu
sudo chmod u+s $JAVA_HOME/lib/criu
To create the jar file you need to build it on the target platform (Linux x64 or aarch64) using a JDK that supports CRaC. You can find builds here Azul.
- make sure you set JAVA_HOME to the JVM with CRaC support
- go the project folder
- run
gradlew clean build
- Now you should find the the jar at
build/libs/nameservice-21.0.0.jar
- This jar file will later be used to run on the docker container
- Make sure to select the correct JDK in the docker file (x64 or aarch64)
docker login
docker build -t nameservice -f Dockerfile_CRaC .
e.g.
docker commit container_id hansolo/nameservice:latest
docker push hansolo/nameservice:latest
- Open a shell window
- Run
docker run -it --privileged --rm -p 8080:8080 --name nameservice nameservice
- In the docker container run
java -XX:CRaCCheckpointTo=/opt/crac-files -jar /opt/app/nameservice-21.0.0.jar
- Open a second shell window
- Run
docker exec -it -u root nameservice /bin/bash
- Enable checkpoint compression
export CRAC_CRIU_OPTS=--compress
- Execute
top
command and note the PID of the running java process - Take the PID and run
jcmd PID JDK.checkpoint
- In the first shell window the application should have created the checkpoint
- Check the folder /opt/crac-files for the checkpoint files being present
- In second shell window run
exit
to get back to your machine
- Now get the CONTAINER_ID from shell window 1 by execute
docker ps -a
in shell window 2 - Run
docker commit CONTAINER_ID nameservice:checkpoint
in shell window 2 - Exit the docker container in shell window 1 by executing
exit
Run docker image without checkpoint:
docker run -it --privileged --rm -p 8080:8080 --name nameservice nameservice:checkpoint java -jar /opt/app/nameservice-21.0.0.jar
Run docker image with checkpoint:
docker run -it --privileged --rm -p 8080:8080 --name nameservice nameservice:checkpoint java -XX:CRaCRestoreFrom=/opt/crac-files
- Open a shell window
- Create a text file named
restore_nameservice.sh
- Add
#!/bin/bash
echo "docker run -it --privileged --rm -p 8080:8080 --name $1 nameservice:checkpoint java -XX:CRaCRestoreFrom=/opt/crac-files"
docker run -it --privileged --rm -p 8080:8080 --name $1 nameservice:checkpoint java -XX:CRaCRestoreFrom=/opt/crac-files
- Make the script executable by executing
chmod +x restore_nameservice.sh
- Now you can start the docker container multiple times executing
restore_nameservice.sh NAME_OF_CONTAINER
If you would like to start the original container without the checkpoint you can still
do that by executing the following command
docker run -it --privileged --rm -p 8080:8080 --name nameservice nameservice:checkpoint java -jar /opt/app/nameservice-21.0.0.jar
- Create a folder on the machine you run the docker image on e.g.
mkdir /home/hansolo/docker_volume
- Create a docker volume:
docker volume create --driver local --opt type=none --opt device=/home/hansolo/docker_volume --opt o=bind myvolume
- Run the docker using the volume:
docker run -it --privileged --rm -v myvolume:/checkpoints -p 8080:8080 --name nameservice nameservice
- In the docker container enable checkpoint compression
export CRAC_CRIU_OPTS=--compress
- In the docker container run
java -XX:CRaCCheckpointTo=/checkpoints -jar /opt/app/nameservice-21.0.0.jar
- Open a second shell window
- Run
docker exec -it -u root nameservice /bin/bash
- Execute
top
command and note the PID of the running java process - Take the PID and run
jcmd PID JDK.checkpoint
- In the first shell window the application should have created the checkpoint
- Check the folder /checkpoints for the checkpoint files being present
- In second shell window run
exit
to get back to your machine
- In first shell window run
exit
to get back to your machine - The checkpoint should now be in the folder you created e.g. /home/hansolo/docker_volume
- Create folder on the target machine e.g.:
mkdir /Users/hansolo/docker_volume
- Copy the files from the former created checkpoint to this folder
- Create a docker volume on the target machine:
docker volume create --driver local --opt type=none --opt device=/Users/hansolo/docker_volume --opt o=bind myvolume
- Run the docker image
docker run -it --privileged --rm -v myvolume:/checkpoints -p 8080:8080 --name nameservice nameservice:checkpoint java -XX:CRaCRestoreFrom=/checkpoints
The checkpoint is created automatically at startup during the LifecycleProcessor.onRefresh phase. After this phase has completed, all non-lazy initialized singletons have been instantiated, and InitializingBean#afterPropertiesSet callbacks have been invoked but the lifecycle has not started, and the ContextRefreshEvent has not yet been published. In this case you don't need to implement the Resource interface because this will only affect the startup of the SpringBoot framework.
- Create a folder on the machine you run the docker image on e.g.
mkdir /home/hansolo/docker_volume
- Create a docker volume:
docker volume create --driver local --opt type=none --opt device=/home/hansolo/docker_volume --opt o=bind myvolume
- Run the docker using the volume:
docker run -it --privileged --rm -v myvolume:/checkpoints -p 8080:8080 --name nameservice nameservice
- In the docker container enable checkpoint compression
export CRAC_CRIU_OPTS=--compress
- In the docker container run
java -Dspring.context.checkpoint=onRefresh -XX:CRaCCheckpointTo=/checkpoints -jar /opt/app/nameservice-21.0.0.jar
- In first shell window run
exit
to get back to your machine - The checkpoint should now be in the folder you created e.g. /home/hansolo/docker_volume
- Create folder on the target machine e.g.:
mkdir /Users/hansolo/docker_volume
- Copy the files from the former created checkpoint to this folder
- Create a docker volume on the target machine:
docker volume create --driver local --opt type=none --opt device=/Users/hansolo/docker_volume --opt o=bind myvolume
- Run the docker image
docker run -it --privileged --rm -v myvolume:/checkpoints -p 8080:8080 --name nameservice nameservice:checkpoint java -XX:CRaCRestoreFrom=/checkpoints
Use CRaC with SpringBoot 3.2 automatic checkpoint at startup locally (need to run on Linux + JDK with CRaC)
- Start the application normally
bash start.sh
- Start the application and automatically create a snapshot
bash start-autocrac.sh
- Restore the application from the stored checkpoint
bash restore-autocrac.sh