快照方案

https://github.com/agent-substrate/substrate/blob/main/docs/architecture.md

  • micro-VM (ateom-microvm): Runs the workload inside a Kata Containers guest on the Cloud Hypervisor VMM. Suspend and resume capture a memory-only VM snapshot and restore it on-demand using userfaultfd memory demand-paging. Container rootfs writes are host-backed: the overlay is assembled on the host (read-only OCI image lower plus a per-actor writable upper) and served to the guest over the single virtio-fs share, so they cost reclaimable host page cache rather than guest RAM, and ROOTFS and MEMORY snapshots ship the upper as its own tar (a ROOTFS restore puts it back and cold-boots on it). DurableDir volumes travel over that same share and are likewise shipped as a tar, so a VOLUMES snapshot can capture them without any guest memory. Each volume is a subdirectory of the share, so an actor can have several at no extra cost in devices, which is why the micro-VM class lifts the single-DurableDir limit that still applies to gVisor.

核心一句话

Cloud Hypervisor(CLH)本身没有一个开关叫 “--no-disk” 直接排除块盘;它的 snapshot 语义是:只保存 VM 运行态(内存、vCPU、设备状态),但不会自动复制块设备的磁盘内容;前提是:你这个 VM 不要依赖 CLH 的 virtio-blk 块盘做根文件系统,把所有持久 / 可写存储挪到宿主机侧(virtio-fs),就是你前面那段 atom-microvm 的方案。

区分两个极易混淆的概念:

  1. 快照会不会拷贝磁盘数据? CLH save 快照不会读取、复制、备份 virtio-blk 后端磁盘文件本身;
  2. 快照会不会保存块设备的设备状态? 会,它会保存 virtio-blk 这个设备的配置、队列状态(设备元数据),但不是磁盘里面的文件内容。
  3. 坑点:如果你用 virtio-blk 当 rootfs,恢复快照时 CLH 只是重新打开同一个后端磁盘文件,磁盘上的数据是快照时间点之后被改动过的,直接恢复会文件系统不一致、crash。

两种路线(对应你文档里的 atom-microvm)

路线 A:标准方案(就是原文用的,推荐)—— 彻底不用 virtio-blk,全部走 virtio-fs

这是它能做到 “内存快照完全不带磁盘数据” 的根本原因:

  1. VM 启动不挂任何 virtio-blk 块设备,没有块盘;
  2. 根文件系统、overlay upper、DurableDir 全部放在宿主机;
  3. 通过 vhost-user-fs(virtio-fs) 共享给 guest;
  4. overlay 在 host 组装:lower = 只读 OCI 镜像,upper = 每个 actor 独立可写目录;
  5. suspend(snapshot):CLH save,只抓 vCPU + RAM + 设备状态(virtio-fs 设备状态很小,只是队列、配置,不带文件内容);
  6. upper 层、DurableDir 在快照之前由上层 runtime 单独打包 tar,作为和内存快照配对的元数据;
  7. restore:
    • 先把对应的 upper tar 解压回宿主机的那个共享目录;
    • 启动 CLH restore,memory_restore_mode = userfaultfd 懒加载内存页;
    • guest 里看到的 rootfs 就是 host 上已经准备好的 overlay;virtio-fs 只是通道,VMM 不碰文件内容。

✅ 优点:

  • CLH 快照本身天然不带磁盘数据;
  • virtio-fs 的文件状态和 VM 内存快照解耦;
  • 可以单独打包 rootfs/volume,就是你文档说的 ROOTFS / VOLUMES snapshot。

重点:virtio-fs 只是一个共享通道,CLH snapshot 不会把 virtio-fs 背后的 host 文件打包进快照。

路线 B:如果你还是需要 virtio-blk(不推荐用于 suspend/resume)

CLH snapshot 不会复制磁盘内容,但磁盘状态是外部的,你自己管理一致性:

  1. 暂停 VM(pause);
  2. 上层业务自己做磁盘一致性处理(guest fsfreeze,或者在 host 侧对磁盘文件做快照 / 拷贝);
  3. 调用 CLH /vm.snapshot 保存内存 + 设备状态;
  4. 恢复时:你要自己保证 virtio-blk 后端文件回到快照那一刻的内容,再 restore CLH 内存快照;
  5. 如果你不做磁盘回滚,直接 restore,guest 看到的是新磁盘内容 + 旧内存状态,大概率文件系统损坏。

