分享

kvm性能优化方案---cpu/内存/磁盘/网络

徐超 发表于 2015-1-12 20:07:03 [显示全部楼层] 只看大图 回帖奖励 阅读模式 关闭右栏 1 43727
问题导读
1、kvm性能优化有哪些内容?
2、网络虚拟化有哪些实现方案?
3、你如何对KVM优化的?






kvm性能优化方案
kvm性能优化,主要集中在cpu、内存、磁盘、网络,4个方面,当然对于这里面的优化,也是要分场景的,不同的场景其优化方向也是不同的,下面具体聊聊这4个方面的优化细节。

cpu
在介绍cpu之前,必须要讲清楚numa的概念,建议先参考如下两篇文章



查看cpu信息脚本:

  1. #!/bin/bash  
  2.   
  3. # Simple print cpu topology  
  4. # Author: kodango  
  5.   
  6. function get_nr_processor()  
  7. {  
  8.     grep '^processor' /proc/cpuinfo | wc -l  
  9. }  
  10.   
  11. function get_nr_socket()  
  12. {  
  13.     grep 'physical id' /proc/cpuinfo | awk -F: '{  
  14.             print $2 | "sort -un"}' | wc -l  
  15. }  
  16.   
  17. function get_nr_siblings()  
  18. {  
  19.     grep 'siblings' /proc/cpuinfo | awk -F: '{  
  20.             print $2 | "sort -un"}'  
  21. }  
  22.   
  23. function get_nr_cores_of_socket()  
  24. {  
  25.     grep 'cpu cores' /proc/cpuinfo | awk -F: '{  
  26.             print $2 | "sort -un"}'  
  27. }  
  28.   
  29. echo '===== CPU Topology Table ====='  
  30. echo  
  31.   
  32. echo '+--------------+---------+-----------+'  
  33. echo '| Processor ID | Core ID | Socket ID |'  
  34. echo '+--------------+---------+-----------+'  
  35.   
  36. while read line; do  
  37.     if [ -z "$line" ]; then  
  38.         printf '| %-12s | %-7s | %-9s |\n' $p_id $c_id $s_id  
  39.         echo '+--------------+---------+-----------+'  
  40.         continue  
  41.     fi  
  42.   
  43.     if echo "$line" | grep -q "^processor"; then  
  44.         p_id=`echo "$line" | awk -F: '{print $2}' | tr -d ' '`   
  45.     fi  
  46.   
  47.     if echo "$line" | grep -q "^core id"; then  
  48.         c_id=`echo "$line" | awk -F: '{print $2}' | tr -d ' '`   
  49.     fi  
  50.   
  51.     if echo "$line" | grep -q "^physical id"; then  
  52.         s_id=`echo "$line" | awk -F: '{print $2}' | tr -d ' '`   
  53.     fi  
  54. done < /proc/cpuinfo  
  55.   
  56. echo  
  57.   
  58. awk -F: '{   
  59.     if ($1 ~ /processor/) {  
  60.         gsub(/ /,"",$2);  
  61.         p_id=$2;  
  62.     } else if ($1 ~ /physical id/){  
  63.         gsub(/ /,"",$2);  
  64.         s_id=$2;  
  65.         arr[s_id]=arr[s_id] " " p_id  
  66.     }  
  67. }   
  68.   
  69. END{  
  70.     for (i in arr)   
  71.         printf "Socket %s:%s\n", i, arr[i];  
  72. }' /proc/cpuinfo  
  73.   
  74. echo  
  75. echo '===== CPU Info Summary ====='  
  76. echo  
  77.   
  78. nr_processor=`get_nr_processor`  
  79. echo "Logical processors: $nr_processor"  
  80.   
  81. nr_socket=`get_nr_socket`  
  82. echo "Physical socket: $nr_socket"  
  83.   
  84. nr_siblings=`get_nr_siblings`  
  85. echo "Siblings in one socket: $nr_siblings"  
  86.   
  87. nr_cores=`get_nr_cores_of_socket`  
  88. echo "Cores in one socket: $nr_cores"  
  89.   
  90. let nr_cores*=nr_socket  
  91. echo "Cores in total: $nr_cores"  
  92.   
  93. if [ "$nr_cores" = "$nr_processor" ]; then  
  94.     echo "Hyper-Threading: off"  
  95. else  
  96.     echo "Hyper-Threading: on"  
  97. fi  
  98.   
  99. echo  
  100. echo '===== END ====='  
