
3.5 KVM相关介绍
下面介绍一下KVM的安装和使用。
3.5.1 KVM安装
1.首先,检查机器是否支持硬件辅助虚拟化,代码如下。

如果输出结果包含Intel的vmx或AMD的svm字段,则说明支持。
2.关闭SELinux,代码如下。

3.安装KVM,代码如下。

重启机器。
4.检查KVM模块是否加载完毕。

3.5.2 KVM虚拟机启动
通过KVM启动虚拟机有三种方式,第一种是通过命令行。
3.5.2.1 virt-install命令行
virt-install是虚拟机启动的命令行工具,通过virt-install启动一个虚拟机。具体命令如下。

name指定虚拟机的名称;vcpus和memory分别设置虚拟机使用的CPU和内存;location指定本地启动镜像,也可以是一个远程的URL地址;disk指定系统镜像路径,镜像大小和格式分别通过size和format指定;graphics指定图形展示方式,可以选择none(不设置)、VNC或者spice桌面。上面的例子中,我们关闭了图形界面,通过console直接配置操作系统。当命令启动后,在控制台将会出现如下配置界面。


上文中的1)表示设置语言和键盘;2)为设置时区;3)为设置安装源;4)为选择软件包安装,5)为设置系统盘;6)为是否开启Kdump的设置;7)为配置网络;8)为设置root密码;9)为添加用户。
上文中的[!]是必须要配置的选项。例如,选择2)配置时区(Time settings),显示如下界面。

假如选择2)Asia,会出现如下选项。


选择64)Shanghai,时区就设置好了。回到主菜单。选择5)设置系统盘(Installotion Destination),出现如下代码。

选择1)继续操作。

选择2)使用所有空间(Use All Space),会有以下选项。

选择3)LVM,使用LVM卷管理。返回主菜单,输入8)设置root密码(root Password)。

输入两次相同的密码。回到主菜单,就可以看到设置完毕后的效果,如下所示。

如果有软件的定制需求,可以进入4)选择软件包安装(Software selection),选择最小化安装、服务版本安装、桌面版本安装等。如果需要设置网络,可以进入7)配置网络(Network configuration),设置主机名、网卡IP和网关等信息。
最后输入b,开始安装。安装完成后,可以通过list命令查询创建的虚拟机代码,如下所示。

还可以通过console命令进入虚拟机,代码如下。

输入用户名和密码。

