使用 CRIU 对 MPI 容器进行热迁移
解读
国内金融、电信、超算中心在国产化替代背景下,对“在线业务不中断”与“资源利用率最大化”提出硬性 KPI。MPI 作业常跑在 Slurm+Infiniband 的专属分区,一旦节点需要腾退给更高优先级任务,热迁移成为唯一选择。Docker 默认只支持“停-拷-启”冷迁移,CRIU(Checkpoint/Restore In Userspace) 是唯一能在用户态实现进程级热迁移的上游方案,因此面试官想验证候选人是否能把“容器+MPI+CRIU”这条技术栈真正跑通,并解决国产化环境(鲲鹏、麒麟、OpenEuler)下的权限、驱动、网络三大痛点。
知识点
- CRIU 原理:通过
/proc/$pid/fd、/proc/$pid/map_files抓取进程地址空间、已打开文件、信号、定时器、Unix 域套接字、共享内存、ptrace 寄存器,生成 img 文件;恢复时利用mmap精准重建,依赖内核 CONFIG_CHECKPOINT_RESTORE 与 3.11+ 版本。 - Docker 集成:Docker ≥ 20.10 实验特性
--checkpoint --checkpoint-dir,底层调用docker-runc checkpoint,依赖 runc 1.0+、criu 3.15+;必须开启 Docker daemon 实验特性"experimental": true。 - MPI 容器特殊性:
- 使用 OpenMPI 4.1+ 时,进程间通过 vader(共享内存)或 openib(Infiniband)通信,CRIU 需要额外处理 XPMEM / CMA / InfiniBand HCA 句柄;
- 启动命令通常为
mpirun --allow-run-as-root --mca btl_tcp_if_exclude docker0 ...,root 权限与能力字 CAP_SYS_ADMIN、CAP_NET_RAW 必须保留; - 共享内存段
/dev/shm需挂载为 tmpfs,并在 checkpoint 前通过--ext-mount-map显式声明。
- 国产环境坑点:
- 鲲鹏 920 ARM64 需使用 criu 3.16+ 修复 ARM64 ptrace 寄存器对齐;
- 麒麟 V10 默认关闭 CONFIG_CHECKPOINT_RESTORE,需重编内核或加载 kylin-criu-kmod 补丁;
- 安全模块 Kysec 会拦截
process_vm_readv,需setsebool -P domain_can_ptrace 1。
- 网络与存储:
- 若容器使用 SR-IOV VFs,CRIU 无法保存硬件队列上下文,需降级为 tc redirect 或 veth pair;
- 挂载 本地 NVMe 时,需
--ext-mount-map把/mnt/nvme标记为 external,并在目标节点同路径挂载相同 UUID 文件系统,否则恢复阶段 mmap 会返回 -ENODEV。
- 自动化与回滚:
- 在 GitLab-CI 中封装
docker checkpoint create --leave-running=true,成功后立即把 img 目录(通常 1–3 GB)通过 rsync+barrier 同步到目标节点; - 若 30 s 内未收到
restore成功回调,自动回滚:在原节点docker start --checkpoint=继续运行,保证 SLA。
- 在 GitLab-CI 中封装
答案
-
环境准备
- 所有节点内核 ≥ 5.4,开启 CONFIG_CHECKPOINT_RESTORE、CONFIG_MEM_SOFT_DIRTY;
- 安装 criu 3.16:
yum install -y criu-3.16-1.ky10.aarch64.rpm - Docker daemon 配置
/etc/docker/daemon.json:{ "experimental": true, "runtimes": { "runc": { "path": "runc" } } } - 加载 overlay2 与 br_netfilter 模块,关闭 swap,开启 cgroup v1(CRIU 3.x 对 v2 支持不完整)。
-
构建 MPI 镜像
Dockerfile 关键点:- 使用 multi-stage 先编译 OpenMPI 4.1.5,再拷贝到运行时镜像;
- 创建非 root 用户
mpi,但保留CAP_SYS_ADMIN,CAP_NET_RAW能力字,以兼容 Infiniband; - 将
/dev/infiniband挂载点、/dev/shm大小 2 G 写入镜像元数据。
-
启动容器
docker run -d --name mpi-job \ --cap-add=SYS_ADMIN --cap-add=NET_RAW \ --device /dev/infiniband --ipc=shareable \ --tmpfs /dev/shm:rw,nosuid,nodev,size=2g \ --security-opt seccomp=unconfined \ myrepo/mpi:4.1.5 \ mpirun --allow-run-as-root --mca btl_tcp_if_exclude docker0 --mca pml ob1 -np 4 /opt/mpi-pi -
执行热迁移
- 在原节点 checkpoint:
观察日志docker checkpoint create --checkpoint-dir=/var/lib/docker/checkpoints --leave-running=true mpi-job ck1journalfu -u docker | grep criu,确认 “Frozen” 与 “Dumped” 状态码 0。 - 同步 img 目录:
rsync -avz --progress /var/lib/docker/checkpoints/ck1 target:/var/lib/docker/checkpoints/ck1 - 目标节点 preload NVMe 与 Infiniband 设备,确保
/sys/class/infiniband/mlx5_0路径一致; - 停止原容器(leave-running=false 可跳过):
docker stop mpi-job - 在目标节点 restore:
docker create --name mpi-job-restored myrepo/mpi:4.1.5 docker restore --checkpoint-dir=/var/lib/docker/checkpoints --checkpoint=ck1 mpi-job-restored - 验证 MPI 作业继续计算,
tail -f /tmp/pi.out看到迭代序号未回退,业务中断时间 < 3 s。
- 在原节点 checkpoint:
-
常见问题速查
Error (criu/mount.c:1234): ext mount mapping for /dev/shm not found→ checkpoint 命令追加--ext-mount-map auto;restore -22→ ARM64 寄存器错位,升级 criu ≥ 3.16;IB device mlx5_0 not found→ 目标节点未加载 mlnx-ofed 驱动,需modprobe mlx5_ib。
拓展思考
-
无根(rootless)容器能否热迁移?
当前 CRIU 需要 CAP_SYS_ADMIN 做process_vm_readv,而 rootless docker 仅拥有用户命名空间权限,无法完整 dump 外部 pid 命名空间中的 socket peer。社区 criu 3.17 已实验支持 unprivileged cgroup freeze,但尚未合并到 runc。落地场景仍需 setuid criu 或 systemd socket 激活辅助,国内等保 2.0 要求禁止 setuid,因此超算中心仍沿用特权容器方案。 -
与 Kubernetes 的衔接
通过 KubeVirt libvirt-criu 插件可把 MPI 容器封装成 VM 类型工作负载,利用 virt-launcher 调用 CRIU,实现跨节点迁移;但需解决 DevicePlugin 重新分配 SR-IOV VF 的问题。可自研 CRD MigrationTask,在 restore 前调用 Multus 重新挂载网络,实现 Kubernetes 级别业务无感知迁移。 -
国产化替代风险
若使用 统信 UOS 内核 4.19-lts,其补丁集回退了soft-dirty特性,导致 CRIU 无法追踪内存变更,增量 checkpoint 体积膨胀 5–7 倍。解决方案:- 向厂商索取 soft-dirty backport 补丁;
- 或改用 pre-copy 迭代 + userfaultfd 机制,牺牲部分兼容性,把单次停机压缩到 300 ms 以内,满足运营商 5G 计费系统 SLA ≤ 500 ms 的硬性红线。