前言

本文的目的是为了解决在使用CloudStack(CloudPlatform)时,基于KVM虚拟化平台,Windows虚拟机的性能低下的问题。

此性能,主要指磁盘IO和网卡性能。


相关文档

由于CS文档中,只强调了PV这个概念,根据PV模式区分使用不同的硬件接口类型。所以收集部分链接给大家扫盲。

关于PV(Paravirtualization-半虚拟化)模式的概念,请参阅:

http://www.rackspace.com/knowledge_center/article/choosing-a-virtualization-mode-pv-versus-pvhvm

http://wiki.xenproject.org/wiki/Paravirtualization_%28PV%29

http://zh.wikipedia.org/wiki/%E8%99%9A%E6%8B%9F%E5%8C%96

http://www.ibm.com/developerworks/cn/cloud/library/cl-hypervisorcompare-kvm/


另外,关于virtio,可以查看如下链接:

http://www.linux-kvm.org/page/Virtio

http://smilejay.com/2012/11/virtio-overview/    (这个值得不懂kvm的兄弟一看)


背景

在两个项目汇总(CS+KVM),客户均提到Windows性能不足,但至于哪方面性能,不细说,太多。使用者反馈就一句话:虚拟机运行太慢。

十几台刀片服务器,30T SAN存储,将近万G内存。才跑了100来个虚拟机,还慢,像话嘛。

得了,慢,咱就得找原因。


初步分析

我这人比较肤浅,首先想到的就是磁盘IO不行,这个是最直接让客户觉得系统慢的原因(排除内存不足的问题)。

经过检查,发现:

1.使用CS创建的Windows虚拟机,系统盘的磁盘类型为IDE(你拿IDE接口跑Windows 2008R2,还要求性能,这像话嘛)。除系统盘之外所创建的所有数据盘,接口类型全部为VirtIO。

至于如何查看硬盘接口类型,可以在系统中,打开设备管理器-查看磁盘驱动器类型。或者在kvm主机中,使用 ”virsh edit 虚拟机名称“ 查找Disk相关配置。直接使用virt-manager也可以。

2.使用CS创建的Linux(常见发行版)虚拟机,系统盘和数据盘硬盘接口类型全部为VirtIO。


到这里,我将问题归结为:在CS中,基于KVM平台,所创建的Windows虚拟机,系统盘磁盘接口类型为IDE,这是导致性能慢的主要原因。最优磁盘接口类型应该为VirtIO。

注:

有人会说你这太武断了,嗯,我上家公司基于KVM做桌面虚拟化产品,当时跟VMware View,XenDesktop做性能对比也不相上下(均是默认配置),跑Win7和Win2008,在负载一般的情况下,很少出现运行慢的问题。所以,基于KVM平台,跑Windows系统,效果并不会太差。当然了,除了硬盘接口使用了VirtIO,也做了其他调优。

但,了解KVM的朋友,应该知道KVM+VirtIO是一对好×××。至于IDE和VirtIO的性能测试对比,稍后有机会我再拿出具体测试数据。


问题视乎变得简单了,将系统盘的IDE接口,换成VirtIO接口即可。


再来看下官方文档中指出,哪些系统类型,默认的系统盘接口类型为VirtIO:

  • (仅KVM) If you choose an OS that is PV-enabled, the VMs created from this ISO will have a SCSI (virtio) root disk. If the OS is not PV-enabled, the VMs will have an IDE root disk. The PV-enabled types are:

    • Fedora 13

    • Fedora 12

    • Fedora 11

    • Fedora 10

    • Fedora 9

    • Other PV

    • Debian GNU/Linux

    • CentOS 5.3

    • CentOS 5.4

    • CentOS 5.5

    • Red Hat Enterprise Linux 5.3

    • Red Hat Enterprise Linux 5.4

    • Red Hat Enterprise Linux 5.5

    • Red Hat Enterprise Linux 6

由上面的信息得知:启用了PV模式的操作系统,根磁盘,也就是系统磁盘,默认会使用VirtIO模式,而没用启用PV模式的操作系统,将会默认使用IDE模式

细心的你也会发现,上面的列表中,几乎全部是Linux系统,除了"Other PV"这个怪胎(其实还有"Windows PV"啦,至于为啥,也许是写文档的人懒,也许是,稍后再说)。

为啥几乎都是Linux系统,官方说了这样一句话:

(仅KVM)All VMs are required to support the virtio drivers. These drivers are installed in all Linux kernel versions 2.6.25 and greater. The administrator must set CONFIG_VIRTIO_BALLOON=y in the virtio configuration.


所有的虚拟机都要求支持virtio驱动,而Linux平台的virtio驱动已经集成到2.6.25或更高版本的内核中。也就是说Linux可以直接识别virtio接口设备。而virtio是一个在hypervisor之上的抽象API接口,是一个半虚拟化驱动,virtio与hypervisor协作工作,用于提供出色的性能。也就是说,virtio不是标准接口类型。你买硬盘的时候有看到过virtio硬盘嘛。所以,Windows中不集成此驱动,也在情理之中。这也是为啥没有启用PV模式的虚拟机的系统磁盘接口类型为IDE。