此时,已经可以在虚拟机内操作了。那么是不是每次启动一个虚拟机都需要安装一遍操作系统呢?当然不是,我们可以将上面的虚拟机导出一个镜像文件,然后便可以通过这个镜像文件启动很多虚拟机实例。CentOS官网也为我们提供了很多成熟的虚拟机镜像(下载地址:https://cloud.centos.org/centos/7/images),下载后便可以直接通过下面命令启动一个虚拟机,而无须再次安装操作系统。
下述命令的核心是通过import参数指定虚拟机启动方式为导入,通过disk指定启动镜像。

用户通常不知道公网下载的镜像登录密码,有两种方式可以解决。方法一是直接进入修复模式,重置root密码;另一种方法是通过guestfish修改。下面简单介绍一下流程。


上面的密码是在另一个终端,通过openssl生成一个“changeme”的密码,如下所示。

还可以通过virt-sysprep命令修改密码,具体如下。

通过-a指定需要修改的镜像,virt-sysprep的功能远不止修改密码,还可以通过--hostname参数修改主机名,通过--firstboot-command设置启动命令等。
3.5.2.2 virt-manager图形界面
除了上面介绍的virt-install命令行,还可以通过图形界面工具virt-manager创建虚拟机。具体步骤如下,首先是单击创建按钮,界面如图3-12所示。
创建虚拟机可以通过ISO镜像安装,也可通过import已有镜像文件,还可以通过HTTP远程下载镜像,或者通过PXE网络启动。选择ISO镜像界面,如图3-13所示。

图3-13 选择ISO镜像界面
这里需要提前下载ISO镜像,配置操作系统类型和版本后,选择机器配置,界面如图3-14所示。

图3-14 选择机器配置界面
主要包括CPU和内存。CPU以核数为单位,内存以MB为单位,然后指定系统盘大小,界面如图3-15所示。

图3-15 指定系统盘大小界面
最后,设定虚拟机名称和网络,默认是NAT网络模式,界面如图3-16所示。

图3-16 设定虚拟机名称和网络界面
单击“Finish”,完成创建,之后就是CentOS操作系统安装了,安装界面如图3-17所示。

图3-17 CentOS操作系统安装界面
3.5.2.3 用代码启动虚拟机
无论是OpenStack,还是CloudStack,各种KVM管理系统都是通过Libvirt接口管理KVM虚拟机的。下面将通过一个Python的Libvirt库函数创建一台VM,代码如下。


通过Libvirt的Python库可以很方便地创建和管理虚拟机。其他Libvirt接口(如虚拟机的关机、挂载存储等),读者可以从官网(https://libvirt.org/docs/libvirt-appdev-guide-python/en-US/html/)获取。
3.5.3 KVM运维
上面介绍了如何通过virt-manger创建虚拟机,但virt-manger功能远不止创建虚拟机。KVM的运维通常借助两个工具,一个是命令行工具virsh;另一个是图形化工具virt-manager。一般来说,只要可以通过virsh命令操作的,virt-manager都支持。
3.5.3.1 虚拟机管理
virsh也是基于Libvirt接口操作KVM的命令行工具,virsh的命令和virt-manager管理页面是相互对应的。在之前介绍的Libvirt主要负责虚拟机、存储、网络接口管理等功能。
1.虚拟机列表查询
通过virsh list查看虚拟机列表,代码如下。

其中,ID为虚拟机Domain ID,Name为虚拟机名称,State为虚拟机当前状态,可以通过virtual Machine Manager管理页面查看,如图3-18所示。

图3-18 virtual Machine Manager管理页面
2.虚拟机详情
通过dumpxml命令可以导出虚拟机整个配置的详细信息,虚拟机的配置文件是存储在“/etc/libvirt/qemu/”中,而Libvirt默认镜像存储在“/var/lib/libvirt/images/”中。

输出结果如下。




virt-manager图形界面如图3-19所示,左侧展示了虚拟机所有的硬件列表,右侧是配置详情。

图3-19 virt-manager图形界面
3.登录虚拟机
KVM的登录方式有很多,包括SSH、VNC,以及本地控制台和瘦客户端登录。SSH登录是最常用的一种登录方式,可以通过用户名和密码登录。如果在公有云场景中,建议使用秘钥登录,这样更加安全。VNC是一种跨平台的远程桌面分享软件,通过VNC客户端或者HTML5浏览器都可以登录,还可以通过SPICE协议(一种KVM远程桌面协议)连接到瘦客户端登录机器。如果是在虚拟机的宿主机上,还可以通过virsh console命令通过串口直接登录机器,但需要开启控制台,以CentOS7为例,需要编辑“/etc/default/grub”文件,追加“console=ttyS0”。代码如下。

执行以下代码。

更新grub配置,并为虚拟机添加console设备。

之后便可以通过“virsh console虚拟机名称”命令登录机器。
3.5.3.2 存储管理
虚拟机可以挂载多种块存储。为了方便管理不同的存储,Libvirt设计了存储池(storage pool)的概念。常见的存储池主要包括:目录存储池、文件系统存储池、逻辑卷存储池、iSCSI存储池、RBD存储池、ZFS存储池等。
如果是逻辑卷(lvm)存储池,每个存储池代表一个逻辑卷组(volume group简称vg),而每个卷(Volume)则代表一个逻辑卷(Logical volume简称lv);如果是文件目录存储池,那么每个存储池则代表一个目录,而卷则是一个镜像块,镜像块可以是qcow2、raw、vmdk等格式;如果是Ceph RBD存储,那么每个存储池对应OSD存储池,而每个卷对应一个RDB块。

图3-20 逻辑卷存储池示意图
下面以常用的逻辑卷存储为例,创建一个池。


设置启动存储池,代码如下所示。

于是,便可以创建存储块。

创建成功后,通过vol-list命令查询。

除此之外,还可以通过lvs命令查看创建的逻辑块。

接下来,便可以通过attach-disk将这个块挂载到虚拟机上,并且命名为vdb。

操作完成后,登录到虚拟机,执行fdisk命令,查看新挂载的磁盘。

如果此时需要对磁盘进行扩容,首先需要对逻辑卷进行扩容。

如果是扩大存储,则没有问题,但如果是缩小存储,会存在丢数据的风险,需要谨慎操作。

最后一个参数20是虚拟机的Domain ID。操作成功后,查看磁盘大小,变成了10.7GB。

如果磁盘已经格式化文件系统,请不要忘记执行resize2fs命令扩容文件系统。
如果是挂载Ceph的RBD存储,可以直接通过attach-device命令挂载。首先,定义RBD存储块,通过source指定Ceph的Monitor地址。

3.5.3.3 网络管理
KVM会在每个主机上创建一个默认(default)的NAT网络。每个网络都可以设置一个DHCP网络段,将虚拟机网卡添加到该网络后会从对应的网段中分配一个可用IP。

虚拟机被外部访问通常有两种做法,一种将虚拟机添加到网桥上,具体命令如下。

上面通过source命令指定虚拟机添加的网桥br2,通过网桥连接到其他网络出口。除此之外,虚拟机还可以直接通过NAT的方式映射到主机网络。Libvirt本身不支持网络映射,所以如果需要将虚拟机IP和端口映射成宿主机的IP和端口,需要借助下面两条iptables规则。

规则1):允许本机转发网络包到虚拟机;规则2):通过DNAT将主机的指定端口映射到虚拟机的某个端口上。
3.5.3.4 快照管理
快照就像拍照片一样,将虚拟机当前的状态记录到文件中,后续还可以根据这些文件恢复到之前的状态。对虚拟机进行重大的配置修改前,可以通过快照的方式保存当前状态,以便迅速恢复到之前的状态。
KVM快照按快照对象分为内存快照和磁盘快照;安装快照时,虚拟机状态可以分为离线快照(对关机状态的虚拟机进行快照)和在线快照(对运行虚拟机进行快照)。安装快照的保存方式可以分为内部快照(保存在镜像文件中)和外部快照(保存到指定的单独文件中)。
(1)内存快照
如果是内存快照,可以通过save,运行以下命令。

