生产机上的一次重启,往往意味着一片业务的停顿。KVM 热迁移要解决的就是这件事:把一台正在运行的虚拟机从 A 宿主机搬到 B 宿主机,业务几乎无感知。这篇用一套三节点实验(KVM1 / KVM2 / NFS)走完「克隆 → 配网络 → 搭共享存储 → 建虚拟机 → 迁移 → 验收 → 迁回」的完整链路,并把课件里没写的迁移命令、SSH 免密前提和回退步骤一并补齐。文中命令名均标注英文全称(如
virsh= virtual shell),完整清单见文末《附:命令英文全称速查表》。
本文概要:
| 项目 | 内容 |
|---|---|
| 文章主题 | KVM 虚拟机热迁移的完整实验流程:共享存储搭建、迁移命令、验收与回退 |
| 所属类别 | 云计算 → 虚拟化 → KVM 运维 |
| 知识关键字 | virsh migrate·--live·virt-clone·nfs-server·showmount -e·exports·_netdev·nofail·virt-install·pool-define-as |
主线导读:这篇分两条线走。第一条是步骤线(第四到八章),照着敲就能把虚拟机从 KVM1 搬到 KVM2;第二条是原理线(第一到三章),讲清「为什么磁盘不用搬」「什么条件下才迁得动」。命令的参数解释集中在各命令小节,步骤线里只给要敲的命令本身。
一、热迁移在搬什么:磁盘不动,只搬内存与 CPU 状态
热迁移(live migration)指虚拟机保持开机运行的前提下,从一台宿主机转移到另一台。整个过程业务侧只感知到一次极短的卡顿,通常是几十毫秒到几秒。
1.1 搬过去的是三样东西
| 搬运对象 | 说明 |
|---|---|
| 内存 | 虚拟机当前的全部内存页,分多轮迭代搬完 |
| CPU 设备状态 | vCPU 寄存器、中断控制器状态、时钟偏移 |
| 虚拟机定义 | XML 配置在目标端重建,保证迁移后配置一致 |
磁盘不在搬运清单里。源与目标宿主机必须同时能访问同一份虚拟磁盘,磁盘留在原处,两边只是切换「谁在读谁在写」。这就是共享存储成为核心前提的原因。
1.2 迭代迁移:为什么要搬好几轮
内存不是一块静止的快照。迁移期间虚拟机还在跑,内存页持续被改写,第一轮搬过去的页面到第二轮可能又脏了。解决办法是多轮迭代:
- 第一轮把全量内存页搬到目标端;
- 期间被改写的页标记为「脏页」,下一轮只搬脏页;
- 每轮搬运的增量越来越小,小到一定程度时暂停源虚拟机(通常几十毫秒),把最后剩余部分一次性搬完;
- 目标端恢复运行,源端清理。
这也是热迁移有「可容忍短时间中断」说法的由来——中断只发生在最后暂停的那一瞬间。
1.4 四种可用的共享存储
课件列了 NFS / Ceph / RBD / iSCSI 四种,实际选型看规模:
| 方案 | 形态 | 适合场景 | 注意事项 |
|---|---|---|---|
| NFS | 文件级共享 | 实验环境、中小规模 | 依赖网络,延迟敏感型业务要评估 |
| iSCSI | 块级共享 | 已有 SAN 的环境 | 需要多路径配置防单点 |
| Ceph RBD | 分布式块存储 | 中大型云平台 | 三副本起步,成本高 |
| Ceph FS | 分布式文件存储 | 与 KVM 深度整合 | 调优项多,运维门槛高 |
本实验用 NFS,是因为它配置最简、三台机器就能验证完整链路。生产上 Ceph 与 iSCSI 更常见,但迁移命令与前置条件完全一样——换掉的只是「磁盘放在哪」。
1.3 与另外两种迁移的对照
| 迁移类型 | 虚拟机状态 | 中断时长 | 适用场景 |
|---|---|---|---|
| 冷迁移(静态迁移) | 关机 | 分钟级 | 停机窗口充足的维护 |
| 热迁移(动态迁移) | 保持运行 | 毫秒到秒级 | 不能停的业务 |
课件里「用于可容忍短时间中断,但必须要快速恢复业务。比如轻量级数据库业务,桌面云业务」说的就是热迁移的适用边界:高并发写密集的业务要谨慎,内存脏页产生速度超过搬运速度时,迁移可能迟迟无法收敛。
二、什么时候用:三个真实场景
| 场景 | 动因 | 迁移方向 |
|---|---|---|
| 服务器维护前 | 要重启 / 换硬件,先把虚拟机挪走 | 迁出 → 维护完迁回 |
| 服务器升级前 | 升级过程有中断风险 | 迁出 → 升级完迁回 |
| 整合闲置资源 | 把空闲机上的虚拟机集中,关掉空机器 | 迁往集中节点 |
第三类是数据中心最常用的:一台物理机上跑着几十台虚拟机,运维可以把它们均匀分布到少数几台,然后关掉完全空载的机器省电。
三、前置条件四项
迁移开始前逐项自查,任何一项不满足都会在中途失败:
- 磁盘在共享存储上,且源与目标宿主机都能访问。可用 NFS / Ceph / RBD / iSCSI。
- 目标端有一台规格相同的空虚拟机(CPU 核数、内存大小一致)。名字可以与源相同,也可以不同。
- 两台宿主机之间网络可达,且建议关闭防火墙(本实验
systemctl stop firewalld.service)。生产环境应改为放行迁移所需端口。 - 能接受一次短时间中断。业务层要有重连机制,数据库类业务要确认连接池会重试。
⚠️注意:共享存储上的磁盘文件不能被任何一方以独占方式打开。如果源虚拟机磁盘还在 KVM1 的本地目录,即使手工把文件复制到 NFS 上再改 XML 指向它,也会因为缓存不一致导致数据损坏——必须在共享存储上重新创建虚拟机,或先把磁盘转成共享存储上的 raw/qcow2 文件。
四、搭环境:从一台 KVM 克隆出三节点
实验拓扑:KVM1 是已经做完单机实验的宿主机,从它完整克隆出 KVM2 和 NFS,三台机器通过192.168.108.0/24网段互通。
4.1 完整克隆 KVM1 → KVM2 / NFS
克隆前先还原 KVM1 的快照,保证源状态干净。virt-manager 里右键虚拟机 → 克隆 →选择完整克隆(不要链接克隆,完整克隆才是一份独立磁盘)。
# KVM1 上操作,命令行等价写法virt-clone--originalkvm-node--nameKVM2--auto# --auto:自动生成磁盘与 MACvirt-clone--originalkvm-node--nameNFS--autovirt-clone用法速查表:
| 参数 | 含义 | 英文原意 |
|---|---|---|
--original | 源虚拟机名 | original |
--name | 新虚拟机名 | name |
--auto | 自动生成磁盘文件名与 MAC 地址 | auto |
--replace | 目标已存在时直接覆盖,不再询问 | replace |
--file | 指定新磁盘路径,默认与源同目录 | file |
完整克隆与链接克隆的区别:完整克隆把磁盘完整复制一份,新机器与原机器互不影响;链接克隆只建一个差异盘,原机器的磁盘文件被删,链接克隆立刻失效。要做迁移实验,必须用完整克隆。
4.2 改主机名与 IP
三台机器开机顺序:先 KVM1,再依次开 KVM2、NFS。逐台改主机名和 IP。
# KVM1 上把克隆出来的机器改成 KVM2[root@KVM1 ~]# hostnamectl set-hostname KVM2[root@KVM1 ~]# bash # 换 shell 让新主机名生效[root@KVM2 ~]#[root@KVM2 ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens160TYPE=EthernetBOOTPROTO=noneONBOOT=yesIPADDR=192.168.108.102# 改成本机 IPPREFIX=24GATEWAY=192.168.108.2#UUID=3b58adc4-7bfb-4ce7-bdc5-7893a708ebb3 ← 这一行必须删掉[root@KVM2 ~]# nmcli connection reload ens160 # 让配置生效[root@KVM2 ~]# ip addr show ens160 # 验证地址已变| 机器 | IP | 角色 |
|---|---|---|
| KVM1 | 192.168.108.101 | 源宿主机 |
| KVM2 | 192.168.108.102 | 目标宿主机 |
| NFS | 192.168.108.100 | 共享存储 |
⚠️注意:克隆出来的机器带着和源机器相同的 UUID,网卡配置文件里那行
#UUID=不删,两台机器会有相同的网卡标识,nmcli会拒绝加载并报「连接已存在」。NFS 那台同样照此办理,IP 改成192.168.108.100。
4.3 三节点 hosts 同步
迁移命令要用主机名互访,先把 hosts 配好,再复制到另外两台:
# KVM1 上操作[root@KVM1 ~]# cat >> /etc/hosts << EOF192.168.108.100 NFS192.168.108.101 KVM1192.168.108.102 KVM2 EOF[root@KVM1 ~]# scp /etc/hosts root@KVM2:/etc/hosts # 复制到 KVM2[root@KVM1 ~]# scp /etc/hosts root@NFS:/etc/hosts # 复制到 NFS五、NFS 共享存储:让两台 KVM 看到同一份磁盘
5.1 /etc/exports 五个参数逐个讲
[root@NFS ~]# mkdir /nfs # 用于共享的目录[root@NFS ~]# vim /etc/exports/nfs *(rw,no_root_squash,no_subtree_check)| 参数 | 含义 | 英文原意 |
|---|---|---|
/nfs | 共享出去的目录,第一列固定是路径 | 导出路径 |
* | 允许所有网段访问 | 通配所有客户端 |
rw | 客户端对该目录有读写权限 | read-write |
no_root_squash | 客户端用 root 访问时不映射 root | 不压缩 root |
no_subtree_check | 不检查父目录权限 | 不检查子树 |
root_squash(默认行为)会把客户端的 root 映射成匿名用户,虚拟机的磁盘文件以 root 身份写入时就会权限不足,所以这里必须写no_root_squash。
5.2 服务端验证:本地先通
[root@NFS ~]# systemctl restart nfs-server.service # 重启 NFS 服务[root@NFS ~]# showmount -e localhost # 本地测试共享成功没Export listforlocalhost: /nfs *[root@NFS ~]# systemctl stop firewalld.service # 停止防火墙服务5.3 客户端验证:跨机能看见
# 在 KVM1、KVM2 上分别执行[root@KVM1 ~]# showmount -e 192.168.108.100Export listfor192.168.108.100: /nfs *[root@KVM1 ~]# mkdir /NFS[root@KVM2 ~]# mkdir /NFS两级验证都要过:本地showmount -e localhost能列出来,说明服务端导出正常;跨机showmount -e <NFS IP>能列出来,说明网络与防火墙没挡。只过第一级就往下走,是新手最常卡住的地方。
5.4 挂载与持久化
临时挂载用于立即验证,写进 fstab 才能重启后还在:
mount-tnfs192.168.108.100:/nfs /NFS# 临时挂载df-h/NFS# 验证:应看到 192.168.108.100:/nfs# /etc/fstab 追加一行192.168.108.100:/nfs /NFS nfs defaults,_netdev,nofail00| 选项 | 作用 | 英文原意 |
|---|---|---|
_netdev | 声明这是网络挂载,等网络就绪后再挂 | network device |
nofail | 挂不上也不阻塞启动 | no fail |
_netdev不加,开机时网卡还没起来就去挂 NFS,必然失败;nofail不加,NFS 那台没开会让本机卡在启动阶段。两个都写,是网络存储 fstab 的固定写法。
六、KVM 挂载 NFS 存储池并在共享存储上建虚拟机
6.1 图形化添加存储池
virt-manager 里选中 KVM1 → 左下角+添加新存储:
| 字段 | 填法 |
|---|---|
| Type | netfs |
| Target Path | /NFS(本机挂载目录) |
| Host Name | NFS的地址 |
| Source Path | /nfs(NFS 共享目录) |
KVM2 做同样的操作。
6.2 命令行等价写法
图形化点选背后是这几条命令,脚本化部署时直接抄:
[root@KVM1 ~]# virsh pool-define-as nfs-pool netfs \--source-host192.168.108.100 --source-path /nfs--target/NFS[root@KVM1 ~]# virsh pool-build nfs-pool # 建挂载点目录[root@KVM1 ~]# virsh pool-start nfs-pool # 挂载[root@KVM1 ~]# virsh pool-autostart nfs-pool # 设开机自启[root@KVM1 ~]# virsh pool-list --details # 验证:state 应为 runningpool-define-as用法速查表:
| 参数 | 含义 | 英文原意 |
|---|---|---|
<名称> | 存储池名字 | 位置参数 |
netfs | 池类型,网络文件系统 | network file system |
--source-host | NFS 服务器地址 | source host |
--source-path | NFS 上共享出来的目录 | source path |
--target | 本机的挂载点目录 | target |
pool-define-as只是把定义写进 libvirt 配置,并不会真的挂上。pool-build建目录、pool-start才执行挂载,三步缺一不可。
6.3 在共享存储上创建虚拟机
上传 CentOS 镜像到 KVM1,然后创建虚拟机,磁盘位置选共享存储:
# 命令行写法:磁盘放在 nfs-pool 里virt-install--namevm-web01--ram2048--vcpus2\--diskpool=nfs-pool,size=20,bus=virtio\--cdrom/var/lib/libvirt/images/CentOS-stream-9.iso\--networknetwork=default--graphicsvnc,listen=0.0.0.0\--os-variant centos-stream9--disk pool=nfs-pool,size=20指定磁盘建在存储池里。正常装完系统,重启时会自动弹出光盘。
virt-install用法速查表(全文仅此一张,两处示例共用):
| 参数 | 含义 | 英文原意 |
|---|---|---|
--name | 虚拟机名称 | name |
--ram | 内存大小,单位 MB | random access memory |
--vcpus | 虚拟 CPU 核数 | virtual CPUs |
--disk | 磁盘,可写pool=<池名>,size=<GB>,bus=<总线> | disk |
--cdrom | 挂载安装镜像 | CD-ROM |
--network | 网络,可写network=<网络名> | network |
--graphics | 图形方式,vnc,listen=0.0.0.0监听所有地址 | graphics |
--os-variant | 操作系统类型,影响默认硬件配置 | OS variant |
--noautoconsole | 不自动打开控制台窗口 | no auto console |
--print-xml | 只打印 XML 不真正创建,常配管道给virsh define | print XML |
--location | 指定安装源(网络仓库或 ISO 路径) | location |
--print-xml | virsh define /dev/stdin这个组合很常用:先生成 XML 再定义,得到一台不装系统的空虚拟机,正是热迁移目标端需要的形态。
七、执行迁移:两条路
7.1 前置:目标端建好空虚拟机 + SSH 免密
迁移的目标端要先有一台规格相同的空虚拟机。用virt-clone带--replace不适用(源还在跑),正解是直接建一台不装系统的:
# KVM2 上建一台同规格空虚拟机(不装系统)virt-install--namevm-web01--ram2048--vcpus2\--diskpool=nfs-pool,size=20,bus=virtio\--networknetwork=default--graphicsvnc\--os-variant centos-stream9--noautoconsole--print-xml|virshdefine /dev/stdin再配 SSH 免密,virsh migrate走的是 libvirt 远端协议:
# KVM1 上操作[root@KVM1 ~]# ssh-keygen -t ed25519 -N '' -f /root/.ssh/id_ed25519[root@KVM1 ~]# ssh-copy-id root@KVM2 # 输入 KVM2 的 root 密码一次[root@KVM1 ~]# ssh root@KVM2 'virsh --version' # 验证免密可用7.2 virsh migrate:参数逐个表
virshmigrate--live--persistent--undefinesource\vm-web01 qemu+ssh://KVM2/system| 参数 | 含义 | 英文原意 |
|---|---|---|
--live | 热迁移,虚拟机不停机 | 在线 |
--persistent | 目标端配置持久化,重启后仍在 | 持久 |
--undefinesource | 迁移成功后删除源端虚拟机定义 | 取消源定义 |
--copy-storage-all | 磁盘也一起复制(非共享存储场景) | 复制全部存储 |
--change-protection | 禁止迁移期间被意外中断 | 保护模式 |
qemu+ssh://KVM2/system | 目标 libvirt 地址 | qemu 协议 + SSH 通道 |
--undefinesource对应课件里「将源虚拟机删除」那一步。如果不加,KVM1 上会留下一份已关机的同名定义,之后在 KVM1 上误启动会同时挂载同一份磁盘,造成数据损坏。
7.3 图形化迁移
virt-manager 里右键虚拟机 →Migrate(迁移),填目标主机名KVM2,勾选「迁移后删除源定义」,等待进度条走完。图形化本质就是调用同一条virsh migrate --live。
7.4 迁移前的最后一步:关防火墙
# KVM1、KVM2 都要执行[root@KVM1 ~]# systemctl stop firewalld.service[root@KVM2 ~]# systemctl stop firewalld.service7.5 迁移过程的实时观测
迁移是异步进行的,命令返回后作业仍在后台跑。用domjobinfo看进度:
# 在 KVM1 上执行,另开一个终端[root@KVM1 ~]# virsh domjobinfo vm-web01Job type: Virtual machine migration Operation: Outgoing migration Time elapsed:8124ms Time remaining:43876ms Data processed:1.125GiB Data remaining:2.875GiB Memory processed:1.125GiB Memory remaining:2.875GiB Expected downtime:12ms Compression cache:256.5MiB| 字段 | 含义 | 怎么用 |
|---|---|---|
Data processed/Data remaining | 已搬 / 待搬的内存总量 | 估算还要多久 |
Expected downtime | 预计的最后一次暂停时长 | 判断业务能否接受 |
Memory bandwidth | 当前搬运速率 | 过低说明网络或磁盘是瓶颈 |
⚠️ 关注Expected downtime:它随迭代轮次逐步收敛。若长时间停留在几百毫秒以上,说明脏页产生速度接近搬运速度,迁移可能迟迟无法完成——写密集型业务要警惕这一点。
7.6 限速与大规模迁移
一次迁走十几台虚拟机会把网络占满,影响同网段的其他业务。virsh migrate-setmaxdowntime控制暂停时长上限,带宽则靠存储与网络侧控制:
# 在 KVM1 上执行[root@KVM1 ~]# virsh migrate-setmaxdowntime vm-web01 500 # 暂停上限 500 毫秒[root@KVM1 ~]# virsh migrate-setmaxdowntime vm-web01 0 # 0 = 取消上限,尽快完成暂停上限越大,总迁移时间越短(省去反复迭代),但业务中断感越明显;上限越小中断越短,总时间越长。生产上按业务的敏感程度折中取 100~500 毫秒。
八、验收与回退
8.1 四项验收
迁移命令返回即算成功,但要确认真的落在 KVM2 上:
# 在 KVM2 上执行[root@KVM2 ~]# virsh list --all # 状态应为 running[root@KVM2 ~]# virsh domblklist vm-web01 # 磁盘应指向 /NFS 下的共享文件[root@KVM2 ~]# virsh domjobinfo vm-web01 # 无进行中的作业# 在虚拟机内部执行ipaddr show ens160# 业务连通性仍在,IP 未变domblklist是最关键的一条:如果磁盘还指向 KVM1 的本地路径,说明这台虚拟机根本没建在共享存储上,之前几步有遗漏。
8.2 中途失败的回退
迁移过程中中断,源虚拟机通常仍在运行,业务不受影响。若源端状态异常:
# KVM1 上执行[root@KVM1 ~]# virsh list --all # 确认源还在[root@KVM1 ~]# virsh domjobabort vm-web01 # 放弃未完成的迁移作业[root@KVM1 ~]# virsh snapshot-revert vm-web01 clean # 必要时还原到迁移前的快照8.3 迁回 KVM1
维护 / 升级完成后原路迁回,方向反过来即可:
# 在 KVM2 上操作[root@KVM2 ~]# virsh migrate --live --persistent --undefinesource \vm-web01 qemu+ssh://KVM1/system九、踩坑记录
| 现象 | 根因 | 处置 |
|---|---|---|
nmcli connection reload后 IP 没变 | 配置文件里 UUID 行没删,加载的是旧连接 | 删#UUID=行后重新 reload |
showmount -e <IP>超时 | 防火墙没关,或 NFS 服务没起 | systemctl stop firewalld+systemctl restart nfs-server |
| 客户端 root 写不进共享目录 | exports 用了默认的root_squash | 改成no_root_squash并exportfs -r |
| 迁移报磁盘路径不可访问 | 磁盘不在共享存储,或在 KVM1 本地 | 在共享存储池上重建虚拟机 |
| 迁移后 KVM1 仍留同名定义 | 漏加--undefinesource | 手工virsh undefine vm-web01 |
十、补遗:libvirt 工具族与虚拟化产品清单
10.1 virt-viewer:只做一件事的图形控制台
| 项 | 说明 |
|---|---|
| 作用 | 连接虚拟机的 VNC / SPICE 画面,单独弹出窗口 |
| 不管什么 | 不创建、不修改、不删除虚拟机 |
| 典型用法 | virt-viewer --connect qemu:///system vm-web01 |
virt-manager 内置的虚拟机控制台就是调用它。只想看一台机器屏幕时,单独开一个 viewer 比打开整个管理界面轻。
10.2 virt-sysprep:镜像交付前的清理
把虚拟机转成模板前,用它清掉机器相关的身份信息,避免克隆出来的机器主机名、SSH 主机密钥、MAC 缓存全一样:
virt-sysprep-dvm-web01# 直接作用于一台已关机的虚拟机| 清理项 | 说明 |
|---|---|
| 主机名与 /etc/hosts | 还原成默认值 |
| SSH 主机密钥 | 删除,下次开机重新生成 |
| 网卡 MAC 与 udev 持久化规则 | 清除 |
| 日志与历史记录 | 清空 |
⚠️ 操作前必须关机,且不可逆,建议先做一份快照。
10.3 常见虚拟化产品对照
| 产品 | 定位 |
|---|---|
| KVM | Linux 内核虚拟化模块,开源,云厂商主流 |
| QEMU | 硬件模拟器,常与 KVM 配合做 I/O 虚拟化 |
| Xen | Hypervisor 在操作系统之下,Dom0 管理域 |
| VMware ESX / ESXi | 商业 Hypervisor,vSphere 体系核心 |
| Microsoft Hyper-V | Windows Server 内置虚拟化 |
| Virtuozzo | 容器级虚拟化,共享内核 |
| LXC | Linux 容器,操作系统级隔离 |
| FusionCompute | 华为服务器虚拟化产品 |
📚 参考资料(官方文档 · 中英双语)
英文原版
- virsh 命令行工具手册
- libvirt 迁移官方文档
- exports 文件格式手册(man7)
中文版
- 在 Linux ECS 上挂载 NFS 协议文件系统(阿里云)
其余手册在服务器上直接查(showmount、exportfs、mount.nfs等,无需联网):
man1virsh# 虚拟机管理命令man1virt-install# 创建虚拟机man1virt-clone# 克隆虚拟机man1virt-sysprep# 镜像清理man5exports# NFS 导出配置man8exportfs# 导出表维护man8showmount# 查看 NFS 导出表man8nfsd# NFS 服务端守护进程附:命令英文全称速查表
| 命令 / 缩写 | 英文全称 / 原意 | 作用 |
|---|---|---|
virsh | virtual shell | libvirt 命令行管理工具 |
virt-install | virtual install | 命令行创建虚拟机 |
virt-clone | virtual clone | 克隆虚拟机 |
virt-viewer | virtual viewer | 虚拟机图形控制台 |
virt-sysprep | virtual system preparation | 虚拟机镜像清理 |
migrate | migrate | 虚拟机迁移 |
NFS | Network File System | 网络文件系统 |
nfs-server | NFS server | NFS 服务端 |
showmount | show mount | 查看 NFS 导出表 |
exports | exports | NFS 导出配置文件 |
exportfs | export file system | 维护导出表 |
TCO | Total Cost of Ownership | 总体拥有成本 |
总结:热迁移的关键不在那条
virsh migrate命令,而在它前面的准备——磁盘必须落在 NFS / Ceph 这类共享存储上,目标端要有同规格空虚拟机,两台宿主机之间 SSH 免密与防火墙都要提前打通。这篇的三节点实验把这四件事按顺序做完了,命令可以直接照着敲。
📚同系列 · KVM 虚拟化实战
- 上一篇:《KVM 虚拟机管理:virt-manager 与 virsh 命令速查手册》
- 相关篇:《KVM 装完连不上:VNC、光驱、快照三类坑的排错实录》
- 相关篇:《虚拟化到底虚拟了什么:CPU、内存、IO 三层原理拆解》
⬅️ 上一篇:KVM 虚拟机管理与命令速查 ➡️ 下一篇:ESXi 嵌套部署与磁盘置备
💬 你迁移虚拟机时踩过什么坑?Permission denied还是磁盘路径不可访问?评论区贴出报错,一起看。