使用 CRIU 对 MPI 容器进行热迁移

解读

国内金融、电信、超算中心在国产化替代背景下,对“在线业务不中断”与“资源利用率最大化”提出硬性 KPI。MPI 作业常跑在 Slurm+Infiniband 的专属分区,一旦节点需要腾退给更高优先级任务,热迁移成为唯一选择。Docker 默认只支持“停-拷-启”冷迁移,CRIU(Checkpoint/Restore In Userspace) 是唯一能在用户态实现进程级热迁移的上游方案,因此面试官想验证候选人是否能把“容器+MPI+CRIU”这条技术栈真正跑通,并解决国产化环境(鲲鹏、麒麟、OpenEuler)下的权限、驱动、网络三大痛点。

知识点

  1. CRIU 原理:通过 /proc/$pid/fd/proc/$pid/map_files 抓取进程地址空间、已打开文件、信号、定时器、Unix 域套接字、共享内存、ptrace 寄存器,生成 img 文件;恢复时利用 mmap 精准重建,依赖内核 CONFIG_CHECKPOINT_RESTORE 与 3.11+ 版本。
  2. Docker 集成:Docker ≥ 20.10 实验特性 --checkpoint --checkpoint-dir,底层调用 docker-runc checkpoint,依赖 runc 1.0+、criu 3.15+;必须开启 Docker daemon 实验特性 "experimental": true
  3. 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 显式声明。
  4. 国产环境坑点
    • 鲲鹏 920 ARM64 需使用 criu 3.16+ 修复 ARM64 ptrace 寄存器对齐;
    • 麒麟 V10 默认关闭 CONFIG_CHECKPOINT_RESTORE,需重编内核或加载 kylin-criu-kmod 补丁;
    • 安全模块 Kysec 会拦截 process_vm_readv,需 setsebool -P domain_can_ptrace 1
  5. 网络与存储
    • 若容器使用 SR-IOV VFs,CRIU 无法保存硬件队列上下文,需降级为 tc redirect 或 veth pair
    • 挂载 本地 NVMe 时,需 --ext-mount-map/mnt/nvme 标记为 external,并在目标节点同路径挂载相同 UUID 文件系统,否则恢复阶段 mmap 会返回 -ENODEV。
  6. 自动化与回滚
    • 在 GitLab-CI 中封装 docker checkpoint create --leave-running=true,成功后立即把 img 目录(通常 1–3 GB)通过 rsync+barrier 同步到目标节点;
    • 若 30 s 内未收到 restore 成功回调,自动回滚:在原节点 docker start --checkpoint= 继续运行,保证 SLA。

答案

  1. 环境准备

    • 所有节点内核 ≥ 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 支持不完整)。
  2. 构建 MPI 镜像
    Dockerfile 关键点:

    • 使用 multi-stage 先编译 OpenMPI 4.1.5,再拷贝到运行时镜像;
    • 创建非 root 用户 mpi,但保留 CAP_SYS_ADMIN,CAP_NET_RAW 能力字,以兼容 Infiniband;
    • /dev/infiniband 挂载点、 /dev/shm 大小 2 G 写入镜像元数据。
  3. 启动容器

    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
    
  4. 执行热迁移

    • 在原节点 checkpoint:
      docker checkpoint create --checkpoint-dir=/var/lib/docker/checkpoints --leave-running=true mpi-job ck1
      
      观察日志 journalfu -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
  5. 常见问题速查

    • 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

拓展思考

  1. 无根(rootless)容器能否热迁移?
    当前 CRIU 需要 CAP_SYS_ADMIN 做 process_vm_readv,而 rootless docker 仅拥有用户命名空间权限,无法完整 dump 外部 pid 命名空间中的 socket peer。社区 criu 3.17 已实验支持 unprivileged cgroup freeze,但尚未合并到 runc。落地场景仍需 setuid criusystemd socket 激活辅助,国内等保 2.0 要求禁止 setuid,因此超算中心仍沿用特权容器方案。

  2. 与 Kubernetes 的衔接
    通过 KubeVirt libvirt-criu 插件可把 MPI 容器封装成 VM 类型工作负载,利用 virt-launcher 调用 CRIU,实现跨节点迁移;但需解决 DevicePlugin 重新分配 SR-IOV VF 的问题。可自研 CRD MigrationTask,在 restore 前调用 Multus 重新挂载网络,实现 Kubernetes 级别业务无感知迁移

  3. 国产化替代风险
    若使用 统信 UOS 内核 4.19-lts,其补丁集回退了 soft-dirty 特性,导致 CRIU 无法追踪内存变更,增量 checkpoint 体积膨胀 5–7 倍。解决方案:

    • 向厂商索取 soft-dirty backport 补丁;
    • 或改用 pre-copy 迭代 + userfaultfd 机制,牺牲部分兼容性,把单次停机压缩到 300 ms 以内,满足运营商 5G 计费系统 SLA ≤ 500 ms 的硬性红线。