👉 这种方式:快照文件本身不大(只有内存),但磁盘一致性由你上层负责,CLH 不帮你管。 这就是为什么 serverless microVM 设计里直接抛弃 virtio-blk、换成 virtio-fs。

CLH API 层面怎么操作(ch-remote / HTTP API)

CLH snapshot/restore 接口本身不分 “带不带磁盘”,它保存的是 VM state:内存、vcpu、所有注册设备的状态(virtio-blk/virtio-fs/net 的设备状态),不包含设备后端存储介质内容cloudhyper...。

# 1. pause VM
ch-remote --api-socket $socket pause

# 2. 保存VM状态(内存+设备状态,不复制块盘文件)
ch-remote --api-socket $socket snapshot --snapshot-path ./mem_snap

# 3. 【上层自己做】此时打包 virtio-fs 的 upper/持久目录 tar
# tar -czf upper.tar ./host-overlay-upper

# restore
# 1. 先把 upper.tar 放回 host 的 virtio-fs 共享目录
# 2. restore,开启 userfaultfd 按需分页
ch-remote --api-socket $socket restore --snapshot-path ./mem_snap --memory-restore-mode userfaultfd

memory_restore_mode: userfaultfd 就是你原文提到的按需缺页加载,只影响内存加载方式,不碰磁盘cloudhyper...

关键约束和踩坑点

  1. virtio-blk 的后端文件路径、fd 会记录在快照里;restore 时 CLH 会打开同一个文件。文件内容变了就不一致。
  2. virtio-fs 的后端目录路径也会记录在快照;但文件内容不在快照里,所以要求上层在 restore 前把目录内容恢复到当时的状态(解压 tar)。
  3. CLH 快照里的 “设备状态” ≠ 磁盘数据,只是设备寄存器、virtqueue、配置,体积很小,几 KB 级别,不要和磁盘内容混在一起。
  4. 不要指望 CLH 帮你做 overlay、upper 层快照,这部分是 Kata / 上层 runtime 的责任,不是 VMM 的能力。

和 QEMU 的对比(帮你理解差异)

  • QEMU:可以做内部快照,把磁盘增量写进快照;也可以只 savevm 内存(同样要求磁盘不变);
  • CLH:设计目标就是云轻量 VM,没有内置的块盘快照 / 增量 COW 能力;它的 snapshot 定位就是运行态内存快照,存储交给外部(virtio-fs / 上层)。

回到你原文的那个架构的设计本质

为什么它能做到 suspend/resume 只抓内存、rootfs/volume 单独 tar? 不是 CLH 有参数排除磁盘,而是它根本不让 guest 的根文件系统放在 CLH 管理的块设备里,全部卸载到 host virtio-fs,把文件状态移出 VMM 的快照域。VMM 只负责 CPU + 内存 + 轻量 virtio 设备通道,文件状态交给上层单独版本化、打包。

 

 

 

 

 

ch-remote 教程(Cloud Hypervisor 控制工具,贴合你前面 atom-microvm /snapshot + uffd 场景)

ch-remote 是 Cloud Hypervisor(CLH)配套的 CLI,本质是调用 CLH 的 HTTP Unix socket API,用来控制已经启动好的 cloud-hypervisor VMM,不是用来启动 VM 的;启动 VM 还是 cloud-hypervisor 主二进制,必须带上 --api-socket 才可以被 ch-remote 控制。 重点:CLH snapshot 只保存 VM 内存、vCPU、virtio 设备状态;不会自动打包 virtio-blk /virtio-fs 后端文件内容,正好对应你关心的「只抓内存快照、磁盘 / 文件状态外部管理」。

1. 安装

两种方式:下载静态二进制 / 源码编译

# 直接下载静态版(推荐,和CLH版本对齐)
wget https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/v48.0/ch-remote-static
mv ch-remote-static /usr/bin/ch-remote
chmod +x /usr/bin/ch-remote

