后复制迁移
后复制用于处理无法收敛,或收敛过慢的迁移。 优点是迁移流量和耗时存在上界;缺点是在后复制阶段,任意一端故障都会导致客户机丢失。
目标端在内存尚未全部传完时启动 CPU。 访问尚未到达的页面会触发缺页,QEMU 将其转换为向源端请求页面。
后复制可以与普通预复制结合:如果预复制在指定时间内未完成,就切换到后复制。
启用后复制
迁移开始前,在源端和目标端监视器执行:
migrate_set_capability postcopy-ram on
随后按普通方式启动迁移,最初仍采用预复制。执行:
migrate_start_postcopy
即可切换到后复制。可以在迁移刚开始时或之后任意时刻执行,迁移结束后执行也无害。
内部实现
状态机
状态依次为 ADVISE、DISCARD、LISTEN、RUNNING、END,见 postcopy_state。
Advise
只要启用了后复制,迁移开始时就设置,即使尚未发出切换命令。 目标端检查操作系统支持,并准备适用于后复制的 RAM 映射。 缺少系统支持时会尽早失败。 由收到 POSTCOPY_ADVISE 触发。
Discard
收到首个 discard 命令后进入。 执行前通过 madvise 禁用大页,避免后复制阶段生成新大页, 并拆分包含待丢弃页面的大页。
Listen
数据包首个 POSTCOPY_LISTEN 命令切换到该状态,并启动监听线程接管迁移流的页面接收。 主线程继续处理数据包。 此时对 RAM 启用缺页检测,Linux 使用 userfault 机制。
Running
POSTCOPY_RUN 使目标端同步全部状态,启动 CPU 和 I/O 设备。 主线程完成数据包处理后恢复类似普通预复制完成后的行为, 但不能执行正常迁移结束时的清理。
End
监听线程退出并清理迁移状态,迁移完成。
设备传输
加载设备状态可能访问尚未到达的客户机 RAM,触发需要源端解决的缺页。 因此设备加载期间,迁移流必须仍能接收页面。 为此,在加载前必须先完整读取设备数据,释放流供页面使用。 实现方式是把设备数据打包成一个可一次读取的整体。
源端行为
进入后复制前,迁移流与普通预复制相同, 只在开头增加 postcopy advise,通知目标可能切换。 切换时,源端先发送页面丢弃数据,再构造包含以下内容的数据包:
postcopy listen 命令。
设备状态:与预复制相同的若干节,但不包含 RAM 等可以后复制的设备。
postcopy run 命令。
数据包作为 CMD_PACKAGED 的数据部分发送,内部格式与主迁移流相同。
后复制期间,源端像预复制一样扫描脏页并主动发送。 但收到目标缺页请求后,会从请求位置重新开始扫描, 使请求页及其后续页面尽早到达,期待这些相邻页也很快被访问。
目标端行为
初始与预复制相同,单线程读取迁移流。 postcopy advise 和 discard 改变 RAM 管理方式,但不影响流处理。
------------------------------------------------------------------------------
1 2 3 4 5 6 7
主 -----DISCARD-CMD_PACKAGED ( LISTEN DEVICE DEVICE DEVICE RUN )
线程 | |
| (页面请求 )
| \___
v \
监听线程: --- 页面 -- 页面 -- 页面 -- 页面 -- 页面 --
a b c
------------------------------------------------------------------------------
收到 CMD_PACKAGED(1)时:
将数据包全部读入内存,即图中括号部分。 主线程递归调用 qemu_loadvm_state_main 处理内容(2), 包括命令(3、6)和设备(4 等)。
收到数据包首条 postcopy listen(3)时:
启动线程 a 接管迁移流,主线程继续加载数据包。 监听线程既接收普通后台页 b,也接收设备加载缺页(5)请求返回的页 c, 使主线程得以继续。
数据包最后的 RUN(6):
启动目标 CPU。 数据包结束(7)后,主线程恢复正常工作,不再参与迁移; 监听线程继续接收页面,直到结束。
源端页面位图
与预复制基本相同,每一位表示对应页需要发送。 预复制期间,CPU 写脏页面时会更新位图。 后复制期间源 CPU 已停止,不应再产生脏页,发送相应页面后清除位即可。
后复制特性
恢复后复制
后复制的错误处理不同于预复制。 发生网络等错误时,虚拟机数据分布在两端,无法简单地使迁移失败并回退。 两端会进入暂停状态,需要恢复阶段才能继续。
通常按以下流程恢复:
网络故障时,两端进入 POSTCOPY_PAUSED。
网络恢复,或提供新网络后,在目标端发送 QMP migrate-recover,建立新通道。
源端发送带 resume=true 的 migrate, 进入 POSTCOPY_RECOVER_SETUP,尝试重新建立通道。
连接成功后,两端进入 POSTCOPY_RECOVER,通过握手同步虚拟机状态。 例如,在断网瞬间发送、途中丢失的页面必须重新发送。
同步完成后,两端回到 POSTCOPY_ACTIVE,继续迁移。
迁移暂停期间,虚拟机逻辑上仍可运行,访问已到达目标的页面不受影响。 但访问缺失页的线程会等待,可能一直阻塞到恢复完成。
影响也取决于客户机配置。例如启用异步缺页时,客户机可以主动调出等待页面的线程。
大页支持
后复制支持 hugetlbfs 内存,但要求:
目标 Linux 内核支持大页 userfault。
两端 RAMBlock 使用相同页大小。
大页不足时,-mem-path /dev/hugepages 可能回退到普通 RAM,违反上一条件。 使用 -mem-prealloc 强制分配大页。
谨慎选择大小。2 MB 通常适合;1 GB 可能有问题, 因为通过 10 Gbps 链路传输约需一秒,完整页面到达前目标线程一直阻塞。
抢占模式
QEMU 8.0 引入 postcopy preempt, 将目标明确缺页请求的紧急页面放到独立抢占通道,而非在后台迁移通道排队。 关注缺页延迟的用户应启用此功能,默认未启用。
阻塞时间统计
Blocktime 表示 vCPU 因缺页处于可中断睡眠的时间。 目标端既计算所有 vCPU 阻塞时间的重叠指标,也分别计算各 vCPU 的时间。 在目标监视器启用:
migrate_set_capability postcopy-blocktime on
通过 QMP query-migrate 获取。 postcopy-blocktime 表示所有 vCPU 的重叠阻塞时间, postcopy-vcpu-blocktime 列出各 vCPU 的阻塞时间。