-
Notifications
You must be signed in to change notification settings - Fork 118
CRaC
CRaC(Coordinated Restore at Checkpoint)是一个可以实现Java极速启动的技术,可以实现10x甚至更多的启动性能提升。但是CRaC的使用并不像JDK中其它特性通过调整选项即可开启,它的使用涉及JVM选项、用户代码的适配甚至CI/CD流程的修改。
在本文中通过对spring petclinc应用使用CRaC了解基本使用流程。
- 下载支持CRaC的Java版本,Dragonwell 11需要版本号需要>=待补充
- 准备一台有root权限的Linux机器(支持x86和aarch64),内核版本>=4.19
- 获取spring petclinc代码
git clone https://github.com/spring-projects/spring-petclinic.git cd spring-petclinic git reset --hard ab9135ad9bbc8631221caf0a7ee3eadd0561311d - 替换tomcat-embed-core为支持CRaC的版本,进入到spring-petclinc的目录后,执行命令:
patch pom.xml <<EOF
--- pom.xml 2024-10-11 16:05:33
+++ pom.xml.new 2024-10-11 16:06:26
@@ -49,8 +49,19 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.tomcat.embed</groupId>
+ <artifactId>tomcat-embed-core</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
+ <groupId>io.github.crac.org.apache.tomcat.embed</groupId>
+ <artifactId>tomcat-embed-core</artifactId>
+ <version>9.0.58</version>
+ </dependency>
+ <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
EOF
- 执行命令mvn clean packages进行构建,构建的产物即target/spring-petclinic-2.4.0.BUILD-SNAPSHOT.jar
- 执行如下命令启动spring petclinc并为快照做准备,启动后通过curl http://127.0.0.1:8080/检查
java -XX:CRaCCheckpointTo=cr -jar spring-petclinic-2.4.0.BUILD-SNAPSHOT.jar - 执行如下命令产生快照到目录cr,其中$pid为上一步进程的pid。执行成功以后会导致上面进程退出
jcmd $pid JDK.checkpoint
java -XX:CRaCRestoreFrom=cr -jar spring-petclinic-2.4.0.BUILD-SNAPSHOT.jar
恢复成功不会有输出,因为是从进程暂停的地方恢复执行,通过curl http://127.0.0.1:8080/确认是否恢复成功
理解CRaC之前,需要先了解https://criu.org/Main_Page ,因为它是CRaC的运行必须的组件。 它在用户态对进程做checkpoint,checkpoint相当于对进程当前状态产生快照文件。一旦有了快照文件,就可以从快照文件恢复进程到checkpoint的状态。可以用在如下一些场景下:
- 迁移进程:由于种种原因例如内核升级,更换机器等,需要将进程从某机器迁移到其它机器上。为什么要不直接在其它机器上重新运行呢?这里可能的原因有多方面: a. 进程可能是用户的,用户希望无感迁移 b. 进程可能执行一些耗时的科学计算,如果进程停止了,再重新运行需要很长时间
- 加速启动:以Java为例,Java语言本身的动态性使得Java在启动过程中类加载以及JIT编译会占用较多资源,只有进程进入到稳定态,才能达到性能的峰值。如果能直接从峰值的状态恢复,则会节约重新执行的时间。
criu会将进程的如下状态记录下来:
- 内存
- 映射
- 线程的寄存器
- 文件
- cgroup
- ... 在checkpoint时通过syscall将状态持久化到文件中,然后在restore时通过相应的syscall再重建进程的状态,从而实现恢复进程。
criu从理论上可以对任何用户态的进程进行checkpoint并restore,但是如果直接使用criu会遇到很多阻碍。例如:
- 如果在checkpoint时候,进程正在写文件中,可能正在写的过程中,也有可能write调用成功了,但是数据还没有flush到磁盘上。
- checkpoint时与restore时系统时间、IP地址会发生变化,程序中依赖这些变化的数据,必须要进行相应的变化。假设程序中一个心跳检测的线程判断执行某个操作是否超时了,由于checkpoint时将当时的系统时间保存到内存中,当restore时可能已经是很久之后的,这时如果用当前时间与checkpoint时的系统时间比较,就会发现可能已经超时了。程序可能认为异常了。
- 进程运行行为依赖配置文件,但是restore时的配置文件与checkpoint并不相同。配置文件中可能配置了应用需要的各种信息,但是不可能在checkpoint时枚举所有的配置。
针对上述的问题,抽象出共性的一些需求:
- checkpoint时整个程序必须处于某个可控的“安全状态”,这个安全状态包括: a. 用户可见的状态:用户能控制的部分,比如文件确保写入到磁盘上 b. 用户不可见的状态:对于JVM来说,就是JVM内部的状态。
- restore需要给用户控制状态的恢复,从而实现一个快照在多个环境上可以恢复,从而避免一个快照只能给一个环境使用。
那么CRaC是如何保证上述的几点的:
- 为了实现JVM的内部的状态安全: a. checkpoint是在进入safepoint以后完成的 b. JVM内部的类例如随机数都是在restore重新初始化
- 为了实现应用程序的可控的安全状态,CRaC提供了API给用户注册callback,这些callback在checkpoint之前调用,从而让用户自己保证程序在安全状态
- 为了实现一个快照在多个环境恢复,在恢复时提供了callback实现用户自定义的恢复
- JDK是否满足要求?只Dragonwell 11
- 运行环境内核版本是否满足要求: a. 内核版本< 4.19: 不支持 b. 内核版本>=4.19但<5.9:是否允许以privileged模式运行,如果不允许则不支持 c. 内核版本>=5.9: 是否允许程序拥有CAP_CHECKPOINT_RESTORE,如果不允许则不支持
- 评估CRaC收益是否达到预期: CRaC的收益取决于代码中调用checkpoint的位置,位置离启动成功越近,收益越大,但是可能带来代码适配工作量越大。
- 是否能接受应用程序针对CRaC的适配: 涉及到对应用程序的代码进行修改,具体的工作多少取决于应用的状态恢复的复杂程度
1.Linux内核必须是4.19以及之后的版本
- 如果是4.19只支持以privileged运行
-
=5.9支持以非privileged运行
- checkpoint时确保有足够的空闲内存,否则可能由于OOM而导致失败(具体大小取决于应用)
- checkpoint以及restore的需要如下一致,否则可能恢复失败:
- JVM版本完全一致
- restore机器的CPU的features需要是checkpoint的features的超集
- 此特性与AppCDS(EagerAppCDS)存在冲突,不可同时使用
使用jcmd $PID JDK.checkpoint生成快照
程序中主动调用org.crac.Core.checkpointRestore()生成快照。一旦执行这个调用以后,快照就会生成(在未出错的情况下),同时进程退出
这一步可以会遇到快照没有生成,或者报错,请移步到错误处理
-
(root用户运行无需此步骤) 设置${JAVA_HOME}/lib/criu有相应capability:
setcap cap_checkpoint_restore+eip ${JAVA_HOME}/lib/criu - JVM增加选项表示保存到快照的目录cr,并运行java
- root用户:
-XX:CRaCCheckpointTo=cr - 非root用户:
-XX:CRaCCheckpointTo=cr -XX:+CRaCUnprivileged
- 产生快照
- JVM增加如下选项,从目录cr中的快照恢复,运行java进行恢复
- root用户:
-XX:CRaCRestoreFrom=cr - 非root用户:
-XX:CRaCRestoreFrom=cr -XX:+CRaCUnprivileged
-
(privileged模式运行无需此步骤) 镜像构建时对${JAVA_HOME}/lib/criu设置相应的capability:
RUN setcap 'cap_checkpoint_restore+eip cap_setpcap+eip' /jdk/lib/criu - JVM增加选项表示保存到快照的目录cr
- privileged模式:
-XX:CRaCCheckpointTo=cr -XX:CRaCRestoreInheritPipeFds=1,2 - 非privileged模式:
-XX:CRaCCheckpointTo=cr -XX:CRaCRestoreInheritPipeFds=1,2 -XX:+CRaCUnprivileged
- docker run用于准备checkpoint,需要额外增加如下参数:
- privileged模式:
--privileged --user root - 非privileged模式:
--security-opt seccomp=unconfined --cap-add CHECKPOINT_RESTORE --cap-add CAP_SETPCAP
- 产生快照
- JVM增加参数用于从cr目录恢复:
-XX:CRaCRestoreFrom=cr -XX:+CRaCUnprivileged - docker run用于从cr目录恢复,需要额外增加如下参数:
- privileged模式:
--privileged --user root - 非privileged模式:
--security-opt seccomp=unconfined --cap-add CHECKPOINT_RESTORE --cap-add CAP_SETPCAP
通过设置pod的环境变量来控制pod是用于做checkpoint,还是从快照恢复,如下是一个示例的脚本供参考:
#! /bin/sh
APP_DIR=$(dirname "$0")
JAVA=${JAVA_HOME}/bin/java
CR=${APP_DIR}/cr
UNPRIVILEGE_OPT=""
if [[ $EUID -ne 0 ]]; then
UNPRIVILEGE_OPT="-XX:+CRaCUnprivileged"
fi
pushd ${APP_DIR}
if test "x${DO_RESTORE}" = xtrue; then
export CRAC_CRIU_OPTS="-o ${CR}/restore.log -vvvv"
${JAVA} -XX:CRaCRestoreFrom=${CR} ${UNPRIVILEGE_OPT} -jar spring-petclinic-2.4.0.BUILD-SNAPSHOT.jar
elif test "x${DO_CHECKPOINT}" = xtrue; then
# avoid pid conflit
for i in {1..10}; do sh -c "echo null > /dev/null"; done
${JAVA} -XX:CRaCCheckpointTo=${CR} ${UNPRIVILEGE_OPT} -XX:CRaCRestoreInheritPipeFds=1,2 -jar spring-petclinic-2.4.0.BUILD-SNAPSHOT.jar &
pid=$!
sleep 10
${JAVA_HOME}/bin/jcmd $pid JDK.checkpoint
sleep 5
cat ${CR}/dump4.log
sleep 3600
fi
如果是以非privileged模式运行,则在构建镜像的时候,需要设置的capabilty,如下示例:
FROM alibaba-cloud-linux-3-registry.cn-hangzhou.cr.aliyuncs.com/alinux3/alinux3:latest
RUN groupadd -g 1000 app && useradd -u 1000 -g app -s /bin/sh -d /home/app -m app
USER app:app
ADD dw11.tar.gz /home/app
ADD spring-petclinic-2.4.0.BUILD-SNAPSHOT.tar.gz /home/app
ADD run.sh /home/app
ENV JAVA_HOME=/home/app/jdk
USER root
RUN setcap 'cap_checkpoint_restore+eip cap_setpcap+eip' /home/app/jdk/lib/criu
USER app:app
CMD /home/app/run.sh
与正常的Deployment基本一样,如果是privileged增加在YAML下面的部分:
env:
- name: DO_CHECKPOINT
value: "true"
securityContext:
privileged: true
如果是非privlieged增加如下部分:
env:
- name: DO_CHECKPOINT
value: "true"
securityContext:
capabilities:
add: ["CHECKPOINT_RESTORE"]
上面的Deployment会在启动成功以后生成快照,可以进入pod里将快照文件打包并传输到用于构建镜像的机器上 重新构建镜像,可以在原来的镜像上叠加一层
如果是privileged在YAML中增加如下部分:
env:
- name: DO_RESTORE
value: "true"
securityContext:
privileged: true
如果是非privileged增加如下部分:
env:
- name: DO_RESTORE
value: "true"
securityContext:
capabilities:
add: ["CHECKPOINT_RESTORE"]
CRaC是借助criu对java进程的状态保存下来,再次恢复时会仍上次停下来的地方继续运行,但是在一些情况下需要程序对一些修改,从而确保CRaC能正确的恢复,包括但是不限于:
- 文件可能需要在checkpoint时关闭确保文件的一致性,在restore时需要重新打开
- 网络连接在checkpoint时关闭,在restore时重新连接,因为这些连接可能已经失效
- 应用的状态可能已经无效,例如依赖环境变量、IP等,但是restore时已经发生了变化等
下面以一个简单的程序代码说明如何适配,如下的代码:
public class TimeoutChecker implements Run{
private long lastTime;
public TimeoutChecker() {
lastTime = System.currentTimeInMills();
}
public bool checkTimeout() {
if (System.currentTimeInMills() - lastTime) > 5*1000L) {
throw new TimeoutException();
} else {
lastTime = System.currentTimeInMills();
}
}
}
在checkpoint时lastTime保存的是最后一次的时间,但是在恢复的时候如果不对lastTime进行重新赋值则会导致一旦恢复就会抛出异常。 CRaC提供了Resource接口,提供了回调方法在checkpoint之前以及restore之后调用。 先依赖CRaC的三方包: Maven:
<dependency>
<groupId>org.crac</groupId>
<artifactId>crac</artifactId>
<version>1.4.0</version>
</dependency>
Gradle:
dependencies {
compile group:'org.crac' , name: 'crac', version: "1.4.0"
}
然后让TimeoutChecker实现Resource接口:
+ import org.crac.*;
+ public class TimeoutChecker implements Run, Resource{
- public class TimeoutChecker implements Run{
public TimeoutChecker() {
lastTime = System.currentTimeInMills();
+ Core.getGlobalContext().register(this);
}
+ @Override
+ public void beforeCheckpoint(Context<? extends Resource> context) throws Exception {
+ }
+ @Override
+ public void afterRestore(Context<? extends Resource> context) throws Exception {
+ lastTime = System.currentTimeInMills();
+ }
}文件在一般情况下在checkpoint时需要先关闭,但是针对一些特殊的情况可以不关闭。
log4j以及logback的日志文件一般打开的是方式是O_APPEND和O_WRONLY,这种方式下可以不用关闭文件,但是需要在JVM参数CRaCAppendOnlyLogFiles指定这些文件。CRaCAppendOnlyLogFiles指定的文件如果在恢复的时候不存在的话,会默认创建空文件。 例如指定进程所有通过O_APPEND和O_WRONLY的文件,更多的配置方法参考配置参考。 -XX:CRaCAppendOnlyLogFiles="*"
- 增加 -XX:CRaCIgnoredFileDescriptors=3 让checkpoint时忽略gc日志文件
- restore时创建空的日志文件,重新开始写入
这种文件在checkpoint时确定了,然后在容器下环境恢复,进程可以继续写入,但是这些写入一旦等到容器消亡以后就丢失了。可以通过JDK提供的API来注册这类文件,注册以后在checkpoint时,JDK会将这些文件保存到快照目录中。使用方法如下:
public class JvmCracUtils {
/**
* save the pseudo persistent file to image dir, then restore to origin path
*/
public static final int SAVE_RESTORE = 0x01;
/**
* create a symbol link when restore a file. This should have a better performance than
* COPY_WHEN_RESTORE if file is large
*/
public static final int SYMLINK_WHEN_RESTORE = 0x10;
private static boolean supportCracExtension;
private static Method registerPseudoPersistentMethod;
private static Method appendToAppClassLoaderClassPathMethod;
static {
initCracExtension();
}
private static void initCracExtension() {
try {
Class clsCore = Class.forName("javax.crac.Core");
registerPseudoPersistentMethod =
clsCore.getMethod("registerPseudoPersistent", String.class, int.class);
appendToAppClassLoaderClassPathMethod =
clsCore.getMethod("appendToAppClassLoaderClassPath", String.class);
supportCracExtension = true;
} catch (Throwable ignore) {
}
}
public static void registerPseudoPersistent(String absoluteFilePath, int mode)
throws CheckpointException {
if (!supportCracExtension) {
throw new RuntimeException("Current JDK not support CRaC extension.");
}
try {
registerPseudoPersistentMethod.invoke(null, absoluteFilePath, mode);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
public static void appendToAppClassLoaderClassPath(String path) throws CheckpointException {
if (!supportCracExtension) {
throw new RuntimeException("Current JDK not support CRaC extension.");
}
try {
appendToAppClassLoaderClassPathMethod.invoke(null, path);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
下面将文件/home/admin/logs/app.log注册
JvmCracUtils.registerPseudoPersistent("/home/admin/logs/app.log",
JvmCracUtils.SAVE_RESTORE | JvmCracUtils.SYMLINK_WHEN_RESTORE);
如果是用户程序依赖的jar包,由于是只读的并假设它在恢复一直存在,所以不需要关闭。但是在这个之外的其它文件则需要实现org.crac.Resource接口。
JMX的适配除了关闭javax.management.remote.JMXConnectorServer以及java.rmi.registry.Registry外,较为特殊的地方在于JMX关闭以后,内部的socket并不会立即关闭,所以在checkpoint时需要sleep一段时间,一般在60秒左右会完全关闭。
应用中使用server socket经常会一直以单独的线程中执行无超时的方式accept,如何能让server socket较为优雅的终止,如下是一种方式供参考:
public class FooServer extends Thread {
/** Indicates whether a shutdown of server component has been requested. */
private final AtomicBoolean shutdownRequested = new AtomicBoolean();
public void run() {
try {
while (!this.shutdownRequested.get()) {
try {
ServerConnection conn =
new ServerConnection(
NetUtils.acceptWithoutTimeout(serverSocket), this);
try {
synchronized (activeConnections) {
while (activeConnections.size() >= maxConnections) {
activeConnections.wait(2000);
}
activeConnections.add(conn);
}
conn.start();
conn = null;
} finally {
if (conn != null) {
conn.close();
synchronized (activeConnections) {
activeConnections.remove(conn);
}
}
}
} catch (Throwable t) {
if (checkpointing) {
synchronized (paused) {
paused.compareAndSet(false, true);
paused.notifyAll();
}
synchronized (restoring) {
while (!restoring.get()) {
// wait until notify in CRaCResource.afterRestore
try {
restoring.wait();
} catch (InterruptedException ie) {
// This interrupt exception caused by call closeForCRaC
continue;
}
}
}
} else {
throw t;
}
}
}
} catch (Throwable t) {
//....
}
}
/**
* CRaC checkpoint&restore need pause the thread first, then replace ServerSocket. After that
* start thread again.
*/
private void closeForCRaC() throws InterruptedException, IOException {
checkpointing = true;
Exception exception = null;
try {
this.serverSocket.close();
} catch (IOException ioe) {
exception = ioe;
}
// wake the thread up, in case it is waiting on some operation
interrupt();
// wait the thread paused, so make sure no one modify activeConnections
synchronized (paused) {
while (!paused.get()) {
paused.wait();
}
}
synchronized (activeConnections) {
if (!activeConnections.isEmpty()) {
for (ServerConnection conn : activeConnections) {
conn.close();
}
activeConnections.clear();
}
}
checkpointing = false;
paused.compareAndSet(true, false);
}
private class CRaCResource implements Resource {
@Override
public void beforeCheckpoint(Context<? extends Resource> context) throws Exception {
closeForCRaC();
}
@Override
public void afterRestore(Context<? extends Resource> context) throws Exception {
synchronized (restoring) {
restoring.compareAndSet(false, true);
restoring.notifyAll();
}
}
}
}
- 根据程序的语义,分析哪些状态在checkpoint时要处理,哪些在restore要恢复
- 将上述的逻辑通过实现org.crac.Resource接口并在适当的位置注册到CRaC的上下文中 这里要注意所有注册到CRaC上下文中的Resource都是以soft reference方式,意味着如果没有其它引用Resource,那么Resource在checkpoint时可能已经被清除了,所以需要清理状态的对象的生命周期决定是否需要Resource对象的强引用。
在应用自身状态适配的过程中,有几个常见的问题需要处理,下面一些建议供参考:
由于final字段在对象构建已经确定了,后续无法再修改对象,如下几种方法:
- 去掉final
- 更改字段的类型,用一个新的包装类型将原来的类型包装起来,这样final可以保留,但是包装类型提供方法更新实际的值
- 扩展原来的类,在新的类中去除掉final
- 类型增加一个拷贝方法,在restore时调用此方法恢复对象内部状态
- 检查三方库的官方release notes,确认是否已经CRaC适配了,如果已经适配,则更新版本即可
- 应用代码中使用三方库的入口处,是否可以start或者stop。例如netty可以在应用代码进行start和stop server
- 检查三方库的配置项说明, 是否可以关闭一些不必要但是会导致无法checkpoint的资源。以log4j的jmx为例,如果应用不使用此特性,可以通过-Dlog4j2.disable.jmx=true关闭
对于PaaS类的应用有时会支持用户自定义的jar,在这种情况下checkpoint时无法知道用户最终会使用的jar,所以在restore时会出现class path与checkpoint不一致的情况,提供了如下的API在恢复的上下文时更新class path。
public void afterRestore(Context<? extends Resource> context) throws Exception {
if (userLibDir != null) {
final Path workingDirectory = FileUtils.getCurrentWorkingDirectory();
//... 这里省略list用户jar的逻辑
for (Path jarPath : relativeJars) {
JvmCracUtils.appendToAppClassLoaderClassPath(jarPath.toString());
}
}
}
- 先检查标准错误输出是否有打印异常堆栈,异常主要包括如下几类: a. BAD: opened by application: 表示应用打开了文件,但是在checkpoint是没有关闭,参考:文件相关适配 b. socket以及epoll等:参考: 网络相关适配
- 如果没有异常,则有可能是criu执行过程中发生错误,检查-XX:CRaCCheckpointTo指定的目录中dump4.log查看是否有错误。 如下是常见的问题: | 现象 | 解决方法| | ----------- | ----------- | | Error (compel/src/lib/infect.c:261): The criu itself is within dumped tree. | 确保checkpoint的java进程的pid不能为1 | | (00.006081) Error (criu/files-reg.c:1735): Build-ID (For validation) could not be obtained for file /var/db/nscd/hosts because can't open the file | 目前checkpoint时还不能处理使用nscd cache的情况下。需要关闭nscd cache再进行checkpoint。方法如下:
- 修改/etc/nscd.conf,将passwd和hosts对应的enable-cache设置为no
- 重启nscd服务,service nscd restart |
- 先检查标准错误输出是否有异常
- 如果第一步没有报错,则可能是criu在restore的过程中出错,通过设置环境变量设置restore的日志:export CRAC_CRIU_OPTS="-o restore.log -vvvv" 如下是常见的问题: | 现象 | 解决方法| | ----------- | ----------- | | criu日志中/proc/sys read-only | criu在恢复时需要确保pid与checkpoint时一致,如果内核不支持clone3指定tid,则需要通过修改/proc/sys/kernel/ns_last_pid来控制新的进程(线程的)id,但是如果/proc/sys是只读挂载,则无法修改就会出现restore失败。 解决方法:以只读方式重新挂载 mount -o rw,remount proc /proc mount -o rw,remount proc /proc/sys| |open file: **/metadata for read failed, error:|checkpoint失败了,快照没有生成成功,请先确保checkpoint成功| |vm version ** != **|checkpoint时运行的Java与restore的java版本不一致| |CPU instruction capabilities which JVM used do not match run time|restore的环境中CPU缺少在checkpoint环境中JIT code中使用的指令 |tty: Don't have tty to inherit session from, aborting|这是由于在checkpoint是没有关闭进程的标准输入对应的tty导致,可以在checkpoint和restore时将标准输入不再关联tty. setsid java xxx < /dev/null &> run.log 上述命令会重新创建一个session,并且把输入用/dev/null代替,这时标准输出和错误都会写到run.log中| |容器中恢复以后标准错误和输出没有内容|在checkpoint时 -XX:CRaCRestoreInheritPipeFds=1,2| | Can't dump ghost file ** of 5693440 size, increase limit|根据提示大小调整如下环境变量的上限export CRAC_CRIU_OPTS="--ghost-limit=10M| |restore时指定的main的参数不生效|restore时是从checkpoint往后执行,所以main的参数并不会重新读取。CRaC提供了重新读取system properties的方式,可以将main的参数通过system properties来指定。| |Pid xxx do not match exptected xxx|pid被其它进程占用了,可以在checkpoint时运行若干次shell命令,空出一段pid避免restore时冲突|
| 选项 | 说明 |
|---|---|
| -XX:CRaCCheckpointTo= | 开启checkpoint并且将快照写入到选项指定目录 |
| -XX:CRaCRestoreFrom= | 从指定选项指定的目录中的快照恢复java进程 |
| -XX:CRaCValidateBeforeRestore | 默认打开,在restore之前校验cpu,jvm的一致性,如果不一致则fail back到正常启动 |
| -XX:+CRTraceStartupTime | |
| -Djdk.crac.trace-startup-time=true | 打印快照的恢复的时间戳 |
| -Djdk.crac.debug=true | 打印每个Resource在checkpoint以及restore调用的详细信息 |
| -XX:+CRPrintResourcesOnCheckpoint | 在checkpoint之前打印所有的fd信息 |
| -XX:CRaCIgnoredFileDescriptors | 例如:-XX:CRaCIgnoredFileDescriptors=3表示在checkpoint时忽略fd为3文件 |
| 这个选项用于处理通过-Xloggc:gc.log的打开的gc.log文件,在checkpoint时通过这个选项,可以不用关闭。在恢复的时候,文件需要存在即可,不需要像其它文件要确定file size完全一致。 | |
| -XX:CRaCRestoreInheritPipeFds | 例如-XX:CRaCRestoreInheritPipeFds=�1,2 |
| 用于在容器环境中Java进程的fd为1,2可能是管道,这样容器运行时可以通过管道获取到进程的标准输出和错误。 | |
| 此选项用于在恢复时对于从父进程的fd保留,不做恢复。 | |
| -XX:+CRaCUnprivileged | 以unprivileged模式运行,在此模式运行时需要对${JAVA_HOME}/lib/criu设置相应的capability才可以运行。 |
| 如果内核是4.19,需要CAP_SYS_ADMIN | |
| 如果内核是5.10,需要CAP_CHECKPOINT_RESTORE | |
| -XX:CRaCAppendOnlyLogFiles | 配置通过O_APPEND和O_WRONLY打开文件可以在checkpoint可以忽略。 |
| 指定进程所有通过O_APPEND和O_WRONLY的文件 | |
| -XX:CRaCAppendOnlyLogFiles="*" | |
| 指定进程所有通过O_APPEND和O_WRONLY打开且扩展名为log和txt的文件 | |
| -XX:CRaCAppendOnlyLogFiles=".log,.txt" | |
| 指定进程所有通过O_APPEND和O_WRONLY打开且指定的文件 | |
| -XX:CRaCAppendOnlyLogFiles="/home/admin/foo.log,/home/admin/logs/bar.log" |
| 环境变量名称 | 说明 |
|---|---|
| CRAC_CRIU_OPTS | CRaC内部使用了criu作为快照的引擎,criu本身支持较多的选项,需要使用这些选项,可以环境变量来传递。 |
| criu完整的选项参考:Usage | |
| CRAC_CRIU_LEAVE_RUNNING | 默认情况dump之后就会将目标进程kill掉,有时不希望kill掉,可以将此值设置为true,从而继续运行 |