# 源码编译(在cloud-hypervisor仓库内)
cargo build --release
# target/release/ch-remote

⚠️ 版本强匹配:ch-remote 和 cloud-hypervisor 版本不一致会报 API 不兼容。

2. 前置:启动 CLH VM(必须开 api-socket)

# 示例:不带virtio-blk、只用virtio-fs(就是atom-microvm方案!)
# 先提前启动 virtiofsd
virtiofsd --socket-path=/tmp/virtiofs.sock --shared-dir=/host/overlay-share --cache=never

# 启动CLH,暴露unix api socket
cloud-hypervisor \
  --api-socket /tmp/clh.sock \
  --cpus boot=1 \
  --memory size=256M \
  --kernel ./vmlinux \
  --cmdline "console=hvc0 rootfstype=virtiofs root=myfs rw" \
  --fs tag=myfs,socket=/tmp/virtiofs.sock
  • --fs:vhost-user-fs,没有块盘 virtio-blk,所以快照天然不带磁盘数据
  • --api-socket /tmp/clh.sock:ch-remote 连接点
  • 后面所有 ch-remote 命令都要带 --api-socket /tmp/clh.sock

通用前缀简写:

export CH_SOCK="/tmp/clh.sock"
ch-remote --api-socket $CH_SOCK 子命令

3. 基础常用命令

# 查看VM信息
ch-remote --api-socket $CH_SOCK info

# 暂停VM(snapshot之前必须pause,保证一致性)
ch-remote --api-socket $CH_SOCK pause

# 恢复运行(普通resume,不是快照restore)
ch-remote --api-socket $CH_SOCK resume

# 关机
ch-remote --api-socket $CH_SOCK shutdown

# 重启
ch-remote --api-socket $CH_SOCK reboot

# 热添加 virtio-fs(多卷,对应文档里多个DurableDir)
ch-remote --api-socket $CH_SOCK add-fs tag=data1,socket=/tmp/virtiofs-data1.sock
ch-remote --api-socket $CH_SOCK add-fs tag=data2,socket=/tmp/virtiofs-data2.sock

# 热加磁盘(如果你非要用virtio-blk,不推荐snapshot场景)
ch-remote --api-socket $CH_SOCK add-disk path=/tmp/disk.raw

# 查看性能计数器
ch-remote --api-socket $CH_SOCK counters

4. 核心:Snapshot & Restore(你最关心的,内存快照、userfaultfd 懒加载)

流程:pause → snapshot → 【上层自己tar virtiofs upper/volume目录】 CLH snapshot 输出是一个目录,里面:config.json、state.json、memory-ranges(内存快照),不含 virtiofs 后端文件intelkevin...

4.1 做快照(只抓 VM 运行态,不带文件)

# 1. 先暂停VM(必做,防止内存状态撕裂)
ch-remote --api-socket $CH_SOCK pause

# 2. 快照,URI格式 file://快照目录,目录要预先建好
mkdir -p /snap/mysnap1
ch-remote --api-socket $CH_SOCK snapshot file:///snap/mysnap1

# 3. 【非常关键,上层runtime做】打包virtiofs的upper可写层(rootfs改动)
tar -czf /snap/upper.tar /host/overlay-share/upper

# 4. 可选:恢复VM继续跑
ch-remote --api-socket $CH_SOCK resume

✅ 这里的本质:CLH 只管内存快照;文件状态交给外面 tar 单独保存,和快照解耦,就是原文的 ROOTFS / VOLUMES snapshot。

4.2 Restore,支持 userfaultfd(按需缺页加载,uffd)

memory_restore_mode 两个取值:

  • full:一次性全部把内存加载进来,传统方式
  • userfaultfd:懒加载,访问到哪一页才拉哪一页(atom-microvm 用的模式) ⚠️ restore 不需要先启动 cloud-hypervisor;restore 命令本身会拉起 VM 并加载快照
# 恢复前:先把 upper.tar 解压回 virtiofs 的host共享目录!!
rm -rf /host/overlay-share/upper
tar -xzf /snap/upper.tar -C /host/overlay-share

