2019-06-21-KVM到底是个啥?

KVM的现状

KVM最初是由Qumranet公司的Avi Kivity开发的,作为他们的VDI产品的后台虚拟化解决方案。为了简化开发,Avi Kivity并没有选择从底层开始新写一个Hypervisor,而是选择了基于Linux kernel,通过加载模块使Linux kernel本身变成一个Hypervisor。2006年10月,在先后完成了基本功能、动态迁移以及主要的性能优化之后,Qumranet正式对外宣布了KVM的诞生。同月,KVM模块的源代码被正式纳入Linux kernel,成为内核源代码的一部分。2008年9月4日,Redhat公司以1.07亿美元收购了Qumranet公司,包括它的KVM开源项目和开发人员。自此,Redhat开始在其RHEL发行版中集成KVM,逐步取代Xen,并从RHEL7开始,正式不支持Xen。

在KVM出现之前,Xen虚拟化解决方案已经业界比较成熟的一款开源VMM,但是KVM出现之后,很快被Linux内核社区接受,就是因为Xen不是通过Linux内核去管理系统资源(硬件/软件),而是通过自身的管理系统去完成,仅这一点就让Linux内核社区很不爽。同时,Xen在当时设计上采用半虚方式,需要修改Guest OS的内核来满足I/O驱动性能要求,从而不支持商用OS(Windows、Mac OS)的虚拟化,使得Xen相比KVM来说,在硬件辅助虚拟化的支撑上包袱更重,转型困难重重。

目前,KVM已经成为OpenStack用户选择的事实上的Hypervisor标准。OpenStack自身调查数据显示,KVM占87%以上的部署份额,详细参见http://superuser.openstack.org/articles/openstack-user-survey-insights-november-2014/。可以说,KVM已经主导公有云部署的Hypervisor市场,同时在电信云部署方面也是一枝独秀。

功能上,虚拟化发展到今天,各个Hypervisor的主要功能都差不多。KVM由于其开源性,反而商业上的限制较少。性能上,KVM和Xen都能达到原生系统95%以上的效率(CPU、内存、网络、磁盘等benchmark衡量),KVM甚至还略微好过Xen一点点。微软虽然宣布其Hype-V的性能更好,但这只是微软一家之言,并没有公开的数据支撑。

在电信云NFV领域来说,由于通信网络设备的实时性要求很高,且NFV的开源平台OPNFV选择了OpenStack。为了更好实现网络功能虚拟化的愿景,其实时性要求责无旁贷的落到了KVM头上,NFV-KVM项目也就顺理成章地诞生了,它作为OPNFV的子项目主要解决KVM在实时性方面受到的挑战。详情请参见https://wiki.opnfv.org/display/kvm/Nfv-kvm。

总的来说,虚拟化技术发展到今天已经非常成熟,再加上DPDK代码的开源化、KVM也好、其他Hypervisor也好,在转发性能的优化和硬件辅助虚拟化的支撑上都半斤八两,但由于KVM的开源性特性以及社区的热度,使得其在云计算领域解决方案一枝独秀。甚至,华为在其FusionSphere6.3版本也开始拥抱KVM而抛弃了Xen。要知道,华为在剑桥大学可是专门有一支团队在研究Xen虚拟化。

KVM虚拟化实现

KVM全称是Kernel-based Virtual Machine,即基于内核的虚拟机,是采用硬件辅助虚拟化技术的全虚拟化解决方案。对于I/O设备(如硬盘、网卡等),KVM即支持QEMU仿真的全虚,也支持virtio方式的半虚。KVM从诞生开始就定位于基于硬件虚拟化支持的全虚实现,由于其在Linux内核2.6版本后被集成,通过内核加载模式使得Linux内核变成一个事实上的Hypervisor,但是硬件管理还是由Linux Kernel来完成。因此,它是一个典型Type 2型虚拟化,如下图所示。

如上图,一个KVM客户机就对应一个Linux进程,每个vCPU对应这个进程下的一个线程,还有单独处理I/O的线程,属于同一个进程组(忘了的,请回顾本站CPU虚拟化系列文章)。所以,宿主机上Linux Kernel可以像调度普通Linux进程一样调度KVM虚拟机,这种机制使得Linux Kernel的进程优化和调度功能优化等策略,都能用于KVM虚拟机。比如:通过进程权限限定功能可以限制KVM客户机的权限和优先级等。

