目录
1.什么是KVM
基于内核的虚拟机 Kernel-based Virtual Machine(KVM)是一种内建于 Linux 中的开源虚拟化技术。具体而言,KVM 可帮助您将 Linux 转变为一个 Hypervisor (虚拟机监控程序),使主机计算机能够运行多个隔离的虚拟环境,即虚拟客户机或虚拟机(VM)。
KVM 是 Linux 的一部分,Linux 2.6.20 或更新版本包括 KVM。KVM 于 2006 年首次公布,并在一年后合并到主流 Linux 内核版本中。由于 KVM 属于现有的 Linux 代码,因此它能立即享受每一项新的 Linux 功能、修复和发展,无需进行额外工程。
KVM 将 Linux 转变为 1 类(裸机恢复)虚拟机监控程序。所有虚拟机监控程序都需要一些操作系统层面的组件才能运行虚拟机,如内存管理器、进程调度程序、输入/输出(I/O)堆栈、设备驱动程序、安全管理器以及网络堆栈等。由于 KVM 是 Linux 内核的一部分,因此所有这些组件它都有。每个虚拟机都像普通的 Linux 进程一样实施,由标准的 Linux 调度程序进行调度,并且使用专门的虚拟硬件,如网卡、图形适配器、CPU、内存和磁盘等。
2.KVM架构
KVM 是基于虚拟化扩展(Intel VT 或者 AMD-V)的 X86 硬件的开源的 Linux 原生的全虚拟化解决方案。KVM 中,虚拟机被实现为常规的 Linux 进程,由标准 Linux 调度程序进行调度;虚机的每个虚拟 CPU 被实现为一个常规的 Linux 线程。这使得 KVM 能够使用 Linux 内核的已有功能。
但是,KVM 本身不执行任何硬件模拟,需要用户空间程序通过 /dev/kvm 接口设置一个客户机虚拟服务器的地址空间,向它提供模拟 I/O,并将它的视频显示映射回宿主的显示屏。目前这个应用程序是 QEMU。
Linux 上的用户空间、内核空间和虚机:
- Guest:客户机系统,包括CPU(vCPU)、内存、驱动(Console、网卡、I/O 设备驱动等),被 KVM 置于一种受限制的 CPU 模式下运行。
- KVM:运行在内核空间,提供 CPU 和内存的虚级化,以及客户机的 I/O 拦截。Guest 的 I/O 被 KVM 拦截后,交给 QEMU 处理。
- QEMU:修改过的被 KVM 虚机使用的 QEMU 代码,运行在用户空间,提供硬件 I/O 虚拟化,通过 IOCTL /dev/kvm 设备和 KVM 交互。
KVM 实现拦截虚机的 I/O 请求的原理:
现代 CPU 本身实现了对特殊指令的截获和重定向的硬件支持,甚至新硬件会提供额外的资源来帮助软件实现对关键硬件资源的虚拟化从而提高性能。以 X86 平台为例,支持虚拟化技术的 CPU 带有特别优化过的指令集来控制虚拟化过程。通过这些指令集,VMM 很容易将客户机置于一种受限制的模式下运行,一旦客户机试图访问物理资源,硬件会暂停客户机运行,将控制权交回给 VMM 处理。VMM 还可以利用硬件的虚级化增强机制,将客户机在受限模式下对一些特定资源的访问,完全由硬件重定向到 VMM 指定的虚拟资源,整个过程不需要暂停客户机的运行和 VMM 的参与。由于虚拟化硬件提供全新的架构,支持操作系统直接在上面运行,无需进行二进制转换,减少了相关的性能开销,极大简化了VMM的设计,使得VMM性能更加强大。从 2005 年开始,Intel 在其处理器产品线中推广 Intel Virtualization Technology 即 IntelVT 技术。
QEMU-KVM:
其实 QEMU 原本不是 KVM 的一部分,它自己就是一个纯软件实现的虚拟化系统,所以其性能低下。但是,QEMU 代码中包含整套的虚拟机实现,包括处理器虚拟化,内存虚拟化,以及 KVM需要使用到的虚拟设备模拟(网卡、显卡、存储控制器和硬盘等)。
为了简化代码,KVM 在 QEMU 的基础上做了修改。KVM 运行期间,QEMU 会通过 KVM 模块提供的系统调用进入内核,由 KVM 负责将虚拟机置于处理的特殊模式运行。当虚机进行 I/O 操作时,KVM 会从上次系统调用出口处返回 QEMU,由 QEMU 来负责解析和模拟这些设备。
从 QEMU 角度看,也可以说是 QEMU 使用了 KVM 模块的虚拟化功能,为自己的虚机提供了硬件虚拟化加速。除此以外,虚机的配置和创建、虚机运行所依赖的虚拟设备、虚机运行时的用户环境和交互,以及一些虚机的特定技术比如动态迁移,都是 QEMU 自己实现的。
KVM:
KVM 内核模块在运行时按需加载进入内核空间运行。KVM 本身不执行任何设备模拟,需要 QEMU 通过 /dev/kvm 接口设置一个 GUEST OS 的地址空间,向它提供模拟的 I/O 设备,并将它的视频显示映射回宿主机的显示屏。它是KVM 虚机的核心部分,其主要功能是初始化 CPU 硬件,打开虚拟化模式,然后将虚拟客户机运行在虚拟机模式下,并对虚机的运行提供一定的支持。以在 Intel 上运行为例,KVM 模块被加载的时候,它:
- 首先初始化内部的数据结构
- 做好准备后,KVM 模块检测当前的 CPU,然后打开 CPU 控制及存取 CR4 的虚拟化模式开关,并通过执行 VMXON 指令将宿主操作系统置于虚拟化模式的根模式
- 最后,KVM 模块创建特殊设备文件 /dev/kvm 并等待来自用户空间的指令
接下来的虚机的创建和运行将是 QEMU 和 KVM 相互配合的过程。两者的通信接口主要是一系列针对特殊设备文件 /dev/kvm 的 IOCTL 调用。其中最重要的是创建虚机。它可以理解成KVM 为了某个特定的虚机创建对应的内核数据结构,同时,KVM 返回一个文件句柄来代表所创建的虚机。
针对该句柄的调用可以对虚机做相应地管理,比如创建用户空间虚拟地址和客户机物理地址、真实物理地址之间的映射关系,再比如创建多个 vCPU。KVM 为每一个 vCPU 生成对应的文件句柄,对其相应地 IOCTL 调用,就可以对vCPU进行管理。其中最重要的就是“执行虚拟处理器”。通过它,虚机在 KVM 的支持下,被置于虚拟化模式的非根模式下,开始执行二进制指令。在非根模式下,所有敏感的二进制指令都被CPU捕捉到,CPU 在保存现场之后自动切换到根模式,由 KVM 决定如何处理。
除了 CPU 的虚拟化,内存虚拟化也由 KVM 实现。实际上,内存虚拟化往往是一个虚机实现中最复杂的部分。CPU 中的内存管理单元 MMU 是通过页表的形式将程序运行的虚拟地址转换成实际物理地址。在虚拟机模式下,MMU 的页表则必须在一次查询的时候完成两次地址转换。因为除了将客户机程序的虚拟地址转换了客户机的物理地址外,还要将客户机物理地址转化成真实物理地址。
从上可知KVM只是虚拟化解决方案的一部分,想要实现全虚拟化,还需要其他的条件:
- CPU处理器提供的虚拟化支持(VT-x 硬件辅助虚拟化,可以为GuestOS创建虚拟化处理器,本质是对寄存器的隔离模拟和对指令集的划分)
- 内存可以通过kvm虚拟化成独立的虚拟化地址(/dev/kvm)
- I/O虚拟化(QEMU)
KVM虚拟化 = KVM内核模块 + /dev/kvm + QEMU
3.KVM部署
实验环境: CentOS 7.6
~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
~]# uname -r
3.10.0-957.el7.x86_64
~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: man:firewalld(1)
~]# getenforce
Disabled
检查是否开启虚拟化
~]# grep --color -E '(vmx|svm|lm)' /proc/cpuinfo #有vmx(intel)或svm(amd)就说明支持,lm表示64位操作系统
....................................vmx..........................................
安装依赖包:
~]# wget https://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm #更新源
~]# rpm -ivh epel-release-latest-7.noarch.rpm
~]# yum -y install \
qemu-kvm \
qemu-kvm-tools \
qemu-img \
virt-manager \
libvirt \
libvirt-python \
libvirt-client \
bridge-utils \
virt-viewer \
virt-install
~]# systemctl start libvirtd
~]# systemctl enable libvirtd
创建桥接网卡:
~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0 #加上最后一行
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=dhcp
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
NAME=eth0
UUID=d5792590-f1b7-4705<