生命周期与冷/热轮换
cat 的生命周期状态、冷/热轮换,以及保证销毁/迁移前不丢写的 drain 流程。
核心不变量:数据面不在 guest 里
一个 cat 是一台 Cloud Hypervisor 微虚机,它通过 virtio-fs 挂载一个 dozycat 卷。真正负责"把 writeback 暂存上传到对象存储、维持元数据会话"的,是 宿主机上的 JuiceFS 客户端——不是 guest。这条解耦是整页的基础。
pause、snapshot 乃至杀掉 VMM 进程都不会中断宿主机的上传。冻结一只 cat 不会冻结它的持久化。背景概念(node、cat-agent、volume、route)见 概念与组件;稳定路由见 稳定路由。
生命周期状态
cat 在以下状态之间转换。cat-agent 驱动转换,dozycat 注册表记录观测状态。
| 状态 | 含义 | Cloud Hypervisor 动作 | 吉祥物 |
|---|---|---|---|
| Running | vCPU 执行中、内存常驻 RAM | 已启动的 VM | 🐱 醒着的猫 |
| Paused(热停 / hot-stop) | vCPU 冻结;内存持久化为本地快照(崩溃可恢复,不是仅留 RAM);VMM 保持 paused 以便瞬时恢复 | ch-remote pause 然后本地 snapshot | 😴 打盹的猫(亚秒唤醒) |
| Snapshotted(冷 / cold-stop) | 内存/设备状态落盘;VMM 进程可退出 | ch-remote snapshot 然后 shutdown | 🧊 冰冻的猫 |
| deep-cold(深冷) | 无进程;可丢弃快照以省存储。请求到达时重新激活(数据由卷恢复) | 无进程,按需 boot | 📦 收起来的猫 |
| Terminated | 不可恢复的拆除;先 drain 保证不丢写 | drain → shutdown → 释放会话/锁 | 👋 走掉的猫 |
pause + 本地内存快照——而不是 RAM-only 暂停。内存被持久化到本地快照,宿主机崩溃后仍可恢复。亚秒级唤醒来自 paused 的 VMM;若 VMM 已死,则从本地快照 --restore。冷/热轮换
在规模化场景(一卷一 VM,约 1000 并发、10 万级轮换),cat 在热↔冷之间轮换以省成本:
- 热启动(hot-start):
ch-remote resume。VMM 还活着,内存原封不动——bootid不变、内存计数器接着走。亚秒级。 - 冷启动(cold-start):以
--restore拉起新的 Cloud Hypervisor 进程,重连 virtio-fs,再resumeguest。 - 深冷激活(deep-cold):无快照可用时全新 boot + 重挂卷。持久数据从 JuiceFS/S3 卷恢复(
bootid改变 = 全新引导)。
对 xxxxx.dozyfs 的一次请求即可触发激活:热时为毫秒级,深冷时为秒到数十秒。激活只改写绑定行(epoch+1),稳定名字和客户端连接方式始终不变——详见 稳定路由。
热度驱动的预热(warmup)
dozycat 解析访问日志、识别热目录,并在 cat 真正跑起来之前把"该热的"焐热。dozycat 注册表保存热度图与 warmup 状态,热度模块据此生成 juicefs warmup 计划,把热目录预拉进本地盘缓存。这样冷/深冷激活后首批读不必全部回源对象存储。
转换契约
| 转换 | 动作前必须满足 | 数据面行为 | 结果 |
|---|---|---|---|
| 启动 | 宿主挂载就绪、virtio-fs socket 就绪 | 建立宿主会话;可选 warmup | Running |
| 热停 | guest sync;pause;内存持久化为本地快照 | 宿主上传继续 | Paused |
| 热启 | 无 | 宿主会话本就活着 | Running |
| 冷停 | guest sync;quiesce 写入;pause;snapshot | VMM 退出后宿主上传继续 | Snapshotted |
| 冷启 | 快照文件存在;virtio-fs 可重连 | 宿主会话延续或重建 | Running |
| 终止 | quiesce + 干净 drain | 暂存清零后释放会话 | Terminated |
Drain 运行手册:销毁/迁移前不丢写
启用 --writeback 时,写入先落到本地 <cache-dir>/<UUID>/rawstaging/,上传到对象存储是异步的。这意味着 guest 的 sync/fsync 并不能证明每个暂存块都已抵达对象存储——它只把 guest 脏页推过宿主文件系统边界。
真正可信的持久化信号是:宿主 JuiceFS 挂载报告 juicefs_staging_block_bytes == 0 且 rawstaging 目录树为空,随后干净卸载。deploy/dozyfs-drain.sh 实现了这套检查:
dozyfs-drain.sh <mountpoint> <cache-dir>
可选环境变量:
DRAIN_TIMEOUT=540
DRAIN_OK_MARKER=/var/jfsCache/drain-ok
标记文件在开始时被删除,只有在干净 drain + 干净卸载后才写入。超时、强制卸载、目录缺失、命令失败一律返回非零并且不留下干净标记。
终止时的宿主顺序:
- 若 VM 在跑,先 quiesce 写入并执行 guest
sync。 - 在宿主运行
dozyfs-drain.sh <mountpoint> <cache-dir>。 - 必须拿到干净标记(或零退出码)后,才能释放锁/会话、删除本地状态、销毁或迁移 VM。
rawstaging 字节继续归零——上传不依赖 guest 存活。验证
所有生命周期与 drain 行为都有回归测试覆盖:
- 热停/启:
sync→ pause → 本地内存快照;从 paused 的 VMMresume(VMM 已死则从本地快照 restore)。bootid不变证明内存确实保留。 - 冷停/启:sync → pause → snapshot → shutdown → restore → resume;KV 字节级一致,
bootid改变证明数据来自卷而非 RAM。 - 干净 drain:写入 → drain → 校验标记 → 重挂 → 校验内容。
- 积压处理:制造暂存积压,drain 阻塞直到清零,或超时返回非零。
- 失败路径:强制卸载或移除依赖,确认不产生干净标记。
真机验收(需 /dev/kvm、Cloud Hypervisor、virtiofsd、JuiceFS/S3)已在 AWS c5.metal 上跑通:多 cat 同台启动、逐 cat 隔离、热停保 RAM、冷停保 KV 字节级一致、深冷靠 boot+重挂卷恢复。源码见 github.com/dozycat/dozyfs。
dozycat