由于KVM嵌入Linux内核中,除了硬件辅助虚拟化(如VT-d)透传的硬件设备能被虚拟机看见外,其他的I/O设备都是QEMU模拟出来的,所以QEMU是KVM的天生好基友。而在内存管理方面,由于KVM本身就是Linux Kernel中一个模块,所以其内存管理完全依赖于Linux内核。Linux系统的所有内存管理机制,如大页、零页(重复页)共享KSM、NUMA、mmap共享内存等,都可以用于KVM虚拟机的内存管理上。

在数据存储方面,由于KVM是Linux Kernel的一部分,它可以利用所有存储厂商的存储架构,支持Linux支持的任何存储和文件系统来存储数据。同时,还支持全局文件系统GFS2等共享文件系统上的虚拟机镜像,允许虚拟机在多个Host之间共享存储或使用逻辑卷共享存储。KVM虚拟机的原生磁盘格式为QCOW2,支持磁盘镜像、快照、多级快照和压缩加密等虚拟化特性。但是,有一点需要注意:基于KVM的镜像盘必须使用RAW格式(一种非精简格式盘,在华为FC解决方案中称为普通盘。它就像我们的物理硬盘一样,分配多大空间就是多大空间),否则利用镜像发布虚机会不成功。

在实时迁移方面,KVM虚拟机支持在多个Host之间热迁移,无论是OVS分布式虚拟交换机,还是Linux Bridge分布式虚拟交换机,KVM虚拟机都能完美兼容实现虚拟接入,且对用户(实际用户,APP等,也就是我们常说的租户这个概念)是透明的。同时,还支持将客户机当前状态,也就是快照保存到磁盘,并在以后恢复。

在设备驱动方面,KVM支持混合虚拟化,其中半虚拟化的驱动程序安装在虚拟机的OS中,允许虚拟机使用优化I/O接口而不用模拟设备。KVM使用的半虚拟化驱动程序是IBM和RedHat联合Linux社区开发的virtio标准,它是一个与Hypervisor独立的,构建设备驱动程序的接口,是KVM内核的另一个好基友,不仅支持KVM对其调用,还支持VMware、Hyper-V对其调用。同时,就像前面提到的,KVM也支持VT-d技术,两者如胶似漆,完美契合,通过将Host上PCI总线上的设备透传给虚拟机,让虚拟机可以直接使用原生的驱动程序来驱动这些物理设备。忘了啥是VT-d的,请回顾本站I/O虚拟化一文。同时,就像前面开篇提到的KVM在在性能方面能达到原生的95%以上,不差于Xen虚拟化,但在伸缩性方面支持拥有多达288个vCPU和4TB RAM的虚拟机,远超于Xen(Xen因为DM0的存在,其伸缩性受限,单机最大支持32个vCPU,1.5TB RAM)。

通过上面大致了解,可以说KVM就是在硬件辅助虚拟化技术之上构建起来的VMM。但并非要求所有硬件虚拟化技术都支持才能运行KVM虚拟化,KVM对硬件最低的依赖是CPU的硬件虚拟化支持(比如:Intel的VT-x技术和AMD的AMD-V技术),而其他的内存和I/O的硬件虚拟化支持,会让整个KVM虚拟化下的性能得到更多的提升。所以,我们在虚拟机部署KVM功能时,首先就是要查看宿主机Host(也就是我们实验环境的虚拟机,一般是在VMware Workstations的虚拟机打开CPU虚拟化功能。如果是真实环境,则需要物理服务器的BIOS中要开启VT-x功能,如下图所示。

然后,进入服务器后通过如下指令确认VT-x功能支持,如下图所示。

如果什么输出都没有,那说明你的系统并没有支持虚拟化的处理 ,不能使用KVM。另外Linux发行版本必须在64bit环境中才能使用KVM。

KVM软件架构拆解

KVM是在硬件虚拟化支持下的全虚拟化技术,所以它能在相应硬件上运行几乎所有的操作系统,如:Linux、Windows、FreeBSD、MacOS等。KVM虚拟化的核心主要由以下两个模块组成:

1)KVM内核模块,它属于标准Linux内核的一部分,是一个专门提供虚拟化功能的模块,主要负责CPU和内存的虚拟化,包括:客户机的创建、虚拟内存的分配、CPU执行模式的切换、vCPU寄存器的访问、vCPU的执行。