复制代码



相信通过上面两篇文章,基本已经可以搞清楚node、socket、core、logic processor的关系,可以知道内存、l3-cache、l2-cache、l1-cache和cpu的关系。
针对kvm的优化,一般情况,都是通过pin,将vm上的cpu绑定到某一个node上,让其共享l3-cache,优先选择node上的内存,bind方法可以通过virt-manage processor里面的pinning动态绑定。这个绑定是实时生效的。

由于没有下载到speccpu2005,所以写了个大量消费cpu和内存的程序,来检验绑定cpu所带来的性能提升,程序如下:
  1. #include <stdio.h>  
  2. #include <pthread.h>  
  3. #include <stdlib.h>  
  4.   
  5. #define BUF_SIZE  512*1024*1024  
  6. #define MAX 512*1024  
  7. #define COUNT 16*1024*1024  
  8.   
  9. char * buf_1 = NULL;  
  10. char * buf_2 = NULL;  
  11.   
  12.   
  13. void *pth_1(void *data)  
  14. {  
  15.         char * p1 = NULL;  
  16.         char * p2 = NULL;  
  17.     int value1 = 0;  
  18.     int value2 = 0;  
  19.     int value_total = 0;  
  20.     int i = 0;  
  21.     int j = 0;  
  22.     for (i = 0; i <=COUNT; i++) {  
  23.             value1 = rand() % (MAX + 1);  
  24.             value2 = rand() % (MAX + 1);  
  25.             p1 = buf_1 + value1*1024;  
  26.             p2 = buf_2 + value2*1024;  
  27.             for (j = 0; j < 1024; j++) {  
  28.                     value_total += p1[j];  
  29.                     value_total += p2[j];  
  30.             }  
  31.     }  
  32.     return NULL;  
  33. }  
  34.   
  35. void *pth_2(void *data)  
  36. {  
  37.         char * p1 = NULL;  
  38.         char * p2 = NULL;  
  39.     int value1 = 0;  
  40.     int value2 = 0;  
  41.     int value_total = 0;  
  42.     int i = 0;  
  43.     int j = 0;  
  44.     for (i = 0; i <=COUNT; i++) {  
  45.             value1 = rand() % (MAX + 1);  
  46.             value2 = rand() % (MAX + 1);  
  47.             p1 = buf_1 + value1*1024;  
  48.             p2 = buf_2 + value2*1024;  
  49.             for (j = 0; j < 1024; j++) {  
  50.                     value_total += p1[j];  
  51.                     value_total += p2[j];  
  52.             }  
  53.     }  
  54.     return NULL;  
  55. }  
  56.   
  57.   
  58. int main(void)  
  59. {  
  60.       
  61.         buf_1 = (char *)calloc(1, BUF_SIZE);  
  62.         buf_2 = (char *)calloc(1, BUF_SIZE);  
  63.         memset(buf_1, 0, BUF_SIZE);  
  64.         memset(buf_2, 0, BUF_SIZE);  
  65.          
  66.     pthread_t th_a, th_b;  
  67.     void *retval;  
  68.     pthread_create(&th_a, NULL, pth_1, 0);  
  69.     pthread_create(&th_b, NULL, pth_2, 0);  
  70.   
  71.     pthread_join(th_a, &retval);  
  72.     pthread_join(th_b, &retval);  
  73.     return 0;  
  74. }  
复制代码



我的实验机器上,偶数cpu在node 0 上,奇数cpu在node 1上,vm有2个cpu,程序有2个线程,分别将vm绑定到8,9和10,12,通过time命令运行程序,time ./test,测试结果如下
  1. 8,9  
  2. real    1m53.999s  
  3. user    3m34.377s  
  4. sys 0m3.020s  
  5.   
  6. 10,12  
  7. real    1m25.706s  
  8. user    2m49.497s  
  9. sys 0m0.699s  
