4. CRIU 技术详解:Checkpoint/Restore In Userspace 的架构、内核依赖、支持与限制
CRIU(Checkpoint/Restore In Userspace)是实现容器热迁移与异步迁移的核心底层技术。它能够在用户空间捕获一个运行中进程树的状态(Checkpoint),并将其完整保存到磁盘;随后在相同或兼容的宿主机上恢复执行(Restore),且进程本身几乎感知不到中断。本章深入剖析 CRIU 的架构设计、内核依赖、关键能力与已知限制。
4.1 CRIU 整体架构与核心组件
CRIU 采用用户空间守护进程 + 内核辅助模块的混合架构。其核心组件包括:
- CRIU 主进程(criu):用户空间命令行工具,负责协调 Checkpoint/Restore 全流程。
- ptrace 引擎:通过
ptrace()系统调用冻结目标进程、读取寄存器与内存映射。 - /proc 文件系统解析器:从
/proc/[pid]/读取进程状态、文件描述符、命名空间、cgroup 等信息。 - 内核模块(criu.ko):可选加载,用于加速某些内核对象的转储与恢复(如 TCP 连接、Unix socket)。
- 镜像文件生成器:将捕获的状态序列化为
.img文件,采用 Google Protocol Buffers 格式。 - 恢复引擎:逆向解析镜像文件,重建进程树、内存映射、文件描述符与网络连接。
4.2 内核依赖与系统调用要求
CRIU 对 Linux 内核版本和编译选项有严格依赖。缺少关键内核特性将导致 Checkpoint 或 Restore 失败。
| 内核特性 | 最低内核版本 | 内核配置选项 | 用途 |
|---|---|---|---|
| 命名空间(Namespace) | 3.8 | CONFIG_NAMESPACES |
隔离进程树、网络、挂载点 |
| ptrace 冻结 | 3.3 | CONFIG_CHECKPOINT_RESTORE |
冻结/解冻目标进程 |
| /proc/ |
3.5 | CONFIG_PROC_PID_MAP_FILES |
精确获取内存映射文件路径 |
| TCP 连接转储 | 4.11 | CONFIG_TCP_REPAIR |
保存/恢复 TCP 连接状态 |
| Unix socket 转储 | 4.15 | CONFIG_UNIX_DIAG |
保存/恢复 Unix socket 队列 |
| 用户空间事件轮询(epoll) | 4.5 | CONFIG_EPOLL |
保存 epoll 实例与监听事件 |
| cgroup v2 支持 | 5.2 | CONFIG_CGROUP_BPF |
恢复 cgroup 层级与资源限制 |
⚠ 内核依赖检查命令: 在目标宿主机上执行
criu check --all 可验证所有必需内核特性是否启用。若输出包含 WARN 或 FAIL,则需升级内核或重新编译内核模块。
4.3 Checkpoint 与 Restore 核心流程
CRIU 的 Checkpoint 和 Restore 分别遵循严格的阶段化流程,确保状态一致性与可恢复性。
4.3.1 Checkpoint 阶段
- 冻结(Freeze):通过
ptrace(PTRACE_SEIZE)附加到目标进程树,发送SIGSTOP信号,暂停所有线程。 - 收集(Collect):遍历
/proc/[pid]/收集进程状态、内存映射、文件描述符、信号处理、命名空间等。 - 转储(Dump):将收集到的状态序列化为
.img文件,包括内存页(通过process_vm_readv读取)。 - 解冻(Thaw):发送
SIGCONT恢复进程执行(若为热迁移,则进程继续运行)。 - 验证(Verify):检查镜像文件完整性,生成校验和。
4.3.2 Restore 阶段
- 解析(Parse):读取
.img文件,重建进程树拓扑结构。 - 创建(Create):通过
fork()/clone()创建进程,恢复命名空间、cgroup、挂载点。 - 恢复内存(Restore Memory):使用
mmap()映射内存区域,通过process_vm_writev写入内存页内容。 - 恢复文件描述符(Restore FDs):重建文件、socket、pipe、eventfd 等描述符。
- 恢复网络(Restore Network):重建 TCP 连接(通过 TCP_REPAIR 模式)、Unix socket 队列。
- 恢复信号与定时器(Restore Signals & Timers):恢复信号掩码、pending 信号、POSIX 定时器。
- 恢复执行(Resume):通过
ptrace(PTRACE_DETACH)释放进程,进程从暂停点继续执行。
4.4 支持的能力与功能矩阵
CRIU 支持丰富的进程状态保存与恢复能力,但并非所有 Linux 资源都可被完整转储。下表列出主要支持项与当前状态。
| 资源类型 | 支持状态 | 备注 |
|---|---|---|
| 进程树(多进程/多线程) | ✅ 完整支持 | 支持 clone() 创建的线程组 |
| 内存映射(匿名/文件映射) | ✅ 完整支持 | 包括 huge page、mremap |
| 文件描述符(普通文件) | ✅ 完整支持 | 需文件系统支持 fdinfo |
| TCP 连接 | ✅ 支持(≥4.11) | 通过 TCP_REPAIR 模式,需内核模块 |
| Unix socket | ✅ 支持(≥4.15) | 支持 DGRAM 与 STREAM 类型 |
| 信号与信号处理 | ✅ 支持 | 包括 pending 信号与 sigaction |
| POSIX 定时器 | ✅ 支持 | 包括 timer_create |
| epoll 实例 | ✅ 支持(≥4.5) | 保存监听事件与就绪列表 |
| eventfd / signalfd / timerfd | ✅ 支持 | 需内核 ≥3.10 |
| cgroup v1 / v2 | ✅ 支持 | v2 需内核 ≥5.2 |
| 命名空间(所有类型) | ✅ 支持 | 包括 PID、网络、挂载、用户、UTS |
| 设备映射(Device Mapper) | ⚠️ 部分支持 | 仅支持 snapshot 设备,不支持 thin provisioning |
| GPU 状态(NVIDIA/CUDA) | ❌ 不支持 | GPU 显存、上下文、驱动状态无法转储 |
| DMA / RDMA 设备 | ❌ 不支持 | 硬件状态无法用户空间捕获 |
| FUSE 文件系统 | ⚠️ 部分支持 | 需 FUSE 守护进程配合,可能丢失状态 |
4.5 关键限制与避坑指南
在生产环境中使用 CRIU 进行容器迁移时,以下限制需要特别关注:
🔴 GPU 与加速器限制: CRIU 无法捕获 GPU 显存内容、CUDA 上下文、OpenCL 状态或任何硬件加速器内部状态。如果容器使用了 NVIDIA GPU 或 AMD ROCm,Checkpoint 操作将失败。解决方案:在 Checkpoint 前将 GPU 任务排空,或使用 GPU 热迁移专用方案(如 NVIDIA MIG 配合 vGPU)。
⚠ 设备映射(Device Mapper)限制: CRIU 对 Device Mapper 的支持仅限于 snapshot 设备。如果容器使用了
dm-thin 或 dm-crypt,转储时可能丢失设备元数据。建议在 Checkpoint 前将设备映射转换为普通文件映射,或使用 --ext-unix-sk 选项绕过。
- 时间敏感应用: Checkpoint 过程会导致进程冻结数十毫秒到数秒(取决于内存大小)。对于需要严格低延迟的应用(如高频交易),冻结时间可能触发超时或连接断开。
- 网络连接中断: 虽然 CRIU 支持 TCP 连接恢复,但恢复后对端可能已经超时关闭连接。建议在迁移前设置较长的 TCP keepalive 间隔。
- 文件系统一致性: CRIU 不会对磁盘文件系统做快照。如果进程在 Checkpoint 时正在写入文件,恢复后文件可能处于不一致状态。建议配合文件系统快照(如
fsfreeze)使用。 - 内核版本兼容性: Checkpoint 和 Restore 必须在相同或兼容的内核版本上进行。跨大版本迁移(如 4.x → 5.x)可能导致恢复失败。建议源和目标宿主机内核版本差异不超过一个 minor 版本。
- 内存开销: Checkpoint 时 CRIU 会将所有内存页读取到用户空间并写入磁盘。对于内存密集型容器(如 64GB+),转储过程可能消耗大量临时磁盘空间和 I/O 带宽。
4.6 CRIU 与 Docker 集成实践
Docker 从 1.11 版本开始内置 CRIU 支持,通过 docker checkpoint 和 docker restore 命令实现容器级 Checkpoint/Restore。底层调用链如下:
# 创建容器时启用 checkpoint 支持
docker run -d --name myapp --security-opt seccomp=unconfined \
--cap-add=SYS_PTRACE myapp:latest
# 执行 Checkpoint(生成镜像文件)
docker checkpoint create myapp checkpoint1 \
--leave-running=false \
--checkpoint-dir=/var/lib/docker/checkpoints/
# 在目标节点执行 Restore
docker start --checkpoint checkpoint1 \
--checkpoint-dir=/var/lib/docker/checkpoints/ myapp
💡 生产建议: 使用
--leave-running=false 确保 Checkpoint 后容器停止,避免状态冲突。迁移前执行 criu check --all 验证目标节点内核兼容性。对于跨节点迁移,需手动同步 --checkpoint-dir 目录到目标节点。