那是不是Windows就不能使用VirtIO了?当然不是。人家说了嘛,只要启用了PV模式的操作系统,系统磁盘就会是VirtIO。所以CS中提供了2中PV模式的操作系统类型Other PV 和 Windows PV。 其实只有Other PV这一种。因为代码中,Windows PV最终也是归类为Other PV。

当然了,Windows 系统中的VirtIO驱动,就需要自己安装了。


初步解决

到这里,问题初步已经可以解决了,就是在CS中,将Windows相关的虚拟机和模版中的操作系统类型,定义为:Windows PV或Other PV。

只要选择了PV模式的操作系统类型,系统磁盘类型即可使用VirtIO。

更改CloudStack中KVM平台的Windows虚拟机默认磁盘类型为VirtIO_windows

更改CloudStack中KVM平台的Windows虚拟机默认磁盘类型为VirtIO_virtio_02


如果这样你以为就该高兴了,那我就要呵呵了。

本着 no zuo no die , why you try的原则,我还是要找到根本解决方法。


深入分析

首先想想为啥CS中定义了启用PV和不启用PV模式的操作系统,为啥CS中基于KVM平台的windows虚拟机系统磁盘不直接使用virtio接口类型。而除系统磁盘以外的第二块,第三块磁盘却使用virtio接口类型?难道CS的开发人员不知道这样性能会有质的提升吗?


我认为:

1.启用PV和不启用PV,在CS代码层面,最根本要解决的还是区分根磁盘接口类型使用IDE或VirtIO(针对KVM平台)

2.KVM平台中windows虚拟机系统磁盘不直接使用virtio接口而是使用IDE,实际上是为了方便使用CS部署KVM虚拟机的人员。为啥,如果windows虚拟机默认也使用virtio接口类型的磁盘驱动器,那你会碰到什么问题?你会发现虚拟机启动失败。或者当你在从ISO安装一台windows虚拟机时发现到磁盘驱动器那一步,windows找不到磁盘驱动器。这个时候你会怎么办?

安装 vista,win7,2008.win8 等高版本windows时,在硬盘驱动器那一步骤中,允许你通过ISO加载驱动器驱动。确实也提供了virtio的iso格式驱动安装包,这个时候你可以弹出安装iso,然后附加virtio的驱动iso,安装完驱动以后再弹出该iso,再次附件系统iso。好了,系统可以识别到磁盘驱动器了。那2003呢,xp呢,必须要插入软盘安装驱动,你咋办?CS提供软驱了不?

3.系统盘为IDE接口,其他盘均为virtio,如上面所说。先保证你可以把系统安装好,再来装virtio驱动。不管xp,2003,还是win7,2008,windows系统都能识别到IDE接口的硬盘吧,等你安装好系统,再为数据盘安装virtio驱动。

4.开发人员正是基于如上考虑,才这么设计的吧。当然你可以在安装完virtio的磁盘和网卡驱动后,关机,把虚拟机操作系统类型改为Windows PV模式,再开机,那虚拟机就会使用virtio作为系统磁盘接口类型。或者你可以把安装好virtio驱动的虚拟机转化为模版,并设置操作系统类型为windows pv,那么以后使用该模版创建的虚拟机,都不用再担心驱动问题。

5.当然,如果你在CS之外,已经做好了KVM平台的windows虚拟机模版,并直接加入了virtio的驱动,然后导入CS。然后这个模版的操作系统类型,你也需要设置为Windows PV模式。


但,我目前所在的项目中,不太方便使用将虚拟机或模版的操作系统类型修改为Windows PV的方式。

一来虚拟机多了,统计不方便,所有的windows虚拟机都是windows pv模式。。。二来,本着打破沙锅的精神,从代码层面,直接找到根源,并修改之。这样就不用设置烦人的Windows PV了。

当然,前提是,我的模版中,已经加入了VirtIO的所有驱动(磁盘驱动器+网卡)


解析代码:


在CS官方的github https://github.com/apache/cloudstack

搜索如下关键字: vritio,windows pv,other pv,ide 等等,自由发挥。


经过一番研究,找到如下两个文件:

https://github.com/apache/cloudstack/blob/afc188cb5c72e316975799c95529e8692ddcb94b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/KVMGuestOsMapper.java


https://github.com/apache/cloudstack/blob/7542ffc48282ff703fdb586ce447091260ae3c02/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java



1.首先来看KVMGuestOsMapper.java , 定义了kvm平台中,支持的操作系统列表:

public class KVMGuestOsMapper {
    private static final Logger s_logger = Logger.getLogger(KVMGuestOsMapper.class);
    private static Map<String, String> s_mapper = new HashMap<String, String>();
    static {
       ......
        s_mapper.put("Windows Server 2008 (64-bit)", "Windows Server 2008");
        s_mapper.put("Windows Server 2008 R2 (64-bit)", "Windows Server 2008");
        s_mapper.put("Windows XP (64-bit)", "Windows XP");
        s_mapper.put("Windows NT 4", "Windows NT");
        s_mapper.put("Windows 3.1", "Windows 3.1");
        s_mapper.put("Windows PV", "Other PV");
        s_mapper.put("FreeBSD 10 (32-bit)", "FreeBSD 10");
        s_mapper.put("FreeBSD 10 (64-bits", "FreeBSD 10");
        s_mapper.put("Other PV (32-bit)", "Other PV");
        s_mapper.put("Other PV (64-bit)", "Other PV");
       ......
    }


可以看到,此表为操作系统类型对应关系,s_mapper.put中,有两个字段: s_mapper.put(操作系统版本,操作系统类型);

 一个操作系统类型中,包括多个操作系统版本,注意我们说过的Windows PV,也被归类为Other PV。


2.再来查看LibvirtComputingResource.java文件。

查找virtio看到如下信息:

做了一个判断,如果操作系统类型属于GuestPVEnabled,则返回磁盘bus为VIRTIO,如果不属于,则使用IDE。

    private DiskDef.diskBus getGuestDiskModel(String platformEmulator) {
        if (isGuestPVEnabled(platformEmulator)) {
            return DiskDef.diskBus.VIRTIO;
        } else {
            return DiskDef.diskBus.IDE;
        }
    }


然后再查找GuestPVEnabled,查找到一个类,该类型中,定义了,那些系统类型属于GuestPVEnabled,可以看到大部分linux系统和Other PV被归类为GuestPVEnabled:

    
boolean isGuestPVEnabled(String guestOSName) {
        if (guestOSName == null) {
            return false;
        }
        if (guestOSName.startsWith("Ubuntu") || guestOSName.startsWith("Fedora 13") || guestOSName.startsWith("Fedora 12") || guestOSName.startsWith("Fedora 11") ||
                guestOSName.startsWith("Fedora 10") || guestOSName.startsWith("Fedora 9") || guestOSName.startsWith("CentOS 5.3") || guestOSName.startsWith("CentOS 5.4") ||
                guestOSName.startsWith("CentOS 5.5") || guestOSName.startsWith("CentOS") || guestOSName.startsWith("Fedora") ||
                guestOSName.startsWith("Red Hat Enterprise Linux 5.3") || guestOSName.startsWith("Red Hat Enterprise Linux 5.4") ||
                guestOSName.startsWith("Red Hat Enterprise Linux 5.5") || guestOSName.startsWith("Red Hat Enterprise Linux 6") || guestOSName.startsWith("Debian GNU/Linux") ||
                guestOSName.startsWith("FreeBSD 10") || guestOSName.startsWith("Other PV")) {
            return true;
        } else {
            return false;
        }


彻底解决:

由此,我们有思路了,修改isGuestPVEnabled类(LibvirtComputingResource.java),或修改操作系统对应关系(KVMGuestOsMapper.java )。

但,修改这两个文件,效果不一样:

1.如果修改LibvirtComputingResource.java文件中的isGuestPVEnabled类,在该类中,添加操作系统类型。注意,是操作系统类型,而并非操作系统版本。比如添加windows 2008 操作系统所有版本均为PV模式,要加入guestOSName.startsWith("Windows Server 2008"),这样的话,2008下面的所有版本,均应用PV设置。

2.如果修改KVMGuestOsMapper.java ,可以将其中一个操作系统版本,定义为PV模式。例如,将2008 R2定义为PV模式,

将:       

 s_mapper.put("Windows Server 2008 R2 (64-bit)", "Windows Server 2008");

修改为:

 s_mapper.put("Windows Server 2008 R2 (64-bit)", "Other PV");


这样,不会影响Windows Server 2008 (32-bit)和Windows Server 2008 (64-bit)版本。


修改完毕后,需要将该文件编译(貌似需要将CS代码全部编译一遍,编译单个文件失败)。会得到LibvirtComputingResource.class或KVMGuestOsMapper.class文件。


然后使用winrar 等工具打开KVM节点中:/usr/share/cloudstack-agent/lib/cloud-plugin-hypervisor-kvm-4.2.1.jar 文件,将得到的class文件替换至\com\cloud\hypervisor\kvm\resource目录中的旧文件。保存。


然后再将修改后的jar文件替换回KVM主机,重启cloudstack-agent服务。然后创建新的Windows Server 2008 R2 (64-bit)虚拟机,会发现默认使用virtio磁盘驱动器和网卡适配器。


已经存在的Windows Server 2008 R2 (64-bit)虚拟机,需要关机,再开机。才会应用virtio配置。注意在KVM虚拟化中,虚拟机的关闭,再启动和重启是有区别的。


此方法由于直接修改代码,将需要的操作系统类型或具体版本加入GuestPVEnabled类中,所以无需在CS中指定操作系统类型为Windows PV。从根本上解决该问题。


带来的弊端:

每次升级,都需要编译替换该文件,除非使用自己编译的安装包进行安装。