# restore + userfaultfd 按需分页
ch-remote --api-socket $CH_SOCK restore \
  --snapshot-path file:///snap/mysnap1 \
  --memory-restore-mode userfaultfd

坑:快照里记录了原来的 virtio-fs socket 路径,restore 时必须保证这个 socket 存在、后端目录已经恢复到快照时刻内容,否则 guest 看到的文件系统是错的、不一致。

快照目录内容说明

/snap/mysnap1/
├── config.json    # VM硬件配置(vcpu、内存、设备列表)
├── state.json     # vCPU、virtio设备队列、寄存器状态
└── memory-ranges  # 内存页数据,这是快照大头

CLH 不会读取 / 备份 virtio-fs 或者 virtio-blk 后端文件。

5. Live Migration(顺带)

# 目标端:准备接收迁移
ch-remote --api-socket $DST_SOCK receive-migration unix:/tmp/mig.sock
# 源端:发送迁移
ch-remote --api-socket $SRC_SOCK send-migration --local unix:/tmp/mig.sock

同样,迁移只迁移内存 + 设备状态,磁盘 / 文件由外部保证一致。

6. 完整时序(atom-microvm suspend/resume 标准流程)

1. 运行中的CLH VM(virtio-fs,无virtio-blk)
2. ch-remote pause
3. ch-remote snapshot → 生成内存快照目录
4. 上层:tar upper → rootfs快照;tar durable dir → volume快照
5. ch-remote resume(可选,如果还要继续跑这个VM)

恢复时:
A. 解压 upper.tar 回 host overlay share
B. 启动restore,--memory-restore-mode userfaultfd
C. VM恢复,内存缺页时才读取memory-ranges里的页

7. 重要踩坑清单(结合你前面的问题)

  1. ❌ 不要在有 virtio-blk 根盘的情况下直接 restore 内存快照:CLH 不会回滚磁盘,内存是旧状态、磁盘是新状态,直接 fs panic。想做到快照不含磁盘,方案就是彻底不用 virtio-blk,全部 rootfs 走 virtio-fs。
  2. userfaultfd 是恢复阶段参数,不改变快照文件大小,只改变加载策略。
  3. virtio-fs 的 cache=never:让 guest 不持有文件缓存,缓存落在 host page cache,就是原文说的 reclaimable host page cache。
  4. 快照目录权限:CLH VMM 进程要有读写权限;rootless 场景需要 uid/gid 映射。
  5. 跨机器迁移快照:你需要同时迁移三样东西:
    • snapshot 目录(CLH 内存快照)
    • upper.tar(容器可写层)
    • durable volume tar(持久目录)
  6. pause 之后 snapshot 是一致性快照;不 pause 直接 snapshot 是 crash-consistent,不适合函数 suspend 场景。

8. 查看帮助

ch-remote --help
ch-remote pause --help
ch-remote snapshot --help
ch-remote restore --help




ch-remote 可以之间管理 kata-container 么?

一句话结论

ch-remote 不能直接管理 Kata Containers(Pod / 容器、OCI 生命周期);但如果 Kata 后端 VMM 选的是 Cloud Hypervisor(CLH),你可以找到 CLH 的 api-sock,用 ch-remote 直接操作这个底层 CLH VM 本身(pause/snapshot/restore),但会绕开 Kata shim,风险很大kata-conta...

先分清两层:

  1. Kata(containerd-shim-kata-v2 /runtime-rs):容器 / Pod 层,对外是 CRI/OCI 接口,负责镜像拉取、容器启动、exec、log、网络、overlay、virtiofsd 编排、kata-agent 通信(vsock/ttrpc)。这一层不是 ch-remote 管的
  2. Cloud Hypervisor(CLH):Kata 底层的 VMM,Kata shim 会拉起 cloud-hypervisor 进程;Kata 配置开启 CLH 的--api-socket后,这个 CLH 进程自带 CLH HTTP API,ch-remote 只是 CLH VMM 的客户端,只管 VM 硬件、vCPU、内存、VMM 快照,不知道 Pod、容器、OCI、Kata agentAmazon Web...

架构关系

containerd/cri → kata shim(runtime-rs) → 启动 cloud-hypervisor(CLH) + virtiofsd
                                              ↓
                                       CLH暴露 /run/vc/vm/xxx/clh-api.sock
                                              ↓
                                    ch-remote 只能连这个 sock

