其中,KVM全称是基于内核的虚拟机(Kernel-basedVirtualMachine),它是一个Linux的一个内核模块,该内核模块使得Linux变成了一个Hypervisor:
本文介绍的是基于X86CPU的KVM。
KVM是实现拦截虚机的I/O请求的原理:
QEMU-KVM:
KVM:
KVM所支持的功能包括:
RedHatLinuxKVM有如下两种安装方式:
选择安装类型为VirtualizaitonHost:
可以选择具体的KVM客户端、平台和工具:
这种安装方式要求该系统已经被注册,否则会报错:
[root@rh65~]#yuminstallqemu-kvmqemu-imgLoadedplugins:product-id,refresh-packagekit,security,subscription-managerThissystemisnotregisteredtoRedHatSubscriptionManagement.Youcanusesubscription-managertoregister.SettingupInstallProcessNothingtodo你至少需要安装qemu-kvmqemu-img这两个包。
#yuminstallqemu-kvmqemu-img你还可以安装其它工具包:
#yuminstallvirt-managerlibvirtlibvirt-pythonpython-virtinstlibvirt-client4.3QEMU/KVM代码下载编译安装4.3.1QEMU/KVM的代码结构QEMU/KVM的代码包括几个部分:
(1)KVM内核模块是Linux内核的一部分。通常Linux比较新的发行版(2.6.20+)都包含了KVM内核,也可以从得到。比如在我的RedHat6.5上:
[root@rh65isoimages]#uname-r2.6.32-431.el6.x86_64[root@rh65isoimages]#modprobe-l|grepkvmkernel/arch/x86/kvm/kvm.kokernel/arch/x86/kvm/kvm-intel.kokernel/arch/x86/kvm/kvm-amd.ko(2)用户空间的工具即qemu-kvm。qemu-kvm是KVM项目从QEMU新拉出的一个分支()。在QEMU1.3版本之前,QEMU和QEMU-KVM是有区别的,但是从2012年底GA的QEMU1.3版本开始,两者就完全一样了。
(3)LinuxGuestOSvirtio驱动,也是较新的Linux内核的一部分了。
(4)WindowsGuestOSvirtio驱动,可以从下载。
RedHat6.5上自带的QEMU太老,0.12.0版本,最新版本都到了2.*了。
(1).参考,将RedHat6.5的ISO文件当作本地源
mount-oloopsoft/rhel-server-6.4-x86_64-dvd.iso/mnt/rhel6/vim/etc/fstab=>/root/isoimages/soft/RHEL6.5-20131111.0-Server-x86_64-DVD1.iso/mnt/rhel6iso9660ro,loop[root@rh65qemu-2.3.0]#cat/etc/yum.repos.d/local.repo[local]name=localbaseurl=file:///mnt/rhel6/enabled=1gpgcjeck=0
(2).安装依赖包包
yuminstallgccyuminstallautoconfyuminstallautoconfautomakelibtoolyuminstall-yglib*yuminstallzlib*(3).从下载代码,上传到我的编译环境RedHat6.5.
tar-jzvfqemu-2.3.0.tar.bz2cdqemu-2.3.0./configuremake-j4makeinstall(4).安装完成
ln-s/usr/bin/qemu-system-x86_64/usr/bin/qemu-kvm4.3.3安装libvirt可以从下载安装包。最新的版本是0.10.2.
使用VMMGUI创建的虚机的xml定义文件在/etc/libvirt/qemu/目录中。
(1)创建一个空的qcow2格式的镜像文件
qemu-imgcreate-fqcow2windows-master.qcow210G(2)启动一个虚机,将系统安装盘挂到cdrom,安装操作系统
qemu-kvm-hdawindows-master.qcow2-m512-bootd-cdrom/home/user/isos/en_winxp_pro_with_sp2.iso(3)现在你就拥有了一个带操作系统的镜像文件。你可以以它为模板创建新的镜像文件。使用模板的好处是,它会被设置为只读所以可以免于破坏。
qemu-imgcreate-bwindows-master.qcow2-fqcow2windows-clone.qcow2(4)你可以在新的镜像文件上启动虚机了
KVM是基于CPU辅助的全虚拟化方案,它需要CPU虚拟化特性的支持。
这个命令查看主机上的CPU物理情况:
[root@rh65s1]#egrep"(vmx|svm)"/proc/cpuinfoflags:fpuvmedepsetscmsrpaemcecx8apicsepmtrrpgemcacmovpatpse36clflushdtsacpimmxfxsrssesse2sshttmpbesyscallnxpdpe1gbrdtscplmconstant_tscarch_perfmonpebsbtsrep_goodxtopologynonstop_tscaperfmperfpnipclmulqdqdtes64monitords_cplvmxsmxesttm2ssse3cx16xtprpdcmpciddcasse4_1sse4_2popcntaeslahf_lmaratepbdtstpr_shadowvnmiflexpriorityeptvpid2.2多CPU服务器架构:SMP,NMP,NUMA从系统架构来看,目前的商用服务器大体可以分为三类:
查看你的服务器的CPU架构:
可见:
(1)qemu-kvm通过对/dev/kvm的一系列ICOTL命令控制虚机,比如
(3)KVM虚机包括虚拟内存、虚拟CPU和虚机I/O设备,其中,内存和CPU的虚拟化由KVM内核模块负责实现,I/O设备的虚拟化由QEMU负责实现。
(3)KVM户机系统的内存是qumu-kvm进程的地址空间的一部分。
(4)KVM虚机的vCPU作为线程运行在qemu-kvm进程的上下文中。
vCPU、QEMU进程、LInux进程调度和物理CPU之间的逻辑关系:
根据上面的1.3章节,支持虚拟化的CPU中都增加了新的功能。以IntelVT技术为例,它增加了两种运行模式:VMXroot模式和VMXnonroot模式。通常来讲,主机操作系统和VMM运行在VMXroot模式中,客户机操作系统及其应用运行在VMXnonroot模式中。因为两个模式都支持所有的ring,因此,客户机可以运行在它所需要的ring中(OS运行在ring0中,应用运行在ring3中),VMM也运行在其需要的ring中(对KVM来说,QEMU运行在ring3,KVM运行在ring0)。CPU在两种模式之间的切换称为VMX切换。从rootmode进入nonrootmode,称为VMentry;从nonrootmode进入rootmode,称为VMexit。可见,CPU受控制地在两种模式之间切换,轮流执行VMM代码和GuestOS代码。
对KVM虚机来说,运行在VMXRootMode下的VMM在需要执行GuestOS指令时执行VMLAUNCH指令将CPU转换到VMXnon-rootmode,开始执行客户机代码,即VMentry过程;在GuestOS需要退出该mode时,CPU自动切换到VMXRootmode,即VMexit过程。可见,KVM客户机代码是受VMM控制直接运行在物理CPU上的。QEMU只是通过KVM控制虚机的代码被CPU执行,但是它们本身并不执行其代码。也就是说,CPU并没有真正的被虚级化成虚拟的CPU给客户机使用。
是关于vSphere中CPU虚拟化的,我觉得它和KVMCPU虚拟化存在很大的一致。下图是使用2socket2core共4个vCPU的情形:
几个概念:socket(颗,CPU的物理单位),core(核,每个CPU中的物理内核),thread(超线程,通常来说,一个CPUcore只提供一个thread,这时客户机就只看到一个CPU;但是,超线程技术实现了CPU核的虚拟化,一个核被虚拟化出多个逻辑CPU,可以同时运行多个线程)。
上图分三层,他们分别是是VM层,VMKernel层和物理层。对于物理服务器而言,所有的CPU资源都分配给单独的操作系统和上面运行的应用。应用将请求先发送给操作系统,然后操作系统调度物理的CPU资源。在虚拟化平台比如KVM中,在VM层和物理层之间加入了VMkernel层,从而允许所有的VM共享物理层的资源。VM上的应用将请求发送给VM上的操作系统,然后操纵系统调度VirtualCPU资源(操作系统认为VirtualCPU和物理CPU是一样的),然后VMkernel层对多个物理CPUCore进行资源调度,从而满足VirtualCPU的需要。在虚拟化平台中OSCPUScheduler和HyperviisorCPUScheduler都在各自的领域内进行资源调度。
KVM中,可以指定socket,core和thread的数目,比如设置“-smp5,sockets=5,cores=1,threads=1”,则vCPU的数目为5*1*1=5。客户机看到的是基于KVMvCPU的CPU核,而vCPU作为QEMU线程被Linux作为普通的线程/轻量级进程调度到物理的CPU核上。至于你是该使用多socket和多core,有仔细的分析,其结论是在VMwareESXi上,性能没什么区别,只是某些客户机操作系统会限制物理CPU的数目,这种情况下,可以使用少socket多core。
一个普通的Linux内核有两种执行模式:内核模式(Kenerl)和用户模式(User)。为了支持带有虚拟化功能的CPU,KVM向Linux内核增加了第三种模式即客户机模式(Guest),该模式对应于CPU的VMXnon-rootmode。
KVM内核模块作为Usermode和Guestmode之间的桥梁:
三种模式的分工为:
QEMU-KVM相比原生QEMU的改动:
主机Linux将一个虚拟视作一个QEMU进程,该进程包括下面几种线程:
在我的测试环境中(RedHataLinux作Hypervisor):
1个主线程(I/O线程)、4个vCPU线程、3个其它线程
要将客户机内的线程调度到某个物理CPU,需要经历两个过程:
KVM使用标准的Linux进程调度方法来调度vCPU进程。Linux系统中,线程和进程的区别是进程有独立的内核空间,线程是代码的执行单位,也就是调度的基本单位。Linux中,线程是就是轻量级的进程,也就是共享了部分资源(地址空间、文件句柄、信号量等等)的进程,所以线程也按照进程的调度方式来进行调度。
根据Linux进程调度策略,可以看出,在Linux主机上运行的KVM客户机的总vCPU数目最好是不要超过物理CPU内核数,否则,会出现线程间的CPU内核资源竞争,导致有虚机因为vCPU进程等待而导致速度很慢。
关于这两次调度,业界有很多的研究,比如上海交大的论文提出动态地减少vCPU的数目即减少第二次调度。
另外,谈到的是vSphereCPU的调度方式,有空的时候可以研究下并和KVMvCPU的调度方式进行比较。
KVM支持SMP和NUMA多CPU架构的主机和客户机。对SMP类型的客户机,使用“-smp”参数:
-smp[,cores=][,threads=][,sockets=][,maxcpus=]对NUMA类型的客户机,使用“-numa”参数:
RedHatLinux6上使用默认的cpu64-rhe16作为客户机CPUmodel:
你可以指定特定的CPUmodel和feature:
你也可以直接使用-cpuhost,这样的话会客户机使用和主机相同的CPUmodel。
我们来假设一个主机有2个socket,每个socket有4个core。主频2.4GMHZ那么一共可用的资源是2*4*2.4G=19.2GMHZ。假设主机上运行了三个VM,VM1和VM2设置为1socket*1core,VM3设置为1socket*2core。那么VM1和VM2分别有1个vCPU,而VM3有2个vCPU。假设其他设置为缺省设置。
那么三个VM获得该主机CPU资源分配如下:VM1:25%;VM2:25%;VM3:50%
假设运行在VM3上的应用支持多线程,那么该应用可以充分利用到所非配的CPU资源。2vCPU的设置是合适的。假设运行在VM3上的应用不支持多线程,该应用根本无法同时使用利用2个vCPU.与此同时,VMkernal层的CPUScheduler必须等待物理层中两个空闲的pCPU,才开始资源调配来满足2个vCPU的需要。在仅有2vCPU的情况下,对该VM的性能不会有太大负面影响。但如果分配4vCPU或者更多,这种资源调度上的负担有可能会对该VM上运行的应用有很大负面影响。
确定vCPU数目的步骤。假如我们要创建一个VM,以下几步可以帮助确定合适的vCPU数目
1了解应用并设置初始值
该应用是否是关键应用,是否有ServiceLevelAgreement。一定要对运行在虚拟机上的应用是否支持多线程深入了解。咨询应用的提供商是否支持多线程和SMP(Symmetricmulti-processing)。参考该应用在物理服务器上运行时所需要的CPU个数。如果没有参照信息,可设置1vCPU作为初始值,然后密切观测资源使用情况。
2观测资源使用情况
假如分配有4个vCPU,如果在该VM上的应用的CPU
3更改vCPU数目并观测结果
每次的改动尽量少,如果可能需要4vCPU,先设置2vCPU在观测性能是否可以接受。
EPT和NPT采用类似的原理,都是作为CPU中新的一层,用来将客户机的物理地址翻译为主机的物理地址。关于EPT,Intel官方文档中的技术如下(实在看不懂...)
EPT的好处是,它的两阶段记忆体转换,特点就是将GuestPhysicalAddress→SystemPhysicalAddress,VMM不用再保留一份SPT(ShadowPageTable),以及以往还得经过SPT这个转换过程。除了降低各部虚拟机器在切换时所造成的效能损耗外,硬体指令集也比虚拟化软体处理来得可靠与稳定。
KSM在Linux2.6.32版本中被加入到内核中。
其好处是,在运行类似的客户机操作系统时,通过KSM,可以节约大量的内存,从而可以实现更多的内存超分,运行更多的虚机。
(1)初始状态:
(2)合并后:
(3)Guest1写内存后:
这是KVM虚拟机的又一个优化技术.。Intel的x86CPU通常使用4Kb内存页,当是经过配置,也能够使用巨页(hugepage):(4MBonx86_32,2MBonx86_64andx86_32PAE)
使用巨页,KVM的虚拟机的页表将使用更少的内存,并且将提高CPU的效率。最高情况下,可以提高20%的效率!
使用方法,需要三部:
mkdir/dev/hugepagesmount-thugetlbfshugetlbfs/dev/hugepages#保留一些内存给巨页sysctlvm.nr_hugepages=2048(使用x86_64系统时,这相当于从物理内存中保留了2048x2M=4GB的空间来给虚拟机使用)#给kvm传递参数hugepagesqemu-kvm-qemu-kvm-mem-path/dev/hugepages也可以在配置文件里加入:
验证方式,当虚拟机正常启动以后,在物理机里查看:
cat/proc/meminfo|grep-ihugepages老外的,他使用的是libvirt方式,先让libvirtd进程使用hugepages空间,然后再分配给虚拟机。
参考资料:
虚拟化技术性能比较和分析,周斌,张莹
Qemu纯软件的方式来模拟I/O设备,其中包括经常使用的网卡设备。GuestOS启动命令中没有传入的网络配置时,QEMU默认分配rtl8139类型的虚拟网卡类型,使用的是默认用户配置模式,这时候由于没有具体的网络模式的配置,Guest的网络功能是有限的。全虚拟化情况下,KVM虚机可以选择的网络模式包括:
分别使用的qemu-kvm参数为:
网桥模式是目前比较简单,也是用的比较多的模式,下图是网桥模式下的VM的收发包的流程。
如图中所示,红色箭头表示数据报文的入方向,步骤:
注意:RedHatLinuxKVM不支持SCSI模拟。
(2)virtio-net的流程:
使用virtio类型的设备比较简单。较新的Linux版本上都已经安装好了virtio驱动,而Windows的驱动需要自己下载安装。
(1)检查主机上是否支持virtio类型的网卡设备
[root@rh65isoimages]#kvm-netnic,model=qemu:SupportedNICmodels:ne2k_pci,i82551,i82557b,i82559er,rtl8139,e1000,pcnet,virtio(2)指定网卡设备model为virtio,启动虚机
(4)查看pci设备
其它virtio类型的设备的使用方式类似virtio-net。
前面提到virtio在宿主机中的后端处理程序(backend)一般是由用户空间的QEMU提供的,然而如果对于网络I/O请求的后端处理能够在在内核空间来完成,则效率会更高,会提高网络吞吐量和减少网络延迟。在比较新的内核中有一个叫做“vhost-net”的驱动模块,它是作为一个内核级别的后端处理程序,将virtio-net的后端处理任务放到内核空间中执行,减少内核空间到用户空间的切换,从而提高效率。
根据KVM官网的,vhost-net能提供更低的延迟(latency)(比e1000虚拟网卡低10%),和更高的吞吐量(throughput)(8倍于普通virtio,大概7~8Gigabits/sec)。
vhost-net与virtio-net的比较:
vhost-net的要求:
vhost-net的使用实例:
(1)确保主机上vhost-net内核模块被加载了
(2)启动一个虚拟机,在客户机中使用-net定义一个virtio-net网卡,在主机端使用-netdev启动vhost
(3)在虚拟机端,看到virtio网卡使用的TAP设备为tap0。
(4)在宿主机中看vhost-net被加载和使用了,以及Linux桥br0,它连接物理网卡eth1和客户机使用的TAP设备tap0
一般来说,使用vhost-net作为后端处理驱动可以提高网络的性能。不过,对于一些网络负载类型使用vhost-net作为后端,却可能使其性能不升反降。特别是从宿主机到其中的客户机之间的UDP流量,如果客户机处理接受数据的速度比宿主机发送的速度要慢,这时就容易出现性能下降。在这种情况下,使用vhost-net将会是UDPsocket的接受缓冲区更快地溢出,从而导致更多的数据包丢失。故这种情况下,不使用vhost-net,让传输速度稍微慢一点,反而会提高整体的性能。
使用qemu-kvm命令行,加上“vhost=off”(或没有vhost选项)就会不使用vhost-net,而在使用libvirt时,需要对客户机的配置的XML文件中的网络配置部分进行如下的配置,指定后端驱动的名称为“qemu”(而不是“vhost”)。
…
另一个比较特殊的virtio设备是virtio-balloon。通常来说,要改变客户机所占用的宿主机内存,要先关闭客户机,修改启动时的内存配置,然后重启客户机才可以实现。而内存的ballooning(气球)技术可以在客户机运行时动态地调整它所占用的宿主机内存资源,而不需要关闭客户机。该技术能够:
优势和不足:
在QEMUmonitor中,提供了两个命令查看和设置客户机内存的大小。
(1)启动一个虚机,内存为2048M,启用virtio-balloon
(2)通过vncviewer进入虚机,查看pci设备
(3)看看内存情况,共2G内存
(4)进入QEMUMonitor,调整balloon内存为500M
(5)回到虚机,查看内存,变为500M
本文将分析PCI/PCIe设备直接分配(Pass-through)和SR-IOV,以及三种I/O虚拟化方式的比较。
设备直接分配(Deviceassignment)也称为DevicePass-Through。
先简单看看PCI和PCI-E的区别(AMDCPU):
(简单点看,PCI卡的性能没有PCI-E高,因为PCI-E是直接连在IOMMU上,而PCI卡是连在一个IOHub上。)
主要的PCI设备类型:
硬盘直接分配:
准备工作:
(1)在BIOS中打开IntelVT-d
(2)在Linux内核中启用PCIPass-through
添加intel_iommu=on到/boot/grub/grub.conf文件中。(在我的RedHatLinux6上,该文件是/boot/grub.conf)
(3)重启系统,使得配置生效
实际分配:
(1)使用lspci-nn命令找到待分配的PCI设备。这里以一个FC卡为例:
使用lspci命令得到的PCI数字的含义,以后使用libvirtAPI分配设备时会用到:
(2)使用virshnodedev-list命令找到该设备的PCI编号
(3)将设备从主机上解除
(4)使用virt-manager将设备直接分配给一个启动了的虚拟机
(5)添加好了后的效果
(6)在虚机中查看该PCI设备
(7)不再使用的话,需要在virt-manager中首先将该设备移除,然后在主机上重新挂载该设备
除了步骤(4),其他步骤同上面。
光纤卡SR-IOV的例子:
简单来说,SR-IOV分配步骤和设备直接分配相比基本类似,除了要使PF虚拟化成多个VF以外。
纯模拟网卡和物理网卡的比较:
(测试环境:两台物理服务器HostA和HostB,都使用GB以太网。HostA使用82566DC网卡,HostB使用82567LM-2网卡,一台虚机运行在HostB上,使用KVM-76.)
结论:
Virtio和vhost_net的吞吐量比较:
RedHatLinux6上virtio,vhost_net,SR-IOV和物理设备网络延迟的比较:
RedHatLinux6上virtio和vhost_net所消耗的主机CPU资源的比较:
使用virtio的KVM与物理机的TCP吞吐量对比:
物理机与使用SR-IOV的KVM的网络性能对比:
物理机与使用Pass-through的KVM的TCP性能对比:
KVM依赖的Intel/AMD处理器的各种虚拟化扩展:
I/O虚拟化方案的选择:
其它参考资料:
为什么需要Libvirt?
Libvirt提供了什么?
目前,libvirt已经成为使用最为广泛的对各种虚拟机进行管理的工具和应用程序接口(API),而且一些常用的虚拟机管理工具(如virsh、virt-install、virt-manager等)和云计算框架平台(如OpenStack、OpenNebula、Eucalyptus等)都在底层使用libvirt的应用程序接口。
LibvirtAPI就是对各种对象的各种操作,包括基本的增、删、改、查操作和其它操作。
Libvirt使用XML来定义各种对象,其中,与OpenStackNova关系比较密切的有:
2.domain:定义DHCPserver的DNSdomain。
3.forward:定义虚拟网络直接连到物理LAN的方式.”mode“指转发模式。
(1)mode=‘nat’:所有连接到该虚拟网络的虚拟的网络都会经过物理机器的网卡,并转换成物理网卡的地址。
libvirtAPI的实现是在各个Hypervisordriver和Storagedirver内。Hypervisor驱动包括:
有三种方式来安装libvirt:
(1)下载libvirt的源代码,然后编译和安装
(2)从各Linux的发行版中直接安装,比如Ubuntu上运行apt-getinstalllibvirt-bin
(3)从git上克隆libvirt的代码,然后编译和安装
描述了livbirtlog。设置所有日志的方法是在/etc/libvirt/libvirtd.conf中添加下面的配置然后重启libvirt:
在NovaCompute节点上运行的nova-compute服务调用HypervisorAPI去管理运行在该Hypervisor的虚机。Nova使用libvirt管理QEMU/KVM虚机,还使用别的API去管理别的虚机。
libvirt的实现代码在/nova/virt/libvirt/driver.py文件中。
这里是。
请注意Juno版本Nova对libvirt和QEMU的各种最低版本要求:
支持devicecallback
不支持image设置hw_disk_discard属性,具体参考NUMAtopology1.0.4无法获取node的NUMAtopology信息,就无法将虚机的vCPU指定到特定的nodeCPU上,会影响虚机的性能
Nova使用libvirt来管理虚机,包括:
(注意:image的元数据属性的优先级高于nova.conf中的配置。只有在没有property的情况下才使用nova.conf中的配置)
创建虚机的过程的几个主要阶段:
(1)消息由nova-api路由到某个novacompute节点(API->Scheduler->Compute(manager)->LibvirtDriver)
(2)调用NeutronRESTAPI去准备网络。其返回的数据类似:
[VIF({'profile':{},'ovs_interfaceid':u'59cfa0b8-2f5c-481a-89a8-7a8711b368a2','network':Network({'bridge':'br-int','subnets':[Subnet({'ips':[FixedIP({'meta':{},'version':4,'type':'fixed','floating_ips':[],'address':u'10.0.10.14'})],'version':4,'meta':{'dhcp_server':u'10.0.10.11'},'dns':[],'routes':[],'cidr':u'10.0.10.0/24','gateway':IP({'meta':{},'version':4,'type':'gateway','address':u'10.0.10.1'})})],'meta':{'injected':False,'tenant_id':u'74c8ada23a3449f888d9e19b76d13aab'},'id':u'a924e87a-826b-4109-bb03-523a8b3f6f9e','label':u'demo-net2'}),'devname':u'tap59cfa0b8-2f','vnic_type':u'normal','qbh_params':None,'meta':{},'details':{u'port_filter':True,u'ovs_hybrid_plug':True},'address':u'fa:16:3e:e0:30:e7','active':False,'type':u'ovs','id':u'59cfa0b8-2f5c-481a-89a8-7a8711b368a2','qbg_params':None})](3)从image启动话,nova会调用GlaneRESTAPI后者imagemetadata和准备本地启动盘
imagemetadata:
{u'status':u'active',u'deleted':False,u'container_format':u'bare',u'min_ram':0,u'updated_at':u'2015-04-26T04:34:40.000000',u'min_disk':0,u'owner':u'74c8ada23a3449f888d9e19b76d13aab',u'is_public':False,u'deleted_at':None,u'properties':{},u'size':13167616,u'name':u'image',u'checksum':u'64d7c1cd2b6f60c92c14662941cb7913',u'created_at':u'2015-04-26T04:34:39.000000',u'disk_format':u'qcow2',u'id':u'bb9318db-5554-4857-a309-268c6653b9ff'}本地启动盘:
{'disk_bus':'virtio','cdrom_bus':'ide','mapping':{'disk':{'bus':'virtio','boot_index':'1','type':'disk','dev':u'vda'},'root':{'bus':'virtio','boot_index':'1','type':'disk','dev':u'vda'},'disk.local':{'bus':'virtio','type':'disk','dev':'vdb'},'disk.swap':{'bus':'virtio','type':'disk','dev':'vdc'}}}本地启动盘的文件信息:
一个从image启动的Domain的配置XML实例(蓝色部分是注释说明):
(1)使用volumeid通过volumedriver找到指定的volume
(2)调用volumedriver来建立主机和Volume之间的连接
主机信息为:
{'ip':'192.168.1.15','host':'compute2','initiator':'iqn.1993-08.org.debian:01:a9f2b45c24f9'}建立的iSCSI连接信息为:
{u'driver_volume_type':u'iscsi',u'data':{u'access_mode':u'rw',u'target_discovered':False,u'encrypted':False,u'qos_specs':None,u'target_iqn':u'iqn.2010-10.org.openstack:volume-51da0d1f-0a17-4e7f-aeff-27438963348a',u'target_portal':u'10.0.2.41:3260',u'volume_id':u'51da0d1f-0a17-4e7f-aeff-27438963348a',u'target_lun':1,u'auth_password':u'hXG64qrzEjNt8MDKnERA',u'auth_username':u'fKSAe6vhgyeG88U9kcBV',u'auth_method':u'CHAP'}}volume在主机上的磁盘为:
root@compute2:/home/s1#ls/dev/disk/by-path/-lstotal00lrwxrwxrwx1rootroot9Jun1012:18ip-10.0.2.41:3260-iscsi-iqn.2010-10.org.openstack:volume-51da0d1f-0a17-4e7f-aeff-27438963348a-lun-1->../../sdc
Disk/dev/sdc:1073MB,1073741824bytes34heads,61sectors/track,1011cylinders,total2097152sectorsUnits=sectorsof1*512=512bytesSectorsize(logical/physical):512bytes/512bytesI/Osize(minimum/optimal):512bytes/512bytesDiskidentifier:0x00000000
Disk/dev/sdcdoesn'tcontainavalidpartitiontable
(3)通过domainname来找到指定domain对象(通过调用lookupByNameAPI)
(4)生成volume连接的配置xml,比如:
51da0d1f-0a17-4e7f-aeff-27438963348a
(5)调用attachDeviceFlagsAPI将volume挂载到该虚机
(1)运行novainterface-attach,传入network-id,Neutron会分配如下networkinfo给Nova
VIF({'profile':{},'ovs_interfaceid':u'0142efee-7382-43ef-96e8-d0084ecc893c','network':Network({'bridge':u'br-int','subnets':[Subnet({'ips':[FixedIP({'meta':{},'version':4,'type':u'fixed','floating_ips':[],'address':u'10.0.0.40'})],'version':4,'meta':{u'dhcp_server':u'10.0.0.3'},'dns':[],'routes':[],'cidr':u'10.0.0.0/24','gateway':IP({'meta':{},'version':4,'type':u'gateway','address':u'10.0.0.1'})})],'meta':{u'injected':False,u'tenant_id':u'74c8ada23a3449f888d9e19b76d13aab'},'id':u'01630966-b21f-4a6d-95ff-10c4575f1fe2','label':u'demo-net'}),'devname':u'tap0142efee-73','vnic_type':u'normal','qbh_params':None,'meta':{},'details':{u'port_filter':True,u'ovs_hybrid_plug':True},'address':u'fa:16:3e:14:32:d9','active':True,'type':u'ovs','id':u'0142efee-7382-43ef-96e8-d0084ecc893c','qbg_params':None})(2)执行下面的命令,将Neutron分配的port连接到OVS
(4)调用attachDeviceFlagsAPI来挂载该interface到虚机
QEMU/KVM快照的定义:
关于崩溃一致(crash-consistent)的附加说明:
快照还可以分为livesnapshot(热快照)和Clodsnapshot:
libvit做snapshot的各个API:
virDomainSave
virDomainSaveFlags
virDomainManagedSave
virDomainRestore
virDomainRestoreFlags
virDomainCreate
virDomainCreateWithFlags
virshsave/restore系统检查点virDomainSnapshotCreateXMLvirDomainRevertToSnapshotvirshsnapshot-create/snapshot-revert分别来看看这些API是如何工作的:
1.virDomainSnapshotCreateXML(virDomainPtrdomain,constchar*xmlDesc,unsignedintflags)
作用:根据xmlDesc指定的snapshotxml和flags来创建虚机的快照。
其内部实现根据虚机的运行状态有两种情形:
有其实现代码,可见其基本的实现步骤:
这几个API功能都比较类似:
对运行中的domaind-2运行“virshsave”命令。命令执行完成后,d-2变成“shutoff”状态。
看看domain的磁盘镜像文件和snapshot文件:
内存数据被保存到raw格式的文件中。
要恢复的时候,可以运行“vishrestored-2.snap1”命令从保存的文件上恢复。
先看看它的用法:
每个磁盘的镜像文件都包含了snapshot的信息:
virsh#snapshot-revertinstance-0000002e1433950148根据,libvirt将内存状态保存到某一个磁盘镜像文件内(”stateissavedinsideoneofthedisks(asinqemu's'savevm'systemcheckpointimplementation).Ifneededinthefuture,wecanalsoaddanattributepointingout_which_disksavedtheinternalstate;maybedisk='vda'.)
(2)可以使用“--memspec”和“--diskspec”参数来给内存和磁盘外部快照。这时候,在获取内存状态之前需要Pause虚机,就会产生服务的downtime。
[root@rh65osdomains]#virshsnapshot-revertd-21434467974error:unsupportedconfiguration:reverttoexternaldisksnapshotnotsupportedyet
(4)还可以使用“--live”参数创建系统还原点,包括磁盘、内存和设备状态等。使用这个参数时,虚机不会被Paused(那怎么实现的?)。其后果是增加了内存dump文件的大小,但是减少了系统的downtime。该参数只能用于做外部的系统还原点(externalcheckpoint)。
综上所述,对于snapshot-create-as命令来说,
[root@rh65~]#virshsnapshot-revertd-21434478313error:revertrequiresforce:Targetdeviceaddresstypenonedoesnotmatchsourcepci[root@rh65~]#virshsnapshot-revertd-21434478313--force[root@rh65~]#1.3外部快照的删除目前libvirt还不支持直接删除一个外部快照,可以参考介绍的workaround。
(1)对从镜像文件启动的虚机做快照
(2)对从卷启动的虚机做快照
严格地说,Nova虚机的快照,并不是对虚机做完整的快照,而是对虚机的启动盘(rootdisk,即vda或者hda)做快照生成qcow2格式的文件,并将其传到Glance中,其作用也往往是方便使用快照生成的镜像来部署新的虚机。Nova快照分为LiveSnapshot(不停机快照)和CloldSnapshot(停机快照)。
满足2.1.1中所述条件时,运行命令”novaimage-create“后,Nova会执行LiveSnapshot。其过程如下:
该API从backing文件中拷贝数据,或者拷贝整个backing文件到@base文件。Nova中的调用方式为:domain.blockRebase(disk_path,disk_delta,0,libvirt.VIR_DOMAIN_BLOCK_REBASE_COPY|libvirt.VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT|libvirt.VIR_DOMAIN_BLOCK_REBASE_SHALLOW)默认的话,该API会拷贝整个@disk文件到@base文件,但是使用的话就只拷贝差异数据(topdata)因为@disk和@base使用相同的backing文件。表示需要使用已经存在的@base文件因为Nova会预先创建好这个文件。简单的示意图:
有个过程的PoC代码描述该过程。
有该过程的完整libvirt日志分析。
当虚机不在运行中时或者不满足livesnapshot的条件的情况下,Nova会执行Coldsnapshot。其主要过程如下:
(1)当虚机处于running或者paused状态时:
(2)调用qemu-imgconvert命令将rootdisk的镜像文件转化一个相同格式的镜像文件。
(3)调用virDomainCreateWithFlagsAPI将虚机变为初始状态
(4)将在步骤1中卸载的PCI和SR-IOV设备重新挂载回来
(5)将元数据和qcow2文件传到Glance中
(0)从卷启动虚机,并且再挂载一个卷,然后运行novaimage-create命令。
|image|Attempttobootfromvolume-noimagesupplied||key_name|-||metadata|{}||name|vm10||os-extended-volumes:volumes_attached|[{"id":"26446902-5a56-4c79-b839-a8e13a66dc7a"},{"id":"de127d46-ed92-471d-b18b-e89953c305fd"}](1)从DB获取该虚机的块设备(BlockDevicesMapping)列表。
(2)对该列表中的每一个卷,依次调用CinderAPI做快照。对LVMDriver的volume来说,执行的命令类似于"lvcreate--size100M--snapshot--namesnap/dev/vg00/lvol1“。
虚拟化环境中的静态迁移也可以分为两种,一种是关闭客户机后,将其硬盘镜像复制到另一台宿主机上然后恢复启动起来,这种迁移不能保留客户机中运行的工作负载;另一种是两台宿主机共享存储系统,这时候的迁移可以保持客户机迁移前的内存状态和系统运行的工作负载。
(3)对服务性能的影响:客户机迁移前后性能的影响,以及目的主机上其它服务的性能影响。
动态迁移的应用场景包括:负载均衡、解除硬件依赖、节约能源和异地迁移。
对于静态迁移,你可以在宿主机上某客户机的QEMUmonitor中,用savevmmy_tag命令保存一个完整的客户机镜像快照,然后在宿主机中关闭或者暂停该客户机,然后将该客户机的镜像文件复制到另一台宿主机中,使用在源主机中启动该客户机时的命令来启动复制过来的镜像,在其QEMUmonitor中loadvmmy_tag命令恢复刚才保存的快照即可完全加载保存快照时的客户机状态。savevm命令可以保证完整的客户机状态,包括CPU状态、内存、设备状态、科协磁盘中的内存等。注意,这种方式需要qcow2、qed等格式的磁盘镜像文件的支持。
如果源宿主机和目的宿主机共享存储系统,则只需要通过网络发送客户机的vCPU执行状态、内存中的内容、虚机设备的状态到目的主机上。否则,还需要将客户机的磁盘存储发到目的主机上。共享存储系统指的是源和目的虚机的镜像文件目录是在一个共享的存储上的。
在基于共享存储系统时,KVM动态迁移的具体过程为:
注意,当客户机中内存使用率非常大而且修改频繁时,内存中数据不断被修改的速度大于KVM能够传输的内存速度时,动态迁移的过程是完成不了的,这时候只能静态迁移。
(1)在源宿主机上挂载NFS上的客户机镜像,并启动客户机
mountmy-nfs:/raw-images//mnt/kvm/mnt/rh1.img-smp2-m2048-netnic-nettap(2)在目的宿主机上也挂载镜像目录,并启动一个客户机用于接收动态迁移过来的内存内容
mountmy-nfs:/raw-images//mnt/kvm/mnt/rh1.img-smp2-m2048-netnic-nettap-incomingtcp:0:6666注意:(1)NFS挂载目录必须一致(2)“-incomingtcp:0:6666”参数表示在6666端口建立一个TCPsocket连接用于接收来自源主机的动态迁移的内容,其中0表示运行来自任何主机的连接。“-incoming“使qemu-kvm进程进入到监听模式,而不是真正以命令行中的文件运行客户机。
(3)在源宿主机的客户机的QEMUmonitor中,使用命令”migratetcp:host2:6666"即可进入动态迁移的流程。
过程类似,包括使用相同backingfile的镜像的客户机迁移,以及完全不同镜像文件的客户机的迁移。唯一的区别是,migrate命令中添加“-b”参数,它意味着传输块设备。
除了直接拷贝磁盘镜像文件的冷迁移,OpenStack还支持下面几种虚机热迁移模式:
实时迁移的过程并不复杂,复杂在于环境配置。
这种方式需要配置源(compute1)和目的主机(compute2)之间能够通过SSH相互访问,以确保能通过TCP拷贝文件,已经可以通过SSH在目的主机建立目录。
使用nova用户在compute1上执行操作:
root@compute1:/var/lib/nova/.ssh#chown-Rnova:nova/var/lib/novaroot@compute1:/var/lib/nova/.ssh#chmod700/var/lib/nova/.sshroot@compute1:/var/lib/nova/.ssh#chmod600/var/lib/nova/.ssh/authorized_keys测试SSH无密码访问:
在compute1和compute2上做如下配置:
->Edit/etc/libvirt/libvirtd.conflisten_tls=0listen_tcp=1auth_tcp=“none”->Edit/etc/init/libvirt-bin.confenvlibvirtd_opts="-d-l"->Edit/etc/default/libvirt-bin#optionspassedtolibvirtd,add"-l"tolistenontcplibvirtd_opts="-d-l"
->Edit/etc/nova/nova.conf,addfollowingline:[libvirt]block_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_TUNNELLED,VIR_MIGRATE_NON_SHARED_INClive_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_TUNNELLEDlive_migration_uri=qemu+tcp://%s/system2.2.2共享存储Livemigration环境配置其实共享存储的实时迁移配置的要求和块拷贝的实时迁移的配置差不多,除了下面几点:
live_migration_flag=VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_TUNNELLED注意:对于上面第二点,在Kilo版本中(前面版本的情况未知),当Nova使用RBD作为imagebackend时,Nova会认为是在共享存储上:
defcheck_instance_shared_storage_local(self,context,instance):"""Checkifinstancefileslocatedonsharedstorage."""ifself.image_backend.backend().is_shared_block_storage():returnNone在classRbd(Image):类中:
@staticmethoddefis_shared_block_storage():"""Trueifthebackendputsimagesonasharedblockstorage."""returnTrue目前,只有RBD作为imagebackend时,该函数才返回true。对于其它类型的backend,Nova会在目的host上的instancefolder创建一个临时文件,再在源host上查看该文件,通过判断是否该文件在共享存储上来判断是否在使用共享存储。
常见问题:
(1)在源host上,出现”liveMigrationfailure:operationfailed:FailedtoconnecttoremotelibvirtURIqemu+tcp://compute2/system:unabletoconnecttoserverat'compute2:16509':Connectionrefused“
其原因是2.1.1部分的libvirt设置不正确。
(2)在目的host上,出现”libvirtError:internalerror:processexitedwhileconnectingtomonitor:2015-09-21T14:17:31.840109Zqemu-system-x86_64:-drivefile=rbd:vms/6bef8898-85f9-429d-9250-9291a2e4e5ac_disk:id=cinder:key=AQDaoPpVEDJZHhAAu8fuMR/OxHUV90Fm1MhONQ==:auth_supported=cephx\;none:mon_host=9.115.251.194\:6789\;9.115.251.195\:6789\;9.115.251.218\:6789,if=none,id=drive-virtio-disk0,format=raw,cache=writeback,discard=unmap:couldnotopendiskimagerbd:vms/6bef8898-85f9-429d-9250-9291a2e4e5ac_disk:id=cinder:key=AQDaoPpVEDJZHhAAu8fuMR/OxHUV90Fm1MhONQ==:auth_supported=cephx\;none:mon_host=9.115.251.194\:6789\;9.115.251.195\:6789\;9.115.251.218\:6789:Couldnotopen'rbd:vms/6bef8898-85f9-429d-9250-9291a2e4e5ac_disk:id=cinder:key=AQDaoPpVEDJZHhAAu8fuMR/OxHUV90Fm1MhONQ==:auth_supported=cephx\;none:mon_host=9.115.251.194\:6789\;9.115.251.195\:6789\;9.115.251.218\:6789':Operationnotpermitted“
原因:目的host上的用户操作RBD的权限设置不正确,检查secret设置。
Nova有三个与迁移有关的命令:migrate,live-migrate和resize。
直接使用流程图来说明:
1.migrate和resize都是执行静态迁移。
2.静态迁移分为三个阶段:
(1)调用Scheduler算法选择目的node(步骤5),并通过RPC远程调用prep_resize做些迁移前的准备工作
(2)在源主机上,调用libvirtdriver做一系列操作:
(3)通过RPC,调用目的node上的Nova的finish_resize方法。该方法会在自己本机上设置网络、结束网络设置工作,并调用libvirtdriver来:
至此,虚机已经被拷贝到目的主机上了。接下来,用户有两个选择:resize_confirm和resize_revert。
迁移确认后,在源主机上,虚机的文件会被删除,虚机被undefine,虚机的VIF被从OVS上拔出,networkfilters也会被删除。
取消迁移的命令首先发到目的node上,依次teardownnetwork,删除domain,断掉volumeconnections,然后调用源主机上的方法来重建network,删除临时文件,启动domain。这样,虚机就会需要到resize之前的状态。
可以Novaclient的live-migration命令来做实时迁移,除了要被迁移的虚机和目的node外,它可以带两个额外的参数:
实时迁移的主要步骤如下:
其过程也可以分为三个阶段:
Nova通过RPC调用目的主机上novacomutemanager的pre_live_migration方法,它依次:
(1)准备instance目录:
(1)创建instancedir
(2)如果源和目的虚机不共享instancepath:获取镜像类型,为每一个disk,如果不使用backingfile的话则调用“qemu-imgcreate”方法来创建空的磁盘镜像;否则,依次创建空的Ephemeraldisk和Swapdisk,以及从Glance中获取image来创建Rootdisk
(3)如果不是blockmigration而且不is_shared_instance_path,则_fetch_instance_kernel_ramdisk
(2)调用volumerdriverapi为每一个volume建立目的主机和volume的连接
(3)调用plug_vifs(instance,network_info)将每一个vifplug到OVS上
(4)调用network_api.setup_networks_on_host方法,该方法会为迁移过来的虚机准备dhcp和gateway;
(5)调用libvirtdriver的ensure_filtering_rules_for_instance方法去准备networkfilters。
首先比较一下blocklivemigration和livemigration的flags的区别:
#novablocklivemigrationflags:VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_TUNNELLED,VIR_MIGRATE_NON_SHARED_INC#novalivemigrationflags:VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_TUNNELLED各自的含义如下:
再看看两个API的参数:
intvirDomainMigrateToURI(virDomainPtrdomain,constchar*duri,unsignedlongflags,constchar*dname,unsignedlongbandwidth)可见,两个API唯一的区别是不能指定新的虚机使用的XML配置。这时候你必须手动配置VNC或者SPICE地址为0.0.0.0or::(接收全部)或者127.0.0.1or::1(只限本机)。
调用API后,接下来就是等待其完成。这其中的过程应该主要包括:
(1)根据传入的domainxml,启动一个虚机,它处于等待TCPincoming状态
(2)从源node上将domain的数据传过来
(3)快完成时,关闭源node上的虚机,传输最后一次数据,打开目的node上的虚机
(4)将源node上的虚机删除
Nova每个0.5秒检查源虚机的状态,直到它被删除。
迁移完成后,需要执行后续的操作(_post_live_migration)。
在源主机上,依次执行下面的操作:
迁移的三个步骤中,前面第一个和第二个步骤中出现失败的话,会调用_rollback_live_migration启动回滚操作。该方法
(1)将虚机的状态由migrating变为running。
(2)调用network_api.setup_networks_on_host方法重做源主机上的网络设置
(3)通过RPC调用,去目的主机上将准备过程中建立的volume连接删除。
(4)通过RPC调用,去目的主机上调用compute_rpcapi.rollback_live_migration_at_destination函数,该方法会
(1)调用network_api.setup_networks_on_host方法去teardownnetworksondestinationhost
(2)调用libvirtdriver的driver.rollback_live_migration_at_destination方法,它会将domain删除,并且清理它所使用的资源,包括unplugvif,firewall_driver.unfilter_instance,_disconnect_volume,_delete_instance_files,_undefine_domain。
环境:准备两个虚机vm1和vm2,操作系统为cirros。打算将vm1迁移到另一个node上。在vm2上查看vm1在迁移过程中的状态。
迁移前:在vm1中运行“pingvm2”,并在vm2中ssh连接到vm1。
将虚机从compute1迁移到compute2成功,再从compute2迁移到compute1失败,报错如下:
Anerroroccurredtryingtolivemigrate.Fallingbacktolegacylivemigrateflow.Error:unsupportedconfiguration:Unabletofindsecuritydriverforlabelapparmor经比较迁移前后的虚机的xml,发现compute2上的虚机的xml多了一项:。
分别在compute1和2上运行“virshcapabilities”,发现compute1没有使用apparmor,而compute2使用了apparmor。
报错:
Command:iscsiadm-mnode-Tiqn.2010-10.org.openstack:volume-26446902-5a56-4c79-b839-a8e13a66dc7a-p10.0.2.41:3260--rescanExitcode:21Stdout:u''Stderr:u'iscsiadm:Nosessionfound.\n'tocaller原因是cinder代码中有bug,导致目的主机无法建立和volume的连接。fix。