复制代码


可以看出,绑定到同一个node上,比绑到不同node上其消耗时间小不少。测试过程中,也发现如果提供8、9、10、11的cpu,系统会在大部分时间选择8、10和9、11,所以猜测,kvm在cpu bind上,可能已经做了优化,会尽可能的往同一个node上绑定。
这里需要注意的一点是,通过virt-manage pin cpu,仅仅进行cpu bind,会共享l3-cache,并没有限制一定用某一个node上的内存,所以仍然会出现跨node使用内存的情况。


内存
优化项包括EPT、透明大页、内存碎片整理、ksm,下面一个一个来介绍

EPT
针对内存的使用,存在逻辑地址和物理地址的转换,这个转换时通过page table来进行的,并且转换过程由cpu vmm硬件加速,速度是很块的。

但是引入vm之后,vm vaddr----->vm padddr--------->host paddr,首先vm需要进行逻辑地址和物理地址的转换,但是vm的物理地址还是host机的逻辑地址,需要再进行一次逻辑地址到物理地址的转换,所以这个过程有2次地址转换,效率非常低。

幸亏intel提供了EPT技术,将两次地址转换变成了一次。这个EPT技术是在bios中,随着VT技术开启一起开启的。

透明大页
逻辑地址向物理地址的转换,在做转换时,cpu保持一个翻译后备缓冲器TLB,用来缓存转换结果,而TLB容量很小,所以如果page很小,TLB很容易就充满,这样就很容易导致cache miss,相反page变大,TLB需要保存的缓存项就变少,减少cache miss。

透明大页的开启:echo always > /sys/kernel/mm/transparent_hugepage/enabled
内存碎片整理的开启:echo always> /sys/kernel/mm/transparent_hugepage/defrag

KSM
文章http://blog.chinaunix.net/uid-20794164-id-3601786.html,介绍的很详细,简单理解就是可以将host机内容相同的内存合并,节省内存的使用,特别是当vm操作系统都一样的情况,肯定会有很多内容相同的内存,开启了KSM,则会将这些内存合并为一个,当然这个过程会有性能损耗,所以开启与否,需要考虑使用场景,如果不注重vm性能,而注重host内存使用率,可以考虑开启,反之则关闭,在/etc/init.d/下,会有两个服务,服务名称为ksm和ksmtuned,都需要关闭。


磁盘
磁盘的优化包括:virtio-blk、缓存模式、aio、块设备io调度器

virtio
半虚拟化io设备,针对cpu和内存,kvm全是全虚拟化设备,而针对磁盘和网络,则出现了半虚拟化io设备,目的是标准化guest和host之间数据交换接口,减少交互流程和内存拷贝,提升vm io效率,可以在libvirt xml中设置,disk中加入<target dev='vda' bus='virtio'/>

缓存模式
从vm写磁盘,有3个缓冲区,guest fs page cache、Brk Driver writeback cache(qemu的cache)、Host FS page cache,在host上的设置,无法改变guest fs page cache,但是可以改变后面2个cache,缓存模式有如下5种,当采用Host FS page cache,会有一个写同步,会实时将host cache中的数据flush到磁盘上,当然这样做比较安全,不会丢失数据,但写性能会受到影响。具体模式见下图。
1.jpg


第1和第5种,都太极端了,很少会有人使用,常使用的是中间3种,其性能比较如下:
2.jpg


可以看出writeback mode在mail server这种小文件 高io的服务器上,其性能是很差的,none模式大部分情况要比writethrough性能稍好一点,所以选择none。

启用方式在libvirt xml disk中加入<driver name='qemu' type='qcow2' cache='none'/>

aio
异步读写,分别包括Native aio: kernel AIO 和 threaded aio: user space AIO emulated by posix thread workers,内核方式要比用户态的方式性能稍好一点,所以一般情况都选择native,开启方式<driver name='qemu' type='qcow2' cache='none' aio='native'/>

块设备调度器
cfq:perprocess IO queue,较好公平性,较低aggregate throughput