2)QEMU用户态工具,它是一个普通的Linux进程,为客户机提供设备模拟的功能,包括模拟BIOS、PCI/PCIE总线、磁盘、网卡、显卡、声卡、键盘、鼠标等。同时它通过ioctl系统调用与内核态的KVM模块进行交互。

上图中,在KVM虚拟化架构下,每个客户机就是一个QEMU进程,在一个宿主机上有多少个虚拟机就会有多少个QEMU进程。客户机中的每一个虚拟CPU对应QEMU进程中的一个执行线程,一个宿主机Host中只有一个KVM内核模块,所有虚拟机都与这个内核模块进行交互。

KVM内核模块

KVM内核模块是KVM虚拟化的核心模块,它在内核中由两部分组成:一个是处理器架构无关的部分,用lsmod命令中可以看到,叫作kvm模块另一个是处理器架构相关的部分,在Intel平台上就是kvm_intel这个内核模块。如下图所示,KVM的主要功能是初始化CPU硬件,打开虚拟化模式,然后将虚拟机运行在虚拟环境下,并对虚拟机的运行提供一定的支持。

KVM仅支持硬件辅助的虚拟化,所以,“打开并初始化系统硬件以支持虚拟机的运行”是KVM模块本职工作。以Intel CPU架构服务器为例,KVM打开并初始化硬件以支持虚拟机运行的过程如下:

Step1:在被内核加载的时候,KVM模块会先初始化内部的数据结构。

Step2:做好准备之后,KVM模块检测系统当前的CPU,然后打开CPU控制寄存器CR4中的虚拟化模式开关,并通过执行VMXON指令将宿主操作系统(包括KVM模块本身)置于CPU虚拟化模式中的根模式root operation,详细参见本站计算虚拟化之CPU虚拟化一文。

Step3:KVM模块创建特殊设备文件/dev/kvm并等待来自用户空间的命令。

Step4:后续虚拟机的创建和运行,其本质上就是是一个用户空间的QEMU和内核空间的KVM模块相互配合的过程。

这里面的/dev/kvm这个设备比较关键,它可以被当作一个标准的字符设备,用来缓存用户空间与内核空间切换的上下文,也就是ioctl调用上下文,是KVM模块与用户空间QEMU的通信接口。

针对/dev/kvm文件的最重要的loctl调用就是“创建虚拟机”。这里的创建虚拟机,简单理解就是KVM为了某个虚拟机创建对应的内核数据结构,并且返回一个文件句柄来代表所创建的虚拟机。针对该文件句柄的loctl调用可以对虚拟机做相应的管理,比如创建用户空间虚拟地址和客户机物理地址及真实内存物理地址的映射关系,再比如创建多个可供运行的vCPU。KVM模块同样会为每一个创建出来的vCPU生成对应的文件句柄,对其文件句柄进行相应的loctl调用,就可以对vCPU进行调度管理。

而针对vCPU的最重要的loctl调用就是“运行虚拟处理器”。通过它,虚拟机就可以在非根模式下运行,一旦执行敏感指令,就通过VMX Exit切入根模式,由KVM决定后续的操作并返回执行结果给虚拟机。

除了处理器虚拟化,内存虚拟化的实现也是由KVM内核模块完成,包括影子页表和EPT硬件辅助,均由KVM内核模块负责完成GVA—>HPA的两级转换。处理器对设备的访问主要是通过I/O指令MMIO,其中I/O指令会被处理器直接截获,MMIO会通过配置内存虚拟化来捕捉。一般情况下,除非对外设有极高性能要求,比如虚拟中断和虚拟时钟,外设均是由QEMU这个用户空间的进程来模拟实现。

QEMU用户态设备模拟

QEMU原本就是一个著名的开源虚拟机软件项目,既是一个功能完整的虚拟机监控器,也在QEMU-KVM的软件栈中承担设备模拟的工作。它是由一个法国工程师独立编写的代码实现,并不是KVM虚拟化软件的一部分,但是从名字就能知道它和KVM是一辈子的好基友。

QEMU最初实现的虚拟机是一个纯软件的实现,也就是我们常说的通过二进制翻译来实现虚拟机的CPU指令模拟,所以性能比较低。但是,其优点是跨平台,甚至可以支持客户机与宿主机并不是同一个架构,比如在x86平台上运行ARM客户机。同时,QEMU能与主流的Hypervisor完美契合,包括:Xen、KVM、Hyper-v,以及VMware各种Hypervisor等,为上述这些Hypervisor提供虚拟化I/O设备。