其中,CentOS7.0是虚拟机名称,mem.snap是内存快照文件名。如果需要恢复内存数据,可以执行以下命令。

这里需要注意:①在对虚拟机完成内存快照后,虚拟机将处于关机状态;②只能对处于关机状态的虚拟机进行内存快照恢复;③内存快照后,如果需要对磁盘进行修改,可能会导致系统故障。
(2)磁盘快照
磁盘快照是将当前磁盘的状态保存起来。如果是创建内部快照,可以执行以下命令。

其中,--domain指定虚拟机名称,--name指定镜像名称。在创建快照的过程中,虚拟机将会暂时处于挂起状态。对于虚拟机里面的应用程序可能会出现卡顿的状态,建议在业务的低峰期操作。
如果后续需要恢复这个快照点,可以通过snapshot-revert命令完成,代码如下。

执行成功后,虚拟机的磁盘信息将会回到之前的状态。这些快照都会保存到虚拟机的镜像文件中。长期积累,会导致镜像文件越来越大。为此,KVM提供了外部快照功能。外部快照和Docker的AUFS原理非常相似,通过快照的依赖链,如:base<-snapshot1<-snapshot2<-snapshot3。最开始只有base只读层,snapshot1是可以读写的,后续创建snapshot2快照后,snapshot1将变为只读,始终保持最新快照为可读可写,从而完成快照的增量添加。每个快照只记录这段时间内存磁盘的变化情况。

通过disk-only参数指定创建外部快照。除此之外,还可以通过以下命令完成外部快照的创建。

--live支持在不挂起虚拟机的情况下完成快照创建。Libvirt默认的快照保存路径为“/var/lib/Libvirt/images”,这些增量的外部快照非常小。通过snapshot-list命令可以查看创建的快照。

可以看到snapshot1和snapshot2的状态为磁盘快照。如果需要查看快照的依赖关系,可以追加“--tree”参数,如下所示。

也可以通过qemu-img查看镜像。

遗憾的是,目前Libvirt并不支持外部快照的恢复,但可以采用另外一种方案解决:通过外部快照创建一个新的虚拟机,从而完成外部快照的镜像恢复。具体创建方式与基于镜像创建虚拟机的步骤相同,在此不赘述了。
3.5.4 KMV迁移
冷迁移又叫静态迁移,是指将处于关机状态的虚拟机迁移到另一台机器上面。由于不需要迁移虚拟机内存等实时数据,迁移比较安全可控。冷迁移命令如下:

与冷迁移对应的还有一种迁移方式是热迁移,热迁移支持将正在运行的虚拟机迁移到另一台机器上面运行。热迁移命令为:#virsh migrate--live虚拟机名称qemu+ssh://另外一台宿主机地址/system。
关于热迁移还需要注意两点:第一,热迁移需要共享存储支持;第二,热迁移的整个过程能持续很长时间,并且迁移进度的百分比还可能会出现下降的情况,这些都是正常现象,因为必须把内存中所有数据都迁移过去,如果被迁移的虚拟机还在不断写数据,迁移的过程会比较缓慢。
KVM迁移需要注意宿主机的硬件环境是否相同,因为部分虚拟机配置了一些硬件绑定规则,如无法将一台配置了SR-IOV网卡的虚拟机迁移到普通的宿主机上。
3.5.5 KVM克隆
KVM克隆是指克隆出一个相同配置的KVM虚拟机。KVM虚拟机克隆非常方便,通过命令virt-clone即可实现,但克隆虚拟机要求虚拟机必须是关机或者挂起状态,所以务必先挂起或者关闭虚拟机后再执行如下操作。

--o是original的简称,代表克隆的数据源;--n是name的简称,指定克隆出新虚拟机的名称;此外,还可以通过--f指定输出的镜像文件。通过上面命令克隆的新虚拟机需要使用virt-sysprep命令擦除原来的网卡等信息。
3.5.6 KEM优化
1.NUMA优化
在NUMA的架构中,每个CPU都可能会访问其他处理器的存储,速度相差几十倍,所以应尽量访问自己的存储器以提高效率,即通过/proc/sys/kernel/numa_balanceing开启或者关闭NUMA平衡策略。
2.核绑定
每个虚拟机使用一个虚拟核VCPU,一个物理的CPU可以虚拟出多个VCPU,通过virsh CPU info可以查看对应关系。在CPU压力比较大的时候,可以通过cgroup CPUSet技术,将VCPU绑定到指定一个物理核上运行,避免上下文切换带来的性能损耗。
3.巨型页
Linux默认的巨型页大小是4KB,但也可以是使用1GB的巨型页,从而提高内存页分配性能。