默认新版 Kata runtime-rs 默认 VMM 是 Dragonball,不是 CLH!Dragonball 没有 CLH 这套 api 和 ch-remote,自然完全不能用 ch-remote 控制kata-conta...

能不能抓 Kata 里 CLH 的 sock,直接 ch-remote snapshot?

✅ 技术上可以做到(就是你前面 atom-microvm 那条路) Kata 跑 CLH 时,每个 Pod 对应的 VM 的 socket 一般在: /run/vc/vm/<sandbox-id>/clh-api.sock

# 找到sandbox id
crictl pods
# 然后
ch-remote --api-socket /run/vc/vm/xxx/clh-api.sock pause
ch-remote --api-socket /run/vc/vm/xxx/clh-api.sock snapshot ...

⚠️ 但是:这是旁路操作,绕过 Kata shim,坑非常多

  1. Kata shim 不知道你偷偷 pause/snapshot/restore 了 VM;shim、agent、containerd 状态不同步 → containerd 认为 Pod 还在跑,VM 被你暂停、恢复后 agent 连接异常、exec/log 挂掉、资源泄漏
  2. Kata 内部的 virtio-fs、overlay、upper 层、volume,Kata shim 才是所有者;你用 ch-remote restore 内存快照的时候,你自己要保证 virtiofs 后端目录(upper)恢复到快照时刻,Kata shim 不会帮你处理这个 tar 打包 / 解压逻辑
  3. Kata 有自己的 VM 生命周期、监控、清理;你外部 restore 出来一个 VM,shim 不认,后续删除 Pod 的时候 shim 可能直接杀掉这个 CLH 进程,快照白做
  4. Kata 本身原生不支持 “外部 VMM 快照 + uffd restore”,Kata 的快照 / Checkpoint 走的是 Kata 自己的 checkpoint 机制(CRIU,guest 内容器 checkpoint),不是 CLH VMM 内存快照

两种方案对比(贴合你 atom-microvm 目标)

  1. 方案 A(官方 Kata):直接用 Kata,不碰 ch-remote
    • 优点:符合 CRI、kubectl、containerd 状态一致性
    • 缺点:Kata 原生是 CRIU 容器检查点,不是 CLH 的 VMM 级内存快照 + userfaultfd 懒加载,不是你文档里的模式
  2. 方案 B(atom-microvm 这种改造路线):Kata 只复用 Kata agent、guest 内核、virtiofs;上层自己接管 CLH 生命周期,自己管理 snapshot/upper tar,不用 kata shim 管理 VM 生命周期
    • 本质:不再让 kata shim 去启动和管理 CLH,而是自己启动 CLH + virtiofsd,自己对接 kata-agent(这就是 atom-microvm),此时 ch-remote 就是你的标准控制面
    • 这个模式下,它已经不是标准 Kata Pod 了,是 “借用 Kata guest 内核 + agent 的独立 CLH microVM”

重要区分(回答你最容易混淆的点)

  • ch-remote ≠ Kata runtime CLI
  • ch-remote 看不到容器、看不到 OCI spec、不能 kubectl exec、不能拉镜像;它只能看到 vCPU、内存、virtio 设备、VM 内存状态
  • Kata shim 会做:镜像→overlay lower/upper、启动 virtiofsd、启动 guest 内 kata-agent、在 guest 里启动 OCI 容器进程;ch-remote 完全不懂这些业务语义

实操建议

如果你目标就是原文的 atom-microvm(CLH + virtiofs + uffd 内存快照,rootfs upper 单独 tar): 👉 不要用标准 kata shim 去启动 CLH,自己直接启动 cloud-hypervisor + virtiofsd,guest 里放 kata kernel + kata agent,自己实现上层 runtime 管理 upper/volume tar,然后用 ch-remote 做 suspend/resume; 如果你要跑标准 K8s/Kata Pod,就不要用 ch-remote 去操作底层 CLH,状态不同步会炸。

posted @ 2026-10-13 23:50  rincloud  阅读(19)  评论(0)    收藏  举报