半虚拟化驱动
-
VirtIO
- qemu纯软件模拟的I/O设备:回路长,效率低
- virtio半虚拟化驱动可以获得更好的I/O性能,需要在客户机中安装virtio驱动(linux,windows等)
-
直接分配设备
在kvm/qemu中,虚拟机可以使用的设备分为三种:- 纯软件模拟的设备:软驱什么的
- 半虚拟化设备:virtio的驱动
- 硬件直通:pci设备直接分配 (intel VT-d的支持)网卡,硬盘,USB设备,VGA显卡,设备直通,但是只允许一个客户机使用。
-
热插拔需要
- BIOS: qemu/kvm默认使用seaBIOS作为虚拟机的BIOS,支持pci的热插拔
- PCI总线:需要CPU的VT-d支持
- 操作系统
- 虚拟机中的驱动程序
-
动态迁移
分为静态迁移和动态迁移,也有人说冷迁移和热迁移。
静态迁移:
- 关闭客户机后复制镜像。问题:不能保存客户机中运行的工作负载,就是内存中的数据。
- 暂停客户机后,复制内存镜像到另外的机器中运行,可以保存迁移之前的内存状态和工作负载。
动态迁移
动态迁移是指在保证客户机服务的正常运行的同时,让虚拟机在不同的宿主机见迁移。一个成功的动态迁移,需要保证客户机的内存,硬盘,网络迁移之后保持不变,而且迁移过程的服务暂停时候应该很短或者没有。
动态迁移效率的衡量标准:
- 整体的迁移时间
- 服务暂停的时间
- 对运行中服务的影响
动态迁移的应用场景:
- 负载均衡:一台服务器过载时,将正在运行的虚拟机迁移负载较低的服务器上;
- 解除硬件依赖: 服务器需要升级的时候,将该服务器上的虚拟机迁移到别的机器以便升级;
- 节约能源:负载普遍过低的时候,集中服务以节省能源;
- 远程的迁移。
-
动态迁移的例子:基于共享存储的动态迁移。
如果源宿主机和迁移的目的宿主机共享存储,则只需传输vcpu状态,内存和虚拟内存中的设备状态到目的主机就可以,否则还需要传输存储。
考虑到平台:后备镜像使用的是共享存储,但是增量镜像不是共享的,因为这是由业务性质决定的,实验教学用的虚拟机不是提供长期和稳定的服务,将网络I/O替换为磁盘I/O提高了性能。
基于共享存储的动态迁移流程:- 虚拟机在源宿主机上正常运行,迁移开始;
- 虚拟机的内存页传输到目的宿主机上,同时QEMU/KVM记录下期间对已传输内存页的所有修改操作;
- 估计传输速度,当剩余内存可在一定时间周期内传输完成时(30s?),关闭源宿主机上的虚拟机,完成传输;
- 传输保存的内存页修改操作;
- 在目的宿主机上恢复虚拟机的运行状态。
服务暂停的时间:内存页传输时间+虚拟机临时停机时间+修改操作传输的时间+恢复启动的时间?
这个时间和网络带宽有很大关系,如果虚拟机内存页频繁修改,内存I/O速率大于网络I/O则动态迁移不可能完成。
-
KSM(共享内存) Kernel Samepage Merging 内存同页合并
KSM让内核检查正在运行的应用程序,并比较他们的内存,如果多个进程之间共享完全相同的内存页,就合并成一个单一的内存页,并标记为"copy on write",当由进程试图修改cow的内存页的时候,在复制出一个新的内存页。
在KVM/QEMU中,一个客户机就是一个QEMU进程,当多个虚拟机运行的是一个操作系统的时候,KSM的作用就会很显著。主要由两个方面:- 相同的内存合并,减少了客户机的内存使用量。
- 是内存过载使用的一种很好的实现方式。
但是,KSM需要由进程检测可以合并的内存页,虽然内存使用量降低,但是CPU的使用量会提高。1.2 安装 virtio 驱动
Linux 2.6 及以上版本内核都是支持 virtio 的。由于 virtio 的后端处理程序是在位于用户空间的 QEMU 中实现的,所以,在宿主机中只需要比较新的内核即可,不需要特别的编译与 virtio 相关的驱动。
1.2.1 Linux 中的 virtio 驱动
1
2
3
4
5
6
7
8
[root@192.168.118.14 ~]
#lsmod | egrep virt
virtio_balloon 13664 0
virtio_blk 18156 3
virtio_console 28114 0
virtio_net 28024 0
virtio_pci 22913 0
virtio_ring 21524 5 virtio_blk,virtio_net,virtio_pci,virtio_balloon,virtio_console
virtio 15008 5 virtio_blk,virtio_net,virtio_pci,virtio_balloon,virtio_console
其中,virtio,virtio_ring、virtio_pci 等驱动程序提供了对 virtio API 的基本支持,是使用任何 virtio 前端驱动都必须使用的,而且它们的加载还有一定的顺序,应该按照 virtio、virtio_ring、virtio_pci 的顺序加载,而 virtio_net、virtio_blk 这样的驱动可以根据实际需要进行选择性的编译和加载。
1.3 使用 virtio_balloon
(1)ballooning 简介
通常来说,要改变客户机占用的宿主机内存,要先关闭客户机,修改启动的内存配置,然后重启客户机才能实现。而内存的 ballooning (气球)技术可以在客户机运行时动态的调整它所占用的宿主机内存资源,而不需要关闭客户机。
ballooning 技术形象的在客户机占用的内存中引入气球(balloon)的概念。气球中的内存是可以供宿主机使用的,所以,当宿主机内存紧张,空余内存不多时,可以请求客户机回收利用已分配给客户机的部分内存,客户机就会释放其空闲的内存,此时若客户机空闲内存不足,可能还会回收部分使用中的内存,可能会将部分内存换出到客户机的交换分区(swap)中,从而使内存气球充气膨胀,进而使宿主机回收气球中的内存用于其他进程。反之,当客户机中内存不足时,也可以让宿主机的内存气球压缩,释放出内存气球中的部分内存,让客户机使用更多的内存。
目前很多虚拟机,如KVM 、Xen、VMware 等,都对 ballooning 技术提供了支持。
(2)KVM 中 ballooning 的原理及优劣势
KVM 中 ballooning 的工作过程主要有如下几步:
1. Hypervisor 发送请求到客户机操作系统让其归还一定数量的内存给 Hypervisor;
2. 客户机操作系统中 virtio_balloon 驱动接收到 Hypervisor;
3. virtio_balloon 驱动使客户机的内存气球膨胀,气球中的内存就不能被客户机访问。如果此时客户机中内存剩余量不多,并且不能让内存气球膨胀到足够大的以满足 Hypervisor 的请求,那么 virtio_balloon 驱动也会尽可能多的提供内存使气球膨胀,尽量去满足 Hypervisor 的请求中的内存数量;
4. 客户机操作系统归还气球中的内存给 Hypervisor;
5. Hypervisor 可以将从气球中得来的内存分配到任何需要的地方;
6. 即使从气球中得到的内存没有处于使用中,Hypervisor 也可以将内存返还给客户机中,这个过程为:Hypervisor 发请求到客户机的 virtio_balloon 驱动;这个请求使客户机操作系统压缩内存气球;在气球中的内存被释放出来,重新由客户机访问和使用。
ballooning 在节约内存和灵活分配内存方面有明显的优势:
1. 因为ballooning 能够被控制和监控,所以能够潜在的节约大量的内存;
2. ballooning 对内存的调节很灵活,即可以精细的请求少量内存,又可以粗旷的请求大量的内存;
3. Hypervisor 使用 ballooning 让客户机归还部分内存,从而缓解其内存压力。而且从气球中回收的内存也不要求一定要被分配给另外某个进程。
从另一方面来说,KVM 中 ballooning的使用不方便、不完善的地方也是存在的,其缺点如下:
1. ballooning 需要客户机操作系统加载 virt_balloon 驱动,然而并非每个客户机系统都有该驱动;2. 如果有大量内存需要从客户机系统中回收,那么 ballooning 可能会降低客户机操作系统运行的性能。一方面,内存的减少可能会让客户机中作为磁盘数据缓存的内存被放到气球中,从而使客户机中的磁盘I/O访问增加;另一方面,如果处理机制不够好,也可能让客户机中正在运行的进程由于内存不足而执行失败;
3. 目前没有比较方便的、自动化的机制来管理 ballooning,一般采用在 QEMU monitor 中执行 balloon 命令来实现 ballooning。没有对客户机的有效监控,没有自动化的ballooning 机制,这可能会使在生产环境中实现大规模自动化部署不是很方便。
4. 内存的动态增加或减少,可能会使内存被过度碎片化,从而降低内存使用时的性能。
(3)KVM 中 ballooning 使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
1. 创建虚拟机
[root@192.168.118.14 ~]
#qemu-kvm -smp 1 -m 1024 -balloon virtio /images/cirros-0.3.5-i386-disk.img --nographic
2. 查看 PCI 设备是否加载
# lspci -k
00:00.0 Class 0600: 8086:1237
00:01.0 Class 0601: 8086:7000
00:01.1 Class 0101: 8086:7010 ata_piix
00:01.3 Class 0680: 8086:7113
00:02.0 Class 0300: 1013:00b8
00:03.0 Class 0200: 8086:100e e1000
00:04.0 Class 00ff: 1af4:1002 virtio-pci
可以发现,00:04.0 Class 00ff: 1af4:1002 virtio-pci 就是 virtio-pci 设备
3. 通过 balloon 调整内存大小
原内存
# free -m
total used
free
shared buffers
Mem: 1001 12 988 0 0
-/+ buffers: 12 989
Swap: 0 0 0
使用 qemu-monitor 减少内存大小
(qemu) info balloon
balloon: actual=1024
(qemu) balloon 512
(qemu) info balloon
balloon: actual=512
4. 查看调整后的内存大小
# free -m
total used
free
shared buffers
Mem: 489 12 477 0 0
-/+ buffers: 12 234
Swap: 0 0 0
总的来说,对于 ballooning 技术,目前还没有完全成熟的管理控制工具,大规模部署非常不方便,而且性能没有很大的提升,建议慎用。
1.4 使用 virtio_net
在选择KVM中的网络设备时,一般来说优先选择半虚拟化的网络设备而不是模拟纯软件模拟的设备使用 virtio_net 半虚拟化驱动,可以提高网络吞吐量和降低网络延迟,从而让客户机中网络达到几乎和非虚拟化系统中使用原生网卡的网络差不多的性能。
(1)检查 QEMU 是否支持 virtio 类型的网卡
1
2
# qemu-kvm -smp 2 -m 512m -net nic,model=?
qemu: Supported NIC models: ne2k_pci,i82551,i82557b,i82559er,rtl8139,e1000,pcnet,virtio
(2)启动客户机时,指定分配 virtio 网卡设备
1
2
# qemu-kvm -smp 2 -m 512m \
-drive
file
=
/root/cirros-0
.3.5-i386-disk.img,
if
=virtio -net nic,model=virtio -net tap,name=tap0,script=no -daemonize
使用 virtio_net 依然是较低的性能,可以检查宿主机系统中对 GSO 和 TSO 特性的设置。关闭 GSO 和 TSO 可以使用半虚拟化网络驱动的性能更加优化。
1
2
ethtool
-K eno1 gso off
ethtool
-K eno1 tso off
用 vhost_net 后端驱动
前面提到 virtio 在宿主机中的后端处理程序(backend)一般是由用户空间的 QEMU 提供的,然而,如果对于网络 IO 请求的后端处理能够在内核空间来完成,则效率更高,会提高网络吞吐量和减少网络延迟。在比较新的内核中有一个叫做 “vhost_net”的驱动模块,它作为一个内核级别的后端处理程序,将 virtio-net的后端处理任务放到内核空间执行,从而提高效率。
1
2
3
4
5
qemu-kvm -smp 2 -m 512m \
-drive
file
=
/root/cirros-0
.3.5-i386-disk.img,
if
=virtio \
-net nic,model=virtio,macaddr=52:54:00:ac:0f:11 \
-net tap,name=tap0,vnet_hdr=on,vhost=on,script=no \
-daemonize
一般来说,使用 vhost-net 作为后端处理驱动可以提高网络的性能。不过,对于一些使用 vhost-net 作为后端的网络负载类型,可能使其性能不升反降。特别是从宿主机到其客户机之间的 UDP 流量,如果客户机处理接受数据的速度比宿主机发送的速度要慢,这时就容易出现性能下降。在这种情况下,使用 vhost-net 将会使 UDP socket 的接受缓冲区更快的溢出,从而导致更多的数据包丢失。因此在这种情况下不使用vhost-net,让传输速度稍慢一点,反而会提高整体的性能。
1.5 使用 virtio_blk
virtio_blk 驱动使用 virtio API 为客户机提供了一个高效访问块设备 I/O 方法。在 QEMU/KVM 中对块设备使用 virtio,需要在两方面进行配置:客户机中的前端驱动模块 virtio_blk 编译为内核模块,可以作为客户机直接使用 virtio_blk。
启动一个使用 virtio_blk 作为磁盘驱动的客户机,其 qemu-kvm 命令行如下:
1
[root@192.168.118.14 ~]
#qemu-kvm -smp 2 -m 512m -drive file=/images/cirros-0.3.5-i386-disk.img,if=virtio
1
2
3
4
5
6
7
8
9
10
11
12
未使用 virtio 模块的虚拟机
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 39.2M 0 disk
`-sda1 8:1 0 31.4M 0 part /
使用 virtio 模块的虚拟机
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
vda 253:0 0 39.2M 0 disk
`-vda1 253:1 0 31.4M 0 part /
2. 动态迁移
2.1 动态迁移概念
迁移(migration)包括系统整体的迁移和某个工作负载的迁移。系统整体迁移,是将系统上的所有软件完全复制到另一台物理硬件机器上。而工作负载的迁移,是将系统上的某个工作负载转移到另一台物理机上继续运行。服务器系统迁移的作用在于简化了系统维护管理,提高了系统负载均衡,增强了系统容错性并优化了系统电源管理。
在虚拟化环境中的迁移,又分为 静态迁移(static migration)和动态迁移(live migration) 静态迁移和动态迁移最大的区别:静态迁移有明显一段时间客户机中的服务不可用,而动态迁移则没有明显的服务暂停时间。
虚拟化中的静态迁移,也可以分为两种,一种是关闭客户机后,将其硬盘镜像复制到另一台宿主机上然后恢复启动起来,这种迁移不能保留客户机中运行的工作负载;另一种是两台宿主机共享存储系统,只需要暂停客户机后,复制其内存镜像到另一台宿主机中恢复启动,这种迁移可以保持客户机迁移前的内存状态和系统运行的工作负载。
动态迁移,是指在保证客户机上应用服务正常运行的同时,让客户机在不同的宿主机之间进行迁移,其逻辑步骤与前面静态迁移几乎一致,有硬盘存储和内存都复制的动态迁移,也有仅复制内存镜像的动态迁移。不同的是,为了保证迁移过程中客户机服务的可用性,迁移过程仅有非常短暂的停机时间。动态迁移允许系统管理员将客户机在不同物理机上迁移,同时不会断开访问客户机中服务的客户端或应用程序的连接。一个成功的动态迁移,需要保证客户机的内存、硬盘存储、网络连接在迁移到目的的主机后依然保持不变,而且迁移过程的服务暂停时间较短。
2.2 动态迁移的效率和应用场景
虚拟机迁移主要增强了系统的可维护性,其主要目标是在客户没有感觉的情况下,将客户机迁移到了另一台物理机上,并保证其各个服务都正常使用。可以从如下几个方面来衡量虚拟机的迁移的效率:
(1)整体迁移时间:从源主机中迁移操作开始到客户机被迁移到目的主机并恢复其服务所花费的时间;
(2)服务器停机时间:在迁移过程中,源主机和目的主机上客户机的服务都处于不可用状态的时间,此时源主机上客户机已暂停服务,目的主机上客户机还未恢复服务;
(3)对服务的性能影响:不仅包括迁移后的客户机中应用程序的性能与迁移前相比是否有所下降,还包括迁移后对目的主机上的其他服务的性能影响。
动态迁移的好处非常明显,动态迁移的几个场景:
(1)负载均衡:当一台物理服务器的负载较高时,可以将其上运行的客户机动态迁移到负载较低的宿主机服务器中,以保证客户机的服务质量(QoS)。
(2)解除硬件依赖:升级主机,添加某些硬件设备时,可以将宿主机上运行的客户机非常安全高效的动态迁移到其他宿主机上。
(3)节约能源:通过动态迁移将宿主机上的客户机集中迁移到其中几台服务器上。
(4)实现客户机地理位置上的远程迁移:比如跨地域性的迁移。
2.3 KVM 动态迁移原理与实践
在KVM 中,即支持离线的静态迁移,又支持在线的动态迁移。对于静态迁移,可以在源宿主机上某客户机的 QEMU monitor中,用 "savevm my_tag" 命令来保存一个完整的客户机镜像快照(标记为 my_tag),然后在源宿主机中关闭或暂停该客户机,然后将该客户机的镜像文件复制到另外一台宿主机中,用于源主机中启动客户机时以相同的命令启动复制过来的镜像,在其 QEMU monitor 中用 "loadvm my_tag" 命令来恢复刚才保存的快照即可完全加载保存快照时的客户机状态。这里的 "savevm" 命令保存的完整客户机状态包括:CPU 状态、内存、设备状态、可写磁盘中的内容。注意:这种保存快照的方法需要 qcow2、qed 等格式的磁盘镜像文件,因为只有它们才支持快照这个特性。
如果源宿主机和目的宿主机共享存储系统,则只需要通过网络发送客户机的vCPU 执行状态、内存中的内容、虚拟设备的状态到目的主机上。否则,还需要将客户机的磁盘存储发送到目的主机上去。
在不考虑磁盘存储复制的情况下(基于共享存储系统),KVM 动态迁移的具体迁移过程为:在客户机动态迁移开始后,客户机依然在原宿主机上运行,与此同时,客户机的内存页被传输到目的主机之上。QEMU/KVM 会监控并记录下迁移过程中所有已被传输的内存页的任何修改,并在所有的内存页都被传输完成后即开始传输在前面过程中内存页的更改内容。QEMU/KVM 也会估计迁移过程中的传输速度,当剩余的内存数据量能够在一个可设定的时间周期内传输完成之时,QEMU/KVM 将会关闭源宿主机上的客户机,再将剩余的数据量传输到目的主机上去,最后传输过来的内存内容在目的宿主机上恢复客户机的运行状态。至此,KVM的一个动态迁移操作就完成了。
当客户机中内存使用量非常大且修改频繁,内存中数据被不断修改的速度大于 KVM 能够传输的内存速度之时,动态迁移过程是不会完成的,这时要进行迁移只能进行静态迁移。
对于 KVM 动态迁移,有如下几点建议和注意事项:
(1)源宿主机和目的宿主机之间尽量用网络共享的存储系统来保存客户机镜像,尽管KVM动态迁移也支持连通磁盘镜像一起复制。共享存储(如NFS)在源宿主机和目的宿主机上的挂载位置必须一致;
(2)为了提高动态迁移的成功率,尽量在同类型 CPU 的主机上面进行动态迁移;
(3)64 位的客户机只能在 64位宿主机之间迁移,而 32 位客户机可以在 32 位宿主机和 64 位宿主机之间迁移;
(4)动态迁移的源宿主机和目的宿主机对 NX 位的设置是相同的,要么关闭状态,要么打开状态;
(5)在进行动态迁移时,被迁移客户机的名称是唯一的,在目的宿主机上不能与源宿主机中被迁移客户机同名的客户机存在;
(6)目的宿主机和源宿主机的软件配置尽可能是相同,例如,为了保证动态迁移后客户机中网络依然正常工作,需要在目的宿主机上配置和源宿主机相同名称网桥,并让客户机以桥接的方式使用网络。
示例:使用 qemu-kvm 动态迁移
NFS 主机配置:
1
2
3
4
5
6
7
8
[root@192.168.118.16 ~]
#mkdir /images
[root@192.168.118.16 ~]
#mv cirros-0.3.5-i386-disk.img /images/
[root@192.168.118.16 ~]
#yum install nfs-utils -y
[root@192.168.118.16 ~]
#cat /etc/exports
/images
*(rw,async,no_root_squash)
[root@192.168.118.16 ~]
#systemctl start rpcbind
[root@192.168.118.16 ~]
#systemctl start nfs-server
源宿主机配置:
1
2
3
[root@192.168.118.14 ~]
#yum install nfs-utils -y
[root@192.168.118.14 ~]
#mount 192.168.118.16:/images /images/
[root@192.168.118.14 ~]
#qemu-kvm -smp 2 -m 512m /images/cirros-0.3.5-i386-disk.img -monitor stdio
目的迁移主机操作如下:
1
2
3
[root@192.168.118.15 ~]
#yum install nfs-utils -y
[root@192.168.118.15 ~]
#mount 192.168.118.16:/images /images/
[root@192.168.118.15 ~]
#qemu-kvm -smp 2 -m 512m /images/cirros-0.3.5-i386-disk.img -incoming tcp:0:6666
通过 tigervnc 连接:
到此,在源宿主机执行:
1
(qemu) migrate -d tcp:192.168.118.15:6666
再次查看,被迁移的主机:
被迁移主机完成后,也存在运行top命令,迁移完成。
QEMU/KVM 中也支持增量复制磁盘修改部分数据(使用相同的后端镜像时)的动态迁移,以及直接复制整个客户机磁盘镜像的动态迁移。使用相同后端镜像文件的动态迁移过程如下,与前面直接使用NFS共享存储非常类似。
(1)在源宿主机上,根据一个后端镜像文件,创建一个 qcow2 格式的镜像文件,并启动客户机,命令如下:
1
2
[root@192.168.118.14
/images
]
#qemu-img create -f qcow2 -o backing_file=/images/cirros-0.3.5-i386-disk.img,size=20G kvm1.qcow2
[root@192.168.118.14 ~]
#qemu-kvm -smp 2 -m 512m /root/kvm1.qcow2 -monitor stdio
(2)在目的宿主机上,也建立相同的 qcow2 格式的客户机镜像,并带有 "-incoming" 参数来启动客户机使其处于迁移监听状态:
1
2
[root@192.168.118.15 ~]
#qemu-img create -f qcow2 -o backing_file=/images/cirros-0.3.5-i386-disk.img,size=20G kvm1.qcow2
[root@192.168.118.15 ~]
#qemu-kvm -smp 2 -m 512m /root/kvm1.qcow2 -incoming tcp:0:6666
(3)在源宿主机上的客户机的 QEMU monitor 中,运行 "migrate -i tcp:192.168.118.15:6666"
1
2
注意:有些 qemu-kvm 并不支持 -i 增量迁移,可以直接使用:migrate tcp:192.168.118.15:6666
(qemu) migrate tcp:192.168.118.15:6666
至此,基于相同后端镜像的磁盘动态迁移就完成,在目的宿主机上可以看到迁移过来的客户机已经处于和源客户机一样的状态。
3. KSM 技术
在现代操作系统中,共享内存是一个很普遍应用的概念。如在 Linux 系统中,当使用 fork 函数创建一个进程时,子进程与父进程共享全部的内存,而当子进程或父进程试图修改它们的共享内存区域时,内核会分配一块新的内存区域,并将试图修改的共享内存区域复制到新的内存区域上,然后让进程去修改复制的内存。这就是著名的“写时复制”(copy-on-write)技术。而 KSM技术却与这种内存共享概念有点相反。
KSM 是“Kernel SamePage Merging”缩写,中文可称为“内核同页合并”。KSM 允许内核在两个或多个进程之间共享完全相同的内存页。KSM 让内核扫描检查正在运行中的程序并比较它们的内存,如果发现它们有内存区域或内存页是完全相同的,就将相同的内存合并为一个单一的内存页,并将其标识为“写时复制”。这样可以起到节省系统内存使用量的作用。之后,如果有进程试图去修改被标识为“写时复制”的合并的内存页时,就为该进程复制出一个新的内存页供其使用。
在 QEMU/KVM 中,一个虚拟客户机就是一个 QEMU 进程,所以使用 KSM 也可以实现多个客户机之间的相同内存合并。而且,如果在同一宿主机上的多个客户机运行的是相同的操作系统或应用程序,则客户机之间的相同内存页数量就可能还比较大,这种情况下KSM的作用就更加显著。在KVM环境下使用 KSM ,KSM还允许KVM 请求哪些相同的内存页是可以被共享合并的,所以KSM只会识别并合并哪些不会干扰客户机运行,不会影响宿主机或客户机的安全内存页。可见,在KVM 虚拟化环境中,KSM 能够提高内存的速度和使用效率,具体可以从以下两个方面来理解:
(1)在 KSM 的帮助下,相同的内存页被合并了,减少了客户机的内存使用量,一方面,内存中的内存更容易被保存到CPU 的缓存中;另一方面,有更多的内存可用于缓存一些磁盘中的数据。因此,不管是内存的缓存命中率,还是磁盘数据的缓存命中率都会提高,从而提高了 KVM 客户机中操作系统或应用的运行速度;
(2)KSM 是内存过载使用的一种较好的方式。KSM 通过减少每个客户机实际占用的内存数,就可以让多个客户机分配的内存数量之和大于物理内存数量。而对于使用相同的内存量的客户机,在物理内存量不变的情况下,可以在一个宿主机中创建更多客户机,提高了虚拟化客户机部署的密度,提高了物理资源的利用效率。KSM 是在 Linux 内核 2.6中被加入到内核主代码中去的,目前多数流行的 Linux 发行版都已经将 KSM 的支持编译到内核中了,其内核配置文件中有 “CONFIG_KSM=y” 项:
1
2
[root@192.168.118.14 ~]
#egrep -i ksm /boot/config-3.10.0-327.el7.x86_64
CONFIG_KSM=y
Linux 系统的内核进程 ksmd 负责扫描后合并进程的相同内存页,从而实现 KSM 功能。root 用户可以通过 "/sys/kernel/mm/ksm/" 目录下的文件来配置和监控 ksmd 这个守护进程。KSM 只会去扫描和试图合并那些应用程序建议为可合并的内存页。
KSM 最初就是为 KVM 虚拟化中的使用而开发的,不过它对非虚拟化的系统依然非常有用。KSM 可以在KVM 虚拟化环境中非常有效的降低内存使用量,在KSM的帮助下,有人在物理内存为 16GB 的机器上,用KVM 成功运行了多达 52 个 1GB 内存的windows xp 客户机。
由于KSM 对 KVM 宿主机中的内存使用有较大的效率和性能的提高,所以一般建议打开 KSM 功能。,可是,由于KSM必须有一个或多个进程去检测和找出哪些内存页是完全相同可以用于合并的。因此,KSM 让内存使用量降低了,但是 CPU 使用率会有一定程度的升高,也可能会带来隐蔽的性能问题,需要在实际使用环境中进行适当配置 KSM 的使用,以便达到较好的平衡。
KSM对内存合并而节省内存的数量与客户机操作系统及其上运行的应用程序有关,如果宿主机上的客户操作系统及其上的应用程序也类似,节省内存的效果就会很显著,甚至节省超过 50%的内存都有可能的。反之,如果客户机操作系统不同,且运行的应用程序也大不相同,KSM 节省内存效率不好,可能连 5%都不到。
另外,在使用 KSM 实现内存过载使用时,最好保证系统的交换空间(swap space)足够大,因为 KSM 将不同客户机的相同内存页合并而减少了内存使用量,但是客户机可能由于需要修改被KSM合并的内存页,从而使这些被修改的内存被重新复制出来占用内存空间,因此可能会导致系统内存不足,这是需要足够的交换空间来保证系统的正常运行。
3.1 KSM 操作实践
内核 KSM 守护进程是 ksmd,配置和监控 ksmd 的文件在 "/sys/kernel/mm/ksm/" 目录下:
1
2
3
4
5
6
7
8
9
10
11
[root@192.168.118.14 ~]
#ll /sys/kernel/mm/ksm/
total 0
-r--r--r--. 1 root root 4096 Jul 9 08:21 full_scans
-rw-r--r--. 1 root root 4096 Jul 9 08:21 merge_across_nodes
-r--r--r--. 1 root root 4096 Jul 9 08:21 pages_shared
-r--r--r--. 1 root root 4096 Jul 9 08:21 pages_sharing
-rw-r--r--. 1 root root 4096 Jul 9 08:21 pages_to_scan
-r--r--r--. 1 root root 4096 Jul 9 08:21 pages_unshared
-r--r--r--. 1 root root 4096 Jul 9 08:21 pages_volatile
-rw-r--r--. 1 root root 4096 Jul 9 08:21 run
-rw-r--r--. 1 root root 4096 Jul 9 08:21 sleep_millisecs
说明:
full_scans:记录已经对所有可合并的内存区域扫描过的次数;
pages_shared:记录着正在使用中的共享内存页的数量;
pages_sharing:记录着有多少数量的内存页正在使用被合并的共享页,不包括合并的内存页本身。这就是实际节省的内存页数量;
pages_unshared:记录了守护进程去检查并试图合并,却发现了并没有重复内容而不能被合并的内存页数量;
pages_volatile:记录了因为其内容很容易变化而不被合并的内存页;
pages_to_scan:在KSM进程休眠之前会去扫描的内存数量;
sleep_millisecs:ksmd 进程休眠的时间(单位:毫秒),ksmd 的两次运行之间的间隔;
run:控制 ksmd 进程是否运行的参数,默认值为0,要激活 ksm 必须要设置其参数为 1,设置为0,表示停止运行 ksmd 但保持它已经合并的内存页;设置为1,
表示马上运行 ksmd进程;设置为2 表示停止运行 ksmd,并分离已经合并的所有内存页,但是保持已经注册为合并的内存区域给下一次运行使用.通过权限可以看出, 只有 pages_to_scan , sleep_millisecs, run 这 3 个文件对 root 用户是可读可写的,其余 5 个文件都是 只读的,可以向 pages_to_scan 、 sleep_millisecs、 run 这三个文件中写入自定义的值以便控制 ksmd的运行。
例如:
1
2
3
echo
1200 >
/sys/kernel/mm/ksm/pages_to_scan
用来调整每次扫描的内存页数量
echo
10 >
/sys/kernel/mm/ksm/sleep_millisecs
用来设置ksmd两次运行的时间间隔
echo
1 >
/sys/kernel/mm/ksm/run
用来激活ksmd 的运行
pages_sharing 的值越大,说明 KSM 节省的内存越多,KSM 效果越好,如下命令计算了节省的内存数量:
1
2
[root@mongodb ~]
# echo "KSM saved: $(($(cat /sys/kernel/mm/ksm/pages_sharing) * $(getconf PAGESIZE) / 1024 / 1024))MB"
KSM saved: 1159MB
在 CentOS 7 系统中,提供了两个服务 ksm 和 ksmtuned 来动态调节 KSM 的运行情况,这两个服务都包含在 qemu-kvm-common 这个 RPM 安装包中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@192.168.118.14 ~]
#systemctl status ksm
● ksm.service - Kernel Samepage Merging
Loaded: loaded (
/usr/lib/systemd/system/ksm
.service; enabled; vendor preset: enabled)
Active: active (exited) since Tue 2019-07-09 09:59:40 CST; 21s ago
Process: 668 ExecStart=
/usr/libexec/ksmctl
start (code=exited, status=0
/SUCCESS
)
Main PID: 668 (code=exited, status=0
/SUCCESS
)
CGroup:
/system
.slice
/ksm
.service
Jul 09 09:59:40 localhost.localdomain systemd[1]: Starting Kernel Samepage Merging...
Jul 09 09:59:40 localhost.localdomain systemd[1]: Started Kernel Samepage Merging.
[root@192.168.118.14 ~]
#systemctl status ksmtuned
● ksmtuned.service - Kernel Samepage Merging (KSM) Tuning Daemon
Loaded: loaded (
/usr/lib/systemd/system/ksmtuned
.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2019-07-09 09:59:40 CST; 25s ago
Process: 680 ExecStart=
/usr/sbin/ksmtuned
(code=exited, status=0
/SUCCESS
)
Main PID: 685 (ksmtuned)
CGroup:
/system
.slice
/ksmtuned
.service
├─685
/bin/bash
/usr/sbin/ksmtuned
└─686
sleep
60
Jul 09 09:59:40 localhost.localdomain systemd[1]: Starting Kernel Samepage Merging (KSM) Tuning Daemon...
Jul 09 09:59:40 localhost.localdomain systemd[1]: Started Kernel Samepage Merging (KSM) Tuning Daemon.
当 ksm 服务启动时: run 文件为 1
1
2
[root@192.168.118.14 ~]
#cat /sys/kernel/mm/ksm/run
1
在 KSM 服务启动后,KSM 能够共享最多达到系统物理内存一半的内存页。而 ksmtuned 服务一直保持循环执行,以调用 ksm 服务的运行,其配置文件的 /etc/ksmtuned.conf ,默认配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@192.168.118.14 ~]
#cat /etc/ksmtuned.conf
# Configuration file for ksmtuned.
# How long ksmtuned should sleep between tuning adjustments
# KSM_MONITOR_INTERVAL=60
# Millisecond sleep between ksm scans for 16Gb server.
# Smaller servers sleep more, bigger sleep less.
# KSM_SLEEP_MSEC=10
# KSM_NPAGES_BOOST=300
# KSM_NPAGES_DECAY=-50
# KSM_NPAGES_MIN=64
# KSM_NPAGES_MAX=1250
# KSM_THRES_COEF=20
# KSM_THRES_CONST=2048
# uncomment the following if you want ksmtuned debug info
# LOGFILE=/var/log/ksmtuned
# DEBUG=1
在主机内存为 8 GB 的系统上,使用 Linux 3.10 内核的 CentOS 7 系统作为宿主机,开始将 ksm 和 ksmtuned 服务暂停,"/sys/kernel/mm/ksm/run" 的默认值为 0 ,KSM 不生效,然后启动每个内存为 1GB 的 4 个 cirros 客户机,再次启动 KSM 和 ksmtuned服务,5分钟后检查系统内存的使用情况以确定 KSM 效果。脚本及执行如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[root@192.168.118.14 ~]
#cat test-ksm.sh
#!/bin/bash
echo
"stop ksm"
systemctl stop ksm
systemctl stop ksmtuned
echo
"---free -m---"
free
-m
for
i
in
{1..3};
do
qemu-img create -f qcow2 -o backing_file=
"/images/cirros-0.3.5-i386-disk.img"
/images/cirros-
${i}.qcow2
echo
"starting the No. ${i} guest..."
qemu-kvm -smp 1 -m 1024m
/images/cirros-
${i}.qcow2 -daemonize
sleep
20
done
echo
"---free -m---"
sleep
10
free
-m
echo
"---starting services: ksm and ksmtuned..."
systemctl start ksm
systemctl start ksmtuned
sleep
300
echo
"---free -m command output with ksm and ksmtuned running."
free
-m
echo
"KSM saved: $(($(cat /sys/kernel/mm/ksm/pages_sharing) * $(getconf PAGESIZE) / 1024 / 1024))MB"
*****************执行如下*****************
[root@192.168.118.14 ~]
#./test-ksm.sh
stop ksm
---
free
-m---
total used
free
shared buff
/cache
available
Mem: 7823 115 7565 8 142 7537
Swap: 8063 0 8063
Formatting
'/images/cirros-1.qcow2'
,
fmt
=qcow2 size=41126400 backing_file=
'/images/cirros-0.3.5-i386-disk.img'
encryption=off cluster_size=65536 lazy_refcounts=off
starting the No. 1 guest...
VNC server running on `::1:5900'
Formatting
'/images/cirros-2.qcow2'
,
fmt
=qcow2 size=41126400 backing_file=
'/images/cirros-0.3.5-i386-disk.img'
encryption=off cluster_size=65536 lazy_refcounts=off
starting the No. 2 guest...
VNC server running on `::1:5901'
Formatting
'/images/cirros-3.qcow2'
,
fmt
=qcow2 size=41126400 backing_file=
'/images/cirros-0.3.5-i386-disk.img'
encryption=off cluster_size=65536 lazy_refcounts=off
starting the No. 3 guest...
VNC server running on `::1:5902'
---
free
-m---
total used
free
shared buff
/cache
available
Mem: 7823 368 7266 8 188 7261
Swap: 8063 0 8063
---starting services: ksm and ksmtuned...
---
free
-m
command
output with ksm and ksmtuned running.
total used
free
shared buff
/cache
available
Mem: 7823 248 7382 8 191 7377
Swap: 8063 0 8063
KSM saved: 121MB
以上输出中,从 KSM、ksmtuned 服务开始运行之前和之后的 "free -m" 命令看出 used 从 368 降低到 248,明显节约了系统内存。