而QEMU与KVM密不可分的原因,就是我们常说的QEMU-KVM软件协议栈。虚拟机运行期间,QEMU会通过KVM内核模块提供的系统调用ioctl进入内核,由KVM内核模块负责将虚拟机置于处理器的特殊模式下运行。一旦遇到虚拟机进行I/O操作时,KVM内核模块会从上次的系统调用ioctl的出口处返回QEMU,由QEMU来负责解析和模拟这些设备。除此之外,虚拟机的配置和创建,虚拟机运行依赖的虚拟设备,虚拟机运行时的用户操作环境和交互,以及一些针对虚拟机的特殊技术,比如动态迁移,都是由QEMU自己实现的。

QEMU除了提供完全模拟的设备以外,还支持virtio协议的设备模拟。在前端虚拟机中需要安装相应的virtio-blk、virtio-scsi、virtio-net等驱动,就能连接到QEMU实现的virtio的虚拟化后端。除此之外,QEMU还提供了virtio-blk-data-plane的高性能的块设备I/O方式,与传统virtio-blk相比,它为每个块设备单独分配一个线程用于I/O处理,不需要与原QEMU执行线程同步和竞争锁,而且使用ioeventfd/irqfd机制,利用宿主机Linux上的AIO(异步I/O)来处理客户机的I/O请求,使得块设备I/O效率进一步提高。

总之,在KVM虚拟化的软件架构中,KVM内核模块与QEMU用户态程序是处于最核心的位置,有了它们就可通过qemu命令行操作实现完整的虚拟机功能。

KVM的各类上层管理APP

KVM目前已经有libvirt API、virsh命令行工具、OpenStack云管理平台等一整套管理工具,与VMware提供的商业化管理工具相比虽然有所差距,但KVM这一整套管理工具都是API化的、开源的,可以灵活使用,且能二次定制开发。

1)libvirt

libvirt是使用最广泛的对KVM虚拟化进行管理的工具和应用程序接口,已经是事实上的虚拟化接口标准。作为通用的虚拟化API,libvirt不但能管理KVM,还能管理VMware、Hyper-V、Xen、VirtualBox等其他虚拟化方案。但是,在通过Docker或Kolla部署OpenStack时,由于容器镜像中集成了libvirt功能,需要关闭Host的libvirt服务,否则会发生PID调用错误。

2)virsh

virsh是一个常用的管理KVM虚拟化的命令行工具,用于在单个宿主机上进行运维操作。virsh是用C语言编写的一个调用libvirt API的虚拟化管理工具,其源代码也是同步公布在libvirt这个开源项目中的。我们常用的KVM虚拟机查看指令就是virsh list –all,如下图:

3)virt-manager

virt-manager是专门针对虚拟机的图形化管理软件,底层与虚拟化交互的部分仍然是调用libvirt API来操作的。virt-manager除了提供虚拟机生命周期管理的基本功能,还提供性能和资源使用率的监控,同时内置了VNC和SPICE客户端,方便图形化连接到虚拟客户机中。virt-manager在RHEL、CentOS、Fedora等操作系统上都非常流行,因其图形化操作的易用性,成为新手入门学习虚拟化操作的首选管理软件。但是,在真实服务器端由于需要安装图形化界面,所以并不常用(服务器环境实现可视化一般是通过VNC功能实现)。

4)OpenStack

OpenStack是目前业界使用最广泛的功能最强大的云管理平台,它不仅提供了管理虚拟机的丰富功能,还有非常多其他重要管理功能,如:对象存储、块存储、网络、镜像、身份验证、编排服务、控制面板等。OpenStack的Nova、Cinder和Neutron,也就是计算、存储和网络管理组件仍然使用libvirt API来完成对底层虚拟化的管理,其计算、存储和网络服务组件的配置文件conf中,均有[libvirt]分类引用配置项。

以上,就是KVM这个耳熟能详的Hypervisor的全貌,作为运维人员掌握以上知识点即可,也就是理解KVM虚拟机的真正工作原理即可。如果工作层面涉及性能或管理的二次开发,必须进一步了解并掌握其官方社区的源码以及各种热点技术或BUG解决方案,这就需要自己钻研了。后面,我们会从实战的角度来介绍如何用熟KVM虚拟机的各类操作。

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!