deadline:per-device IO queue,较好实时性,较好aggregate throughput,不够公平,当某些vm有大量io操作,占用了大量io资源时,其它后加入的vm很有可能抢占不到io资源。
这个目前笔者还没有做过测试,但是查看网易和美团云的方案,都将其设置为cfq。

开启方式:echo cfq > /sys/block/sdb/queue/scheduler


网络
优化项包括virtio、vhost、macvtap、vepa、SRIOV 网卡

virtio
更改虚拟网卡的类型,由全虚拟化网卡e1000、rtl8139,转变成半虚拟化网卡virtio,virtio需要qemu和vm内核virtio驱动的支持,这个原理和磁盘virtio原理一样,不再赘述。

vhost_net
vhost_net将virtiobackend处理程序由user space转入kernel space,将减少两个空间内存拷贝和cpu的切换,降低延时和提高cpu使用率

macvtap
代替传统的tap+bridge,有4中模式,bridge、vepa、private、passthrough
1, Bridge, 完成与 Bridge 设备类似功能,数据可以在属于同一个母设备的子设备间交换转发. 当前的Linux实现有一个缺陷,此模式下MACVTAP子设备无法和Linux Host通讯,即虚拟机无法和Host通讯,而使用传统的Bridge设备,通过给Bridge设置IP可以完成。但使用VEPA模式可以去除这一限制. macvtap的这种bridge模式等同于传统的tap+bridge的模式.
2, VEPA, 式是对802.1Qbg标准中的VEPA机制的部分软件实现,工作在此模式下的MACVTAP设备简单的将数据转发到母设备中,完成数据汇聚功能,通常需要外部交换机支持Hairpin模式才能正常工作。
3, Private, Private模式和VEPA模式类似,区别是子 MACVTAP之间相互隔离。
4, Passthrough, 可以配合直接使用SRIOV网卡, 内核的macvtap数据处理逻辑被跳过,硬件决定数据如何处理,从而释放了Host CPU资源。MACVTAP Passthrough 概念与PCI Passthrough概念不同,PCI Passthrough针对的是任意PCI设备,不一定是网络设备,目的是让Guest OS直接使用Host上的 PCI 硬件以提高效率。MACVTAP Passthrough仅仅针对 MACVTAP网络设备,目的是饶过内核里MACVTAP的部分软件处理过程,转而交给硬件处理。综上所述,对于一个 SRIOV 网络设备,可以用两种模式使用它:MACVTAP Passthrough 与 PCI Passthrough
PCI pass-through
直通,设备独享。

SO-IOV
优点是虚拟网卡的工作由host cpu交给了物理网卡来实现,降低了host cpu的使用率,缺点是,需要网卡、主板、hypervisor的支持。

这几种优化方案,其关系可由下面这张图来表示。
1.jpg



测试结果,在实验室2台host,分别起1台vm(vm1、vm2),用iperf测试vm1和vm2之间的吞吐量,用ping测试2者之间的响应时间,host机为百兆网卡,结果如下表所示,可以看出随着优化的深入,其吞吐量和响应时间都有所改善,由于暂时没有硬件的支持,macvtap vepa和SR-IOV没有得到测试。

  
Test item
  
Iperf(ping)
rtl8139
87Mb/s(1.239ms)
virtio
89Mb/s(1.140ms)
Virtio + host_net
92Mb/s(1.014ms)
Macvtap(bridge)  + virtio +host_net
94Mb/s(0.989ms)
host
95Mb/s(0.698ms)

总结来看网络虚拟化具有三个层次:

1, 0成本,通过纯软件virtio、vhost、macvtap提升网络性能;
2, 也可以用非常低的成本按照802.1Qbg中的VEPA模型创建升级版的虚拟网络,引出虚拟机网络流量,减少Host cpu负载,但需要物理交换机的配合;
3, 如果网络性能还是达不到要求,可以尝试SR-IOV技术,不过需要SR-IOV网卡的支持。

总结:文章总共阐述了cpu、内存、磁盘、网络的性能优化方案,大部分都是通过kvm参数和系统内核参数的修改来实现。

本帖被以下淘专辑推荐:

已有(1)人评论

跳转到指定楼层
stark_summer 发表于 2015-1-13 10:46:22
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条