RancherVM 源码剖析③:基于 QEMU QMP 协议实现虚拟机热迁移的完整原理(含回滚机制)

发布时间:2026/8/23 11:48:30
RancherVM 源码剖析③:基于 QEMU QMP 协议实现虚拟机热迁移的完整原理(含回滚机制) RancherVM 源码剖析③基于 QEMU QMP 协议实现虚拟机热迁移的完整原理含回滚机制【免费下载链接】old-vm(OBSOLETE) Package and Run Virtual Machines as Docker Containers项目地址: https://gitcode.com/gh_mirrors/ol/old-vmRancherVM一个将虚拟机打包并以 Docker 容器形式运行在 Kubernetes 上的开源项目Package and Run Virtual Machines as Docker Containers不仅能让虚拟机像 Pod 一样被调度还实现了真正的虚拟机热迁移在不停机的情况下把正在运行的虚拟机从一个节点搬到另一个节点。本文从源码出发带你完整理解它是如何利用QEMU QMP 协议完成迁移的以及迁移中途取消时回滚机制是如何兜底的。什么是虚拟机热迁移热迁移Live Migration指在虚拟机持续运行的状态下将其内存、CPU 状态、磁盘等迁移到目标主机全程业务几乎无感知。在 RancherVM 中触发方式极其简单把 VirtualMachine 资源里的nodeName字段改成另一个节点控制器就会自动开始迁移。源码中的状态机为此新增了migrating状态定义见 pkg/apis/ranchervm/v1alpha1/types.gorunning→ 用户修改nodeName后 →migrating→ 迁移完成回到running但落在新节点迁移中途用户把nodeName改回原节点 →触发回滚虚拟机原地恢复运行整体架构一次热迁移的 4 步走热迁移涉及三个角色源节点上正在跑的 VM Pod、目标节点上新建的 VM Pod以及一个专门执行迁移指令的迁移 Job Pod。步骤动作关键源码① 状态切换检测到nodeName变化状态置为migratingpkg/controller/vm/machine.go② 准备目标在目标节点创建第二个 VM Pod等待两边就绪pkg/controller/vm/migrate.go③ 执行迁移创建 Job通过 QMP 向源 QEMU 下发migrate命令pkg/qemu/job.go、pkg/qemu/client.go④ 清理收尾删除旧 Pod 和 Job切换 VirtualMachine 归属pkg/controller/vm/migrate.go入口逻辑在pkg/controller/vm/machine.go的start()中当发现 VM 处于运行状态、但期望节点与 Pod 实际所在节点不一致时就调用migrateMachine()。核心原理如何与 QEMU 对话QMP 协议QMPQEMU Machine Protocol是 QEMU 提供的JSON over Socket管理接口。每个 VM Pod 内都会暴露一个 Unix Socket形如vm名称_monitor.sockRancherVM 的所有 QEMU 操作都通过它完成。1️⃣ 建立连接三步握手客户端实现非常精巧见 pkg/qemu/client.goNewMonitorClient()通过 Unix Socket 连接到 QEMU 监控端读取 QEMU 主动发来的greeting 消息声明 QMP 能力立即发送qmp_capabilities命令完成能力协商——这一步是 QMP 协议的硬性要求之后才能执行真正的操作命令。连接命令的拼装逻辑在 pkg/qemu/commands.go所有命令都是统一的 JSON 结构{execute: 命令名, arguments: { ... 参数 ... }}2️⃣ 下发迁移指令migrate(uri)命令把源 VM 的全部状态通过目标端提供的TCP 地址tcp:目标PodIP:迁移端口推送过去{execute: migrate, arguments: {uri: tcp:10.42.0.7:43501, detach: true}}detach: true表示 QEMU 异步执行迁移客户端立刻返回之后靠轮询查进度。值得一提的是readReply()的实现细节它从 512 字节缓冲区起步每读满一次就翻倍512 → 1024 → …直到某次读取不满从而优雅地应对任意长度的 JSON 应答。3️⃣ 轮询迁移进度query-migrate真正的迁移引擎在 pkg/qemu/migrate.go 的Migrate()方法它每秒轮询一次逻辑清晰得像一段状态机每秒循环 ├─ readSilently() // 静默吞掉 QEMU 的事件推送如 STOP 事件避免污染应答流 ├─ query-migrate // 查询迁移状态 ├─ status active → 记录已传输内存 / 总内存 / 速率估算剩余时间 ├─ status completed → 迁移成功返回 └─ status failed → 迁移失败报错两个值得学习的细节readSilently()给连接设一个 100ms 的读超时把 QEMU 主动推送的事件悄悄读走否则事件消息会混进query-migrate的应答导致 JSON 解析错乱——这是用长连接驱动 QEMU 时最容易踩的坑剩余时间估算用已耗时 × 总量 / 已传输量 - 已耗时做瞬时速率推算简单实用。迁移 Job把 QMP 客户端打包成 K8s Job控制器本身跑在集群里、够不到节点上的 QEMU Socket所以它采用了一个巧妙的设计创建一个一次性 Kubernetes Job通过 Pod 亲和性把它调度到源 VM 所在的节点上详见 pkg/qemu/job.go 的NewMigrationJob()。这个 Job Pod 有 3 个关键配置Pod 亲和性TopologyKey: kubernetes.io/hostname 源 Pod 标签保证迁移 Pod 和源 VM 落在同一台机器挂载 hostPath把节点上 VM 状态目录挂到/vm这样就能摸到 QEMU 的_monitor.sock执行参数/ranchervm -migrate -sock-path /vm/pod_monitor.sock -target-uri tcp:目标IP:端口对应 cmd/main.go 中的migrate分支——它会创建MonitorClient并直接调用Migrate()。目标端口哪来的新建的目标 VM Pod 在创建时addMigratePort()见 pkg/controller/vm/util.go会随机分配一个 32768~65535 之间的端口写入环境变量MIGRATE_PORT和注解migrate_port。源 QEMU 迁移时就把内存状态流推送到这个端口目标端的 QEMU 早已在该端口监听并接收最终完成 CPU/设备状态的切换。回滚机制迁移失败或用户取消怎么办这是本设计最有工程味的部分核心在 pkg/controller/vm/migrate.go 的migrateMachine()// Check if the user canceled mid-migration if oldPod ! nil machine.Spec.NodeName oldPod.Spec.NodeName machine.Status.State api.StateMigrating { return ctrl.migrateRollback(machine, newPod) }只要用户把nodeName改回原节点控制器在下一次调和循环就会发现期望节点 旧 Pod 所在节点判定为用户取消随即执行migrateRollback()做三件事删除还在跑的迁移 Job源端 QEMU 停止向目标推流迁移自然终止删除目标节点上已创建的影子 Pod把 VirtualMachine 状态从migrating恢复为running。这个机制之所以零数据风险是因为 QEMU 热迁移本身的语义就保证了迁移未成功完成时源端 VM 一直是主执行者目标端只是接收副本任何时刻掐掉迁移业务都还活在源节点。RancherVM 的回滚只是把这个天然安全的状态显式地整理干净。迁移成功后的清理migrationCleanup()则负责收尾更新 VirtualMachine 关联新 Pod、删除源 Pod、删除迁移 Job最后还会删掉noVNC 控制台 Pod——因为控制台也是通过节点上的 Unix Socket 连接 QEMU 的VM 换了节点后必须重建。小结这套设计值得借鉴的 3 个地方协议层极薄pkg/qemu/包不到 300 行就把 QMP 连接、命令封装、进度轮询写得干净利落是学习用长连接驱动外部守护进程的好样例编排层复用 K8s 原语亲和性保证同机执行、Job 承载一次性任务、Informers 的事件驱动调和——迁移全程没有一行自定义调度代码幂等 可取消控制器每一步建目标 Pod、查 Job 是否存在、状态比对都是幂等检查任意时刻被中断都能从状态机里自愈用户改回nodeName即回滚体验上迁移和回退完全对称。至此RancherVM 热迁移的完整链路——nodeName 变更 → 状态机流转 → 目标 Pod 准备 → QMP 下发 migrate → 轮询 query-migrate → 清理 / 回滚——就全部讲完了。【免费下载链接】old-vm(OBSOLETE) Package and Run Virtual Machines as Docker Containers项目地址: https://gitcode.com/gh_mirrors/ol/old-vm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