KVM 虚拟机热迁移实战:virsh migrate、共享存储配置、迁移故障排查
详解 KVM 虚拟机热迁移(Live Migration)的实现原理和操作流程:基于共享存储(NFS/Ceph)的热迁移、无共享存储的块迁移、virsh migrate 命令参数详解、迁移前检查、常见错误处理,覆盖 Proxmox VE 和裸 KVM 两种场景。
热迁移(Live Migration)是虚拟化平台的核心能力:在不停机的情况下把运行中的虚拟机从一台物理机迁移到另一台。这是主机维护、负载均衡、故障转移的基础操作。本文讲解 KVM 热迁移的原理、实操和故障处理。
热迁移原理
迁移过程(Pre-copy 算法):
1. 预复制阶段(Pre-copy)
源主机持续把 VM 内存页传输到目标主机
同时 VM 继续运行,脏内存页被追踪并再次传输
2. 停机阶段(Stop-and-copy)
当剩余脏页少到可以快速传输时
短暂暂停 VM(通常 < 100ms)
传输最后的 CPU 状态、设备状态、剩余脏页
3. 恢复阶段(Resume)
在目标主机上恢复 VM 运行
更新网络(ARP 广播通知交换机)
源主机 VM 被停止
前提:
- 源/目标主机的 CPU 类型兼容(或使用 cpu=host-model 屏蔽差异)
- 磁盘对两台主机均可访问(共享存储),或使用块迁移
- 两台主机网络互通(迁移流量走专用网络)
一、环境准备
主机间 SSH 免密(libvirt 迁移需要)
# 在源主机上
ssh-keygen -t ed25519
ssh-copy-id root@dest-host
# 验证
ssh root@dest-host hostname
# libvirt 也需要免密(某些配置下)
ssh-copy-id -i /root/.ssh/id_ed25519.pub root@dest-host
确认 CPU 兼容性
# 查看源主机 CPU 型号
virsh capabilities | grep '<model'
virsh dominfo <vm-name> | grep "CPU model"
# 查看虚拟机的 CPU 配置
virsh dumpxml <vm-name> | grep -A 5 '<cpu'
# 推荐使用 host-model 或 host-passthrough(注意:passthrough 限制了可迁移的主机范围)
# 迁移兼容性最强的配置:
virsh edit <vm-name>
# 修改 cpu 部分:
# <cpu mode='host-model' check='partial'/>
共享存储准备(NFS 方案)
# NFS 服务器上
apt install nfs-kernel-server
mkdir -p /exports/vm-storage
chmod 777 /exports/vm-storage
cat >> /etc/exports << 'EOF'
/exports/vm-storage 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
EOF
exportfs -ra
systemctl restart nfs-kernel-server
# 两台 KVM 主机都挂载
apt install nfs-common
mkdir -p /var/lib/libvirt/images/shared
mount 192.168.1.100:/exports/vm-storage /var/lib/libvirt/images/shared
# 永久挂载(/etc/fstab)
echo "192.168.1.100:/exports/vm-storage /var/lib/libvirt/images/shared nfs defaults,_netdev 0 0" >> /etc/fstab
# 确认两台主机都能访问同一存储路径
ls /var/lib/libvirt/images/shared/
二、基于共享存储的热迁移
# 虚拟机磁盘必须在共享存储上
virsh domblklist <vm-name>
# Target Source
# vda /var/lib/libvirt/images/shared/myvm.qcow2 ← 在共享存储上
# 执行热迁移(最简单方式)
virsh migrate \
--live \ # 热迁移(不停机)
--verbose \ # 显示进度
<vm-name> \
qemu+ssh://dest-host/system # 目标主机 libvirt 连接
# 带更多参数的迁移(生产推荐)
virsh migrate \
--live \
--persistent \ # 在目标主机上持久化(写入 XML 配置)
--undefinesource \ # 迁移完成后删除源主机的 VM 定义
--compressed \ # 迁移数据压缩(减少网络用量)
--auto-converge \ # 自动降低 VM 速度以确保迁移能完成
--verbose \
<vm-name> \
qemu+ssh://dest-host/system
# 查看迁移进度(另一个终端)
virsh domjobinfo <vm-name>
# Job type: Unbounded
# Time elapsed: 5247 ms
# Data processed: 4.095 GiB
# Data remaining: 512.000 MiB
# Data total: 4.607 GiB
# Memory processed: 4.095 GiB
# Memory remaining: 512.000 MiB
# Memory bandwidth: 800.000 MiB/s
# 迁移完成后确认 VM 在目标主机运行
ssh dest-host 'virsh list'
三、块迁移(无共享存储)
当没有共享存储时,可以使用块迁移(同时迁移磁盘数据):
# 块迁移(迁移时间更长,因为需要复制整个磁盘)
virsh migrate \
--live \
--copy-storage-all \ # 复制所有存储(包括磁盘)
--persistent \
--undefinesource \
--verbose \
<vm-name> \
qemu+ssh://dest-host/system
# 指定目标存储路径(目标主机上的路径)
virsh migrate \
--live \
--copy-storage-all \
--persistent \
--verbose \
<vm-name> \
qemu+ssh://dest-host/system \
qemu+ssh://dest-host/system?socket=/tmp/migrate.sock
# 也可以只迁移没有共享的磁盘
virsh migrate \
--live \
--copy-storage-inc \ # 只迁移增量(差异),需要目标已有基础镜像
<vm-name> \
qemu+ssh://dest-host/system
四、迁移网络优化
# 指定迁移使用的网络接口(避免业务流量和迁移流量抢带宽)
# 目标主机开启 libvirt 迁移监听
virsh migrate-setmaxdowntime <vm-name> 500 # 最大停机时间 500ms
# 设置迁移带宽限制(防止迁移占满网络)
virsh migrate-setspeed <vm-name> 1024 # 1024 MiB/s
# 查看当前带宽设置
virsh migrate-getspeed <vm-name>
# 迁移到指定地址(使用迁移专用网络)
virsh migrate \
--live \
--verbose \
<vm-name> \
qemu+ssh://dest-host/system \
tcp://192.168.10.101:49152 # 迁移流量走 192.168.10.x 网段
# 在目标主机 libvirtd 配置中绑定迁移监听地址
# /etc/libvirt/libvirtd.conf
# migration_host = "192.168.10.101" # 目标主机迁移网络 IP
五、Proxmox VE 热迁移
Proxmox 提供了更友好的热迁移界面和命令:
# 通过 Web UI:VM → 右键 → Migrate
# 或命令行(pvesh / qm)
# 查看集群节点
pvecm nodes
# Nodeid Votes Quorum Name State
# 1 1 1 node-01 Online
# 2 1 1 node-02 Online
# 3 1 1 node-03 Online
# 在线迁移(有共享存储时)
qm migrate <vmid> <target-node> --online 1
# 示例
qm migrate 101 node-02 --online 1
# 强制迁移(本地存储,同时迁移磁盘)
qm migrate 101 node-02 --online 1 --with-local-disks 1
# 查看迁移任务
pvesh get /nodes/node-01/tasks | head -20
# Proxmox 迁移进度(实时)
tail -f /var/log/pve/tasks/$(ls -t /var/log/pve/tasks/ | head -1)
六、迁移前检查清单
#!/bin/bash
# 热迁移前检查脚本
VM=$1
DEST=$2
echo "=== 迁移前检查:$VM → $DEST ==="
# 1. 检查 VM 是否在运行
if ! virsh list | grep -q "$VM"; then
echo "❌ VM $VM 未运行"
exit 1
fi
echo "✅ VM 运行中"
# 2. 检查目标主机连通性
if ! ssh -o ConnectTimeout=5 root@$DEST 'exit'; then
echo "❌ 无法 SSH 到目标主机"
exit 1
fi
echo "✅ 目标主机 SSH 可达"
# 3. 检查目标主机 libvirt
if ! ssh root@$DEST 'systemctl is-active libvirtd'; then
echo "❌ 目标主机 libvirtd 未运行"
exit 1
fi
echo "✅ 目标主机 libvirtd 运行中"
# 4. 检查共享存储
DISK_PATH=$(virsh domblklist $VM | grep vda | awk '{print $2}')
if ! ssh root@$DEST "test -f $DISK_PATH"; then
echo "⚠️ 目标主机看不到磁盘文件(可能需要块迁移)"
fi
# 5. 检查目标主机可用内存
VM_MEM=$(virsh dominfo $VM | grep "Used memory" | awk '{print $3}')
DEST_FREE_MEM=$(ssh root@$DEST 'free -k | grep Mem | awk "{print \$7}"')
if [ "$DEST_FREE_MEM" -lt "$VM_MEM" ]; then
echo "❌ 目标主机内存不足(需要 ${VM_MEM}KB,可用 ${DEST_FREE_MEM}KB)"
exit 1
fi
echo "✅ 目标主机内存充足"
echo "=== 检查完成,可以迁移 ==="
七、常见错误处理
# 错误1:CPU 不兼容
# error: Unable to allow access for disk path XXX: no such file or directory
# error: internal error: CPU is incompatible with target CPU
# 解法:VM 使用 host-model CPU 模式,而不是 host-passthrough
virsh edit <vm-name>
# 修改:<cpu mode='host-model' check='partial'/>
# 错误2:磁盘路径目标不可访问
# error: Cannot access storage file 'xxx' (as uid:107, gid:107)
# 解法:确认两台主机共享存储挂载一致,且 qemu 用户有权限
ls -la /var/lib/libvirt/images/shared/
chown -R libvirt-qemu:kvm /var/lib/libvirt/images/shared/
# 错误3:迁移超时(脏页率太高,内存写入太快)
# error: Unable to complete migration due to excessive downtime
# 解法:启用 --auto-converge(降低 VM 速度)或暂停业务写入高峰
virsh migrate --live --auto-converge ...
# 错误4:目标主机 libvirt 版本不兼容
# error: this feature or command is not supported by the connection driver:
# QEMU migration cookie
# 解法:确保两台主机 libvirt 版本接近
virsh version
ssh dest-host 'virsh version'
apt list --installed | grep libvirt
小结
KVM 热迁移的核心是 virsh migrate --live,配合 --persistent 和 --undefinesource 实现完整的 VM 转移。有共享存储(NFS/Ceph)时:迁移速度快,只传内存;无共享存储时:用 --copy-storage-all 同时迁移磁盘,速度慢但不依赖共享存储。生产中的关键配置:CPU 使用 host-model(兼容性最好)、设置迁移专用网络(tcp://)、限制迁移带宽(migrate-setspeed)、开启 --auto-converge(写密集 VM 的救星)。
