动静态迁移的原理
静态迁移是指在虚拟机关闭或暂停的情况下,将源宿主机上虚拟机的磁盘文件和配置文件拷贝到目标宿主机上。这种方式需要显式的停止虚拟机运行,对服务可用性要求高的需求不合适。
***
动态迁移无需拷贝虚拟机配置文件和磁盘文件,但是需要迁移的主机之间有相同的目录结构放置虚拟机磁盘文件,可以通过多种方式实现,本例采用基于共享存储动态迁移,通过NFS来实现。
实验环境
源宿主机:Ubuntu17.10 Server版操作系统,4.10.0-38-generic内核。下文中以“Node1”表示,主机名为Service,IP地址为192.168.200.132,NFS挂载目录/opt。
目标宿主机:Ubuntu17.10 Server版操作系统,4.10.0-38-generic内核。下文中以“Node2”表示,主机名为Service1,IP地址为192.168.200.131,NFS挂载目录/opt。
基于QEMU的动态迁移虚拟机镜像文件为ubuntu16server.img。
基于libvirt的静态迁移测试虚拟机:demo1,IP为192.168.200.132,虚拟镜像文件为ubuntu16.img。
基于libvirt的动态迁移测试虚拟机:demo2,IP为192.168.200.131,虚拟镜像文件为ubuntu16.img。
NFS服务器:Ubuntu17.10 Server版,4.10.0-38-generic内核。IP地址为192.168.200.130,服务目录为/opt/share。
配置步骤
NFS服务器的搭建
KVM虚拟机动态迁移无需拷贝虚拟机配置文件和磁盘文件,但是需要迁移的主机之间有相同的目录结构放置虚拟机磁盘文件(本例为“/opt/share”目录),这里的动态迁移是基于共享存储动态迁移,通过NFS来实现,需要QEMU 0.12.2以上版本支持。可以使用以下命令查看安装的QEMU的版本号。
qemu-img --help|grep version
首先在NFS服务器上,下载安装NFS,kernel-server相当于server端,common是client端,使用命令以下命令进行安装NFS:
sudo apt-get install nfs-kernel-server nfs-common -y
配置NFS服务器,将NFS服务器上的“/opt/share”目录设为服务目录。首先使用以下命令创建目录,然后修改创建的目录权限,同时如果不放心是否权限修改了,可以查看下文件夹的权限,修改后为“drwxr-xr-x”
sudo mkdir /opt/share,
sudo chmod 777 /opt/share
sudo ll /opt接下来使用vim修改“/etc/exports”:文件添加共享目录,在该文件最后添加以下内容即可。/opt/share:表示要设置的共享目录,_*_:表示允许所有的网段访问,也可以使用具体的IP。rw:表示挂载此目录的客户端对该共享目录具有读写权限。sync:表示资料同步写入内存和硬盘。no_root_squash:表示root用户具有对根目录的完全管理访问权限。no_subtree_check:表示不检查父目录的权限。修改完毕后保存退出。
/opt/share *(rw,sync,no_subtree_check,no_root_squash)
_“/etc/exports”_文件修改后,使用命令然后进行刷新。最后启动NFS服务,命令如下:
sudo exportfs –r
sudo /etc/init.d/rpcbind restart
sudo /etc/init.d/nfs-kernel-server restartNFS服务启动后,在Node1上使用以下命令查看远程主机的共享目录,可以看到以下斜体内容
sudo showmount -e 192.168.200.130
root@Service:~# sudo showmount -e 192.168.200.130
Export list for 192.168.200.130:
/opt/share *分别在Node1和Node2上分别挂载NFS服务器的共享文件夹到本地“/opt”目录中,然后将Node1上的的虚拟磁盘文件ubuntu16server.img(前面实验中制作的ubuntu的镜像文件)拷贝到挂载NFS服务器共享目录的文件夹“/opt”中(我制作好的镜像文件在/root文件夹中),之后可以看到ubuntu16server.img镜像文件。在节点2上执行同样的操作,但在Node2中不用执行拷贝这一命令。
sudo mount -t nfs 192.168.200.130:/opt/share /opt -o rw
sudo cp /root/ubuntu16server.img /opt挂载拷贝完成后两节点都有相同的虚拟机磁盘文件存储目录
基于QEMU的虚拟机动态迁移
在Node1上使用如下命令启动虚拟机,ubuntu16server.img为前面实验制作的镜像文件,-monitor stdio表示可以进入QEMU监控器,以便接下来执行迁移命令。
qemu-system-x86_64 -hda ubuntu16server.img -m 1024 -smp 1 -vnc :0 -monitor stdio
(qemu)- 打开VNC界面,连接节点1上的QEMU虚拟机,登陆之后并执行“top”命令,可以看到界面一直在发生改变。
在Node2上使用以下命令启动一个虚拟机,该虚拟机并没有真实启动,只是用于等待接收动态迁移过来的内存内容,使用VNC连接后显示界面状态为黑屏,或者是有一行提示。这里需要注意:在Node2上,NFS挂载目录必须与源主机上保持一致;启动客户机命令也需一致,但是需要增加-incoming 选项。“-incoming tcp:0:6666” 这个参数表示在6666 端口建立一个tcp socket 连接用于接收来自于源宿主机的动态迁移的内容,其中“0”表示允许来自任何主机的连接,“-incoming”表示使QEMU进程进入到迁移监听(migration-listen)模式,而不是真正以命令行中的镜像文件运行客户机。另外,这里是以NFS服务器上的镜像文件作为虚拟机磁盘来启动虚拟机,如果有多个用户同时使用NFS服务器上的镜像文件,请将镜像文件在本地进行派生,使用派生后的镜像文件启动虚拟机,具体方式读者可自行查阅相关资料
qemu-system-x86_64 -hda /opt/ubuntu16server.img -m 1024 -smp 1 -vnc :0 -incoming tcp:0:6666
在Node1源宿主机的qemu monitor 命令行中输入以下命令迁移虚拟机,进入动态迁移的流程,其中“192.168.200.131”是目标宿主机IP,TCP协议和6666端口与目标宿主机上命令行的-incoming 参数保持一致
(qemu) migrate tcp:192.168.200.131:6666 //(qemu)为启动镜像时出现的qemu monitor
- 在Node1上执行"migrate"命令从开始到执行完成,大约十秒钟(视网络而定),在执行完成后迁移成功。迁移后在Node2上,也就是目标宿主机上,之前处于迁移监听状态的虚拟机开始运行,在该虚拟机中可以查看到,原来在Node1上运行的虚拟机上执行的top命令在迁移后仍在继续在Node2上执行。
至此基于QEMU的虚拟机动态迁移完成
基于Libvirt的虚拟机静态迁移
- 静态迁移也叫做常规迁移、离线迁移(Offline Migration)。是在虚拟机关机或暂停的情况下,拷贝虚拟机磁盘文件与配置文件从源宿主机到目标宿主机中,实现的从一台物理机到另一台物理机的迁移。因为虚拟机的文件系统建立在虚拟机镜像文件上面,所以在虚拟机关机的情况下,只需要简单的迁移虚拟机镜像和相应的配置文件到另外一台物理主机上即可。如果需要保存虚拟机迁移之前的状态,那么应该在迁移之前将虚拟机暂停,然后拷贝状态至目标宿主机,最后在目标宿主机重建虚拟机状态,恢复执行。这种方式的迁移过程需要显式的停止虚拟机的运行。从用户角度看,有明确的一段停机时间,虚拟机上的服务不可用。这种迁移方式简单易行,适用于对服务可用性要求不严格的场合。
为网络添加br0网桥
sudo brctl addbr br0
sudo brctl addif br0 ens33
sudo brctl stp br0 on
sudo ifconfig ens33 0
sudo dhclient br0在Node1上进行操作,首先确定demo虚拟机状态为“shut off”,如果不是可以执行“destroy 进行关闭
sudo virsh list --all
准备迁移demo虚拟机,查看demo虚拟机的磁盘文件
virsh domblklist
导出虚拟机配置文件demo.xml、ubuntu16.img,将其发送到Node2中与Node1中文件对应的文件夹(demo.xml文件在文章结尾会给出代码)
sudo scp /root/demo.xml /root/ubuntu16.img 192.168.200.131:/root
- 之后开始在目标宿主机Node2上进行虚拟机的配置和启动。
使用virsh的子命令define定义并注册demo虚拟机
sudo virsh define /root/demo.xml
启动迁移后的demo虚拟机,通过VNC查看,VNC端口查看
sudo virsh vncdisplay
然后通过VNC查看是否可以登陆和其他操作
基于Libvirt的虚拟机动态迁移
- 此处我说明下,这种迁移虽然为基于Libvirt的动态迁移,但是其迁移的只是虚拟机的状态而不是虚拟机的状态和文件,这一点我再次特别说明。
将Node1上的“ubuntu16.img”发送到Node2对应的文件夹,此时的demo状态为“shut off”,发送后启动demo虚拟机
sudo scp /root/ubuntu16.img 192.168.200.131:/root
查看Node1上虚拟机状态,demo虚拟机保证处于“runing”(如果demo处于“shut off”,将其启动运行“sudo virsh start demo”)
sudo virsh list --all
- 查看Node2上虚拟机状态,确保无虚拟机运行,命令如上
- 通过VNC连接登陆,并执行"top"命令
在Node1上执行“virsh migrate”迁移命令,虚拟机demo在迁移出去的过程中,状态有从“running”到“shut off”的一个改变。--verbose 指迁移demo虚拟机,192.168.200.131为节点2的IP地址,使用tcp协议连接,--unsafe参数表示跳过安全检测, /system为以root身份进行状态迁移,qemu+ssh表示为通过ssh通道连接到远程节点的system实例,具有最大权限来管理远程节点上的虚拟机资源
sudo virsh migrate --live --verbose demo qemu+ssh://192.168.200.131/system tcp://192.168.200.131 --unsafe
在Node2上,查看虚拟机demo虚拟机状态为“runing”
sudo virsh list --all
- 在迁移过程中,可以通过另外一台客户机一直ping虚拟机demo,查看demo迁移过程中的可连接性。实际上迁移过程除了偶尔有几个包的中断,基本上没有太大影响
此时虽然demo虚拟机已经在Node2上启动了,但是Node2上还没有demo虚拟机的配置文件。这时需要创建配置文件并定义该虚拟机,可以通过迁移过来的虚拟机内存状态创建虚拟机配置文件,然后通过xml配置文件定义虚拟机。
sudo virsh dumpxml demo > /etc/libvirt/qemu/demo1.xml //之所以命名为demo1.xml是因为,在libvirt静态迁移过程中已经有个demo.xml文件,虽然不在同一个文件夹,为了避免搞混。
sudo virsh define /etc/libvirt/qemu/demo1.xml通过VNC连接查看,VNC端口号查询。
sudo virsh vncdisplay demo
- 在通过VNC连接上后发现迁移前的“top”命令依然在执行
至此,demo虚拟机迁移完成。
demo.xml文件中的内容
<!--
WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
OVERWRITTEN AND LOST. Changes to this xml configuration should be made using:
virsh edit demo
or other application using the libvirt API.
-->
<domain type=‘kvm‘>
<name>demo</name>
<uuid>782ca9b9-8403-4fcd-979e-e4f038aaeb15</uuid>
<memory unit=‘KiB‘>1048576</memory>
<currentMemory unit=‘KiB‘>1048576</currentMemory>
<vcpu placement=‘static‘>1</vcpu>
<os>
<type arch=‘x86_64‘ machine=‘pc-i440fx-trusty‘>hvm</type>
<boot dev=‘cdrom‘/>
<boot dev=‘hd‘/>
</os>
<features>
<acpi/>
<apic/>
<pae/>
</features>
<clock offset=‘localtime‘/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/kvm</emulator>
<disk type=‘file‘ device=‘disk‘>
<driver name=‘qemu‘ type=‘qcow2‘/>
<source file=‘/home/ubuntu16.img‘/>
<target dev=‘hda‘ bus=‘ide‘/>
<address type=‘drive‘ controller=‘0‘ bus=‘0‘ target=‘0‘ unit=‘0‘/>
</disk>
<controller type=‘usb‘ index=‘0‘>
<address type=‘pci‘ domain=‘0x0000‘ bus=‘0x00‘ slot=‘0x01‘ function=‘0x2‘/>
</controller>
<controller type=‘pci‘ index=‘0‘ model=‘pci-root‘/>
<controller type=‘ide‘ index=‘0‘>
<address type=‘pci‘ domain=‘0x0000‘ bus=‘0x00‘ slot=‘0x01‘ function=‘0x1‘/>
</controller>
<interface type=‘bridge‘>
<mac address=‘52:54:00:c0:a2:8a‘/>
<source bridge=‘br0‘/>
<model type=‘rtl8139‘/>
<address type=‘pci‘ domain=‘0x0000‘ bus=‘0x00‘ slot=‘0x03‘ function=‘0x0‘/>
</interface>
<input type=‘tablet‘ bus=‘usb‘/>
<input type=‘mouse‘ bus=‘ps2‘/>
<input type=‘keyboard‘ bus=‘ps2‘/>
<graphics type=‘vnc‘ port=‘-1‘ autoport=‘yes‘ listen=‘0.0.0.0‘ keymap=‘en-us‘/>
<video>
<model type=‘cirrus‘ vram=‘9216‘ heads=‘1‘/>
<address type=‘pci‘ domain=‘0x0000‘ bus=‘0x00‘ slot=‘0x02‘ function=‘0x0‘/>
</video>
<memballoon model=‘virtio‘>
<address type=‘pci‘ domain=‘0x0000‘ bus=‘0x00‘ slot=‘0x04‘ function=‘0x0‘/>
</memballoon>
</devices>
</domain>