一.虚拟机资源Qos

虚拟化资源Qos在云计算场景中有着很重要的作用,可以支持虚拟机的弹性负载,提高硬件复用率,减少资源浪费, 降低企业成本等。虚拟机的资源Qos包括CPU,memory,IO,网络几个方面

二.CPU Qos

CPU Qos控制包括CPU 热插拔CPU 份额CPU 配额

1.CPU 热插拔

cpu热插拔可以帮助虚拟机在高负载的情况下,不用停机直接通过热插拔cpu的方式为虚拟机进行弹性扩展,保证虚拟机里的业务稳定,虚拟化场景中配置cpu支持热插拔的xml格式如下:

1
2
3
4
5
6
7
8
9
<vcpu placement='static' current='3'>6</vcpu>
<vcpus>
<vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>
<vcpu id='1' enabled='no' hotpluggable='yes'/>
<vcpu id='2' enabled='no' hotpluggable='yes'/>
<vcpu id='3' enabled='no' hotpluggable='yes'/>
<vcpu id='4' enabled='yes' hotpluggable='yes' order='2'/>
<vcpu id='5' enabled='yes' hotpluggable='yes' order='3'/>
</vcpus>

vcpu元素中的current属性表示当前可以使用cpu的数量,6则表示支持的最大的cpu数量;
vcpus元素中则是详细描述了每个cpu的控制状态:id值代表每个vcpu的id,这个值会在libvirt中被用作vcpu pining、调度信息以及numa分配,在某些情况下这个值可能和虚拟机看到的id不同,默认vcpu id从0开始;enabled表示在虚拟机启动的时候哪些cpu为随机启动,vcpu 0默认enabled=’yes’(虚拟机启动至少要有一个vcpu);hotpluggable表示当前vcpu是否允许热插拔,vcpu 0默认不支持热插拔; order代表vcpu在虚拟机中online的顺序(hypervisor可能会在某些操作中为了确保有效的配置而会清楚或更新order),其中vcpu的启动必须在vcpu0之后。
另外不是所有的hypervisor都支持cpu热插拔,还有的像PPC64这种平台的cpu热插,如果vcpu都在一个core上,则必须都需要enabled

2.CPU份额

CPU份额表示进程在调度器中所占的权重,在虚拟化场景中,当CPU资源紧张时,可以为不同类型的虚拟机配置不同的CPU份额,例如,为一些承载关键业务的虚拟机配置高份额,保证其CPU资源的稳定。CPU配额libvirt中配置如下:

1
2
3
<cputune>
<shares>2048</shares>
</cputune>

在linux内核中,默认所使用的进程调度器为cfs,cfs调度器会根据cgroup的cpu子系统下的每个cgroup子组的cpu.shares计算进程的weight值,cpu.shares的值是一个相对值,cfs是根据比例计算,默认所有进程的权重值是一样的(默认值1024),即所有进程的权重比例为1:1:1…,kvm中,libvirtd进程启动后会在cpu子系统中建立一个名为machine.slice的文件夹(即代表一个子组),之后在创建虚拟机的时候会在machine.slice下为虚拟机创建一个machine-qemu开头的文件夹,然后在这个文件夹下会为每个vcpu创建一个文件夹(会将每个vcpu线程加入对应的文件夹下的task),同时还会创建一个emulator文件夹(将qemu中除了vcpu线程的其他线程加入到这个文件夹下的task中),另外libvirt在创建虚拟机的时候支持为不同的虚拟机配置不同的cpu share值,这就决定了cfs在调度的时候通过获取每个虚拟机子组中cpu.shares值计算虚拟机的调度优先级,上面xml中的shares值会写进为虚拟机创建machine-qemu开头文件夹下的cpu.shares里,vcpu与emulator的cpu.shares都是默认值1024,即qemu中所有线程的调度权重相等,但不同的qemu进程会由于shares值不同,导致在调度器的权重不同。

3.CPU配额

CPU配额表示限制cpu使用率,通过为不同的虚拟机配置不同的配额,可以防止虚拟机资源消耗过多,同时也可以间接保证一些关键虚拟机的CPU资源。在libvirt中支持三中配置: vcpu配置配额,为整个虚拟机配置配额(即为整个qemu进程配置配额),为io线程配置配额,CPU配额在libvirt中配置如下:

1
2
3
4
5
6
7
8
9
10
<cputune>
<period>1000000</period>
<quota>-1</quota>
<global_period>1000000</global_period>
<global_quota>-1</global_quota>
<iothread_period>1000000</iothread_period>
<iothread_quota>-1</iothread_quota>
<emulator_period>1000000</emulator_period>
<emulator_quota>-1</emulator_quota>
</cputune>

