• [ kvm ] 学习笔记 4:KVM 高级功能详解


    1. 半虚拟化驱动

    1.1 virtio 概述

    KVM 是必须使用硬件虚拟化辅助技术(如 Intel VT-x 、AMD-V)的 Hypervisor,在CPU 运行效率方面有硬件支持,其效率是比较高的;在有 Intel EPT 特性支持的平台上,内存虚拟化的效率也较高。 QEMU/KVM 提供了全虚拟化环境,可以让客户机不经过任何修改就能运行在 KVM 环境中。不过 KVM 在 I/O虚拟化方面,传统的方式是使用 QEMU 纯软件的方式来模拟 I/O 设备(如网卡、磁盘、显卡等),其效率并不非常高。

      CPU 和 内存的虚拟化由KVM内核模块提供,I/O设备虚拟化由QEMU负责实现。

    在KVM中,可以在客户机中使用半虚拟化驱动的方式是采用 Virtio 这个 Linux 上的设备驱动标准框架。

    完全虚拟化:GuestOS 运行在物理机上的 Hypervisor 之上,GuestOS 并不知道它已经被虚拟化,并不需要任何修改就能工作;

    半虚拟化:GuestOS 不仅知道它运行在 Hypervisor 之上,还包括让 GuestOS 更高效的过度到 Hypervisor 的代码。

    在完全虚拟化模式中,hypervisor 必须模拟设备硬件,它是在会话的最低级别进行模拟的,尽管在该抽象中模拟很干净,但它同时也是最低效的,最复杂的。

    在半虚拟化中,GuestOS 和 hypervisor 能够共同合作,让模拟更加高效,缺点是操作系统知道它被虚拟化,并且需要修改才能工作。

    左图在传统的完全虚拟化环境中,hypervisor必须捕捉这些请求,然后模拟物理硬件的行为。尽管这也做提供很大的灵活性(即运行未更改的操作系统),但它的效率比较低.

    右图,半虚拟化,来宾操作系统知道它运行在hypervisor之上,并包含了充当当前的驱动程序.hypervisor为特定的设备模拟实现后端驱动程序.通过在这些前端和后端驱动程序中的virtio,为开发模拟设备提供标准化接口,从而增加代码的跨平台重用率并提高效率.

    QEMU模拟 I/O 设备基本原理和优缺点

    使用QEMU模拟 I/O 的情况下:

      (1)当客户机中的设备驱动程序(device driver)发起 I/O 操作请求时,KVM模块中的 I/O 操作捕获代码会拦截这次 I/O 请求;

      (2)经过处理后将本次 I/O 请求的信息存放到 I/O 共享页,并通知用户控件中的 QEMU 程序;

      (3)QEMU 模拟程序获得 I/O 操作的具体信息之后,交由硬件模拟代码来模拟出本次的 I/O 操作;

      (4)完成之后,将结果放回到 I/O 共享页,并通知KVM 模块中的 I/O 操作捕获代码;

      (5)由 KVM 模块中的捕获代码读取 I/O 共享页中的操作结果,并把结果返回到客户机中。

    在这个过程中,QEMU进程在等待I/O时被阻塞,当客户机通过 DMA 访问大块 I/O时,QEMU模拟程序将不会把操作结果放到I/O共享页中,而是通过内存映射的方式将结果直接写到客户机的内存中去,然后通过KVM模块告诉客户机DMA操作已经完成。

    QEMU 模拟 I/O 设备的方式,其优点是可以通过软件模拟出各种各样的硬件设备,包括一些不常用的或者很老很经典的设备,而且它不用修改客户机操作系统,就可以实现模拟设备在客户机中正常工作。 在KVM客户机中使用这种方式,对于解决手上没有足够设备的软件开发及调试有非常大的好处。而它的缺点是,每次 I/O 操作的路径比较长,有较多的 VMEntry、VMExit发生,需要多次上下文切换(context switch),也需要多次数据复制,所以它的性能较差。

    半虚拟化 virtio 的基本原理和优缺点

    其中前端驱动(frondend,如 virtio-blk、virtio-net等)是在客户机中存在的驱动程序模块,而后端处理程序(backend)是在 QEMU中实现的。

    在这前后端驱动之间,还定义了两层来支持客户机与 QEMU 之间的通信:

      virtio:虚拟队列接口,在概念上将前端驱动程序附加到后端处理程序,一个前端驱动程序可以使用 0 个或 多个队列,具体数据取决于需求;

      例如:virtio-net 网络驱动程序使用两个虚拟队列(一个用于接收、另一个用于发送),而virtio-blk块驱动程序仅使用一个虚拟队列。虚拟队列实际上被实现为跨越客户机操作系统和hypervisor的衔接点,但它可以通过任意方式实现,前提是客户机操作系统和virtio后端程序都遵循一定的标准,以相互匹配的方式实现它。

      virtio-ring:实现了环形缓冲区(ring buffer),用于保存前端驱动和后端处理程序执行的信息,并且它们可以一次性保存前端驱动的多次I/O请求,并且交由后端去批量处理,最后实际调用宿主机中设备驱动实现物理上的I/O操作,这样做就可以根据约定实现批量处理而不是客户机中每次I/O请求都需要处理一次,从而提高客户机与hypervisor信息交换的效率。

    virtio 半虚拟化驱动的方式,可以获得很好的I/O性能,其性能几乎可以达到和 native(非虚拟化环境中的原生系统)差不多的I/O性能。所以,在使用 kvm 时,如果宿主机内核和客户机都支持 virtio的情况下,一般推荐使用 virtio 达到更好的性能。

    virtio 缺点:

      必须要安装特定的virtio驱动使其知道是运行在虚拟化环境中,且按照 virtio 的规定格式进行数据传输,不过客户机中可能有一些老的Linux系统不支持virtio 和主流windows系统需要安装特定的驱动才支持 virtio,不过,较新的一些Linux发行版(如RHEL 6.3、Fedora 17等)默认都将virtio相关驱动编译为模块,可直接作为客户机使用virtio,而且对于主流Windows系统都有对应的virtio驱动程序可供下载使用。

    virtio是对半虚拟化hypervisor中的一组通用模拟设备的抽象.该设置还允许hypervisor导出一组通用的模拟设备,并通过一个通用的应用程序接口(API)让它们变得可用.有了半虚拟化hypervisor之后,来宾操作系统能够实现一组通用的接口,在一组后端驱动程序之后采用特定的设备模拟.后端驱动程序不需要是通用的,因为它们只实现前端所需的行为.

    注意,在现实中(尽管不需要),设备模拟发生在使用 QEMU 的空间,因此后端驱动程序与 hypervisor 的用户空间交互,以通过 QEMU 为 I/O 提供便利。QEMU 是一个系统模拟器,它不仅提供来宾操作系统虚拟化平台,还提供整个系统(PCI 主机控制器、磁盘、网络、视频硬件、USB 控制器和其他硬件元素)的模拟。   

    1.2 安装 virtio 驱动

    Linux 2.6 及以上版本内核都是支持 virtio 的。由于 virtio 的后端处理程序是在位于用户空间的 QEMU 中实现的,所以,在宿主机中只需要比较新的内核即可,不需要特别的编译与 virtio 相关的驱动。

    1.2.1 Linux 中的 virtio 驱动

    [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. 创建虚拟机
    [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 类型的网卡

    # qemu-kvm -smp 2 -m 512m -net nic,model=?
    qemu: Supported NIC models: ne2k_pci,i82551,i82557b,i82559er,rtl8139,e1000,pcnet,virtio
    

    (2)启动客户机时,指定分配 virtio 网卡设备

    # 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 可以使用半虚拟化网络驱动的性能更加优化。

    ethtool -K eno1 gso off
    ethtool -K eno1 tso off
    

    用 vhost_net 后端驱动

    前面提到 virtio 在宿主机中的后端处理程序(backend)一般是由用户空间的 QEMU 提供的,然而,如果对于网络 IO 请求的后端处理能够在内核空间来完成,则效率更高,会提高网络吞吐量和减少网络延迟。在比较新的内核中有一个叫做 “vhost_net”的驱动模块,它作为一个内核级别的后端处理程序,将 virtio-net的后端处理任务放到内核空间执行,从而提高效率。

    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 命令行如下:

    [root@192.168.118.14 ~]#qemu-kvm -smp 2 -m 512m -drive file=/images/cirros-0.3.5-i386-disk.img,if=virtio
    
    未使用 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 主机配置:

    [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
    

    源宿主机配置:

    [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
    

    目的迁移主机操作如下:

    [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 连接:

    到此,在源宿主机执行:

    (qemu) migrate -d tcp:192.168.118.15:6666
    

    再次查看,被迁移的主机:

    被迁移主机完成后,也存在运行top命令,迁移完成。

    QEMU/KVM 中也支持增量复制磁盘修改部分数据(使用相同的后端镜像时)的动态迁移,以及直接复制整个客户机磁盘镜像的动态迁移。使用相同后端镜像文件的动态迁移过程如下,与前面直接使用NFS共享存储非常类似。

    (1)在源宿主机上,根据一个后端镜像文件,创建一个 qcow2 格式的镜像文件,并启动客户机,命令如下:

    [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" 参数来启动客户机使其处于迁移监听状态:

    [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"

    注意:有些 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” 项:

    [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/" 目录下:

    [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的运行。

    例如:

    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 效果越好,如下命令计算了节省的内存数量:

    [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 安装包中。

    [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

    [root@192.168.118.14 ~]#cat /sys/kernel/mm/ksm/run 
    1
    

    在 KSM 服务启动后,KSM 能够共享最多达到系统物理内存一半的内存页。而 ksmtuned 服务一直保持循环执行,以调用 ksm 服务的运行,其配置文件的 /etc/ksmtuned.conf ,默认配置如下:

    [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 效果。脚本及执行如下:

    [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,明显节约了系统内存。

  • 相关阅读:
    Ruby--Array
    SQL--查询相同字段的数据
    Ruby--CSV
    Ruby--String
    Git常用命令
    JS Date函数操作
    FTP命令
    Rails--%w用法[转]
    Rails--export csv
    Mysql远程访问
  • 原文地址:https://www.cnblogs.com/hukey/p/11152860.html
Copyright © 2020-2023  润新知