在cgroup的cpu子系统下面有cpu.cfs_period_us和cpu.cfs_quota_us两个文件两个文件,cpu.cfs_period_us代表cpu分配的周期(微秒),默认为100000。cpu.cfs_quota_us表示该control group限制占用的时间(微秒),默认为-1,表示不限制。如果设为50000,表示占用50000/10000=50%的CPU。上述配置中的period/quota代表的是配置vcpu的配额,global_period/global_quota代表的是配置qemu整个进程(包括所有子线程)的配额,iothread_period/iothread_quota代表的是配置io线程的配额,libvirt支持配置iothread,iothread的作用是增加qemu中设备io的处理能力,emulator_period/emulator_quota代表的是qemu线程中除了vcpu线程外的其他线程的配额

三.内存Qos

内存Qos包括内存热插拔内存balloon内存控制

1.内存热插拔

内存热插拔和CPU热插拔类似,都是在虚拟机处于高内存负载的时候,通过热插拔内存,为虚拟机提供动态扩展,保证虚拟机的高可用。libvirt中内存热插拔的配置如下:

1
2
<maxMemory slots='16' unit='KiB'>16777216表示支持的最大内存,memory表示当前可用内存,热插内存的时候需要主要slots</maxMemory>
<memory unit='KiB'>1048576</memory>

maxMemory中的slots表示有16个内存插槽,16777216表示支持的最大内存,memory表示当前可用内存,热插内存的时候需要主要slots与最大内存两个条件都必须满足才能进行热插,如果热插了15根512M的内存,再热插一个1G的内存,虽然最大内存的上限仍未达到,但是插槽已经满了,仍然不能热插内存了

2.内存balloon

内存balloon是通过给虚拟机增加一个virtio-balloon的设备,同时在虚拟机内安装virtio-balloon驱动,使用可以根据虚拟机配置动态的调账虚拟机可用内存,内存balloon在libvirt配置如下:

1
2
<memory unit='KiB'>4194304</memory>
<currentMemory unit='KiB'>1048576</currentMemory>

当currentMemory小于memory时,qemut通过virtio-balloon设备将memory-currentMemory的差值发送给虚拟机中virtio-balloon驱动,virtio-balloon会根据差值做出相应的inflate或deflate操作(即释放或申请内存)

3.内存控制

内存控制是利用cgroup的memory子系统的能力达到限制虚拟机的内存使用,memory子系统下同样也是针对每个虚拟机有一个相应的machine-qemu开头的子组,通过更改虚拟机对应子组下面的soft_limit_in_bytes、limit_in_bytes、memsw.limit_in_bytes等参数,可以控制虚拟机的内存使用限制,libvirt中内存控制的配置如下:

1
2
3
4
5
<memtune>
<hard_limit unit='KiB'>2097152</hard_limit>
<soft_limit unit='bytes'>1048576</soft_limit>
<swap_hard_limit unit='KB'>4194304</swap_hard_limit>
</memtune>

hard_limit表示虚拟机可以使用的物理内存的上限,因此hard_limit的值会写入到memory子系统的limit_in_bytes文件;
soft_limit表示当系统内存不足时,通过判断虚拟机的内存是否超过soft_limit确定是否需要回收虚拟机的内存,soft_limit的值会写入到soft_limit_in_bytes文件;
swap_hard_limit表示虚拟机可以使用包括物理内存和交换内存的总和的大小,swap_hard_limit的值会写入到memsw.limit_in_bytes文件,另外需要注意的是hard_limit必须小于等于swap_hard_limit,否则swap_hard_limit无法写入memsw.limit_in_bytes

四.磁盘IO Qos

磁盘IO是通过Cgroup实现控制虚拟机的IO读写,对磁盘IO的控制可以帮忙降低虚拟机并发启动时的IO风暴以及定位一些异常高IO的虚拟机。磁盘IO在libvirt中的配置有两种:

  • 第一种是blkiotune的配置,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <blkiotune>
    <weight>800</weight>
    <device>
    <path>/dev/sda</path>
    <weight>1000</weight>
    </device>
    <device>
    <path>/dev/sdb</path>
    <weight>500</weight>
    <read_bytes_sec>10000</read_bytes_sec>
    <write_bytes_sec>10000</write_bytes_sec>
    <read_iops_sec>20000</read_iops_sec>
    <write_iops_sec>20000</write_iops_sec>
    </device>
    </blkiotune>
  • 第二种是iotune的配置,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <disk type='file' snapshot='external'>
    <driver name="tap" type="aio" cache="default"/>
    <source file='/var/lib/xen/images/fv0' startupPolicy='optional'>
    <seclabel relabel='no'/>
    </source>
    <target dev='hda' bus='ide'/>
    <iotune>
    <total_bytes_sec>10000000</total_bytes_sec>
    <read_iops_sec>400000</read_iops_sec>
    <write_iops_sec>100000</write_iops_sec>
    </iotune>
    </disk>

iotune是针对每个虚拟机的磁盘块设备限制io读写,iotune是基于qemu中块设备的io流控实现的,libvirt通过调用block_set_io_throttle命令通知qemu需要设置Qos的设备以及参数。上述配置中的total_bytes_sec表示每秒允许的最大字节数;read_iops_sec表示每秒允许的读iops量;write_iops_sec表示每秒允许的写iops量