8.4 云计算与虚拟化入门通识

image0

经常有朋友问我,你是做什么的呢?

我回答说,云计算。

不回答还好,一回答,他们更加疑惑了。听着挺高大上,挺牛b的,就是不知道是什么。在大多数Pythonista的认知里,学会了Python,不从事爬虫,就是做Web开发,不然就是数据分析/挖掘,人工智能的。

想一想,这也是合理的,大部分培训班为你让你交学费,一般都会把你的职业路线给规划好,什么Web开发,什么爬虫工程师,什么数据分析师等等,却没人告诉你,学会了Python,也可以去做一名云计算工程师(准确地说应该是OpenStack工程师,因为云计算涉及的范围更广,需要的技术栈更多,而不单单是一门编程语言)。

为了让你多了解一些云计算的内容,我想着写这么一篇文章,介绍一下我从事的领域,同时也对云计算和虚拟化这块入门级知识做一个梳理,如果刚好你也想进入这个领域,这份入门通识指南,应该挺适合你的。

image1

8.4.1 云计算是什么?

维基百科上定义的云计算(英语:cloud computing),是一种基于互联网的计算方式,通过这种方式,共享的软硬件资源和信息可以按需求提供给计算机各种终端和其他设备。

枯燥的定义,听起来还是不太好理解,我尝试用自己的语言来解释下。

计算,即计算资源,包括我们熟悉的 CPU,内存,磁盘,带宽等。

云,就是将这些零散实体资源变成一个巨大无比的资源池子,有了这个池子,做为个人用户,你不再需要自己你买一个电脑放在家里,做为小型公司,你不需要自己整一个机房,花很多的人力和设备成本去运营这些基础设施。一旦你需要,你就向池子拥有者申请即可。这极大的提高了资源的利用率,以及分配的灵活性。

image2

还有人说,云就像是天上的云一样,聚焦的水汽多了就会下雨,落到地面的雨水又会蒸发到天上,继续等待下一次下雨。云计算里的云,正如大自然里的云一样,可以实现资源的循环利用。你在公有云提供商那里,购买了一年的云主机,一年后资源被回收,可以再分配给其他人使用。

云计算的模型,是以服务为导向的。根据服务层次的不同,可以分为三类:

  1. IaaS(Infrastructure as a Service):基础设施即服务,简单点说就是提供基础设施,你在阿里云,AWS上购买的云主机就属于这类。

  2. PaaS(Platform as a Service):平台即服务,简单点说就是提供一个平台,典型的应用有,GAE(Google App Engine),直接给你提供一个应用程序的运行环境。

  3. SaaS(Software as a Service):软件即服务,这个你再熟悉不过了,你手机上的APP都是属于这类。

以上三种模型,面向的群体各不相同,从上到下,用户的自主权越来越小,需关注的细节也越来越少。

另外根据部署方式的不同,可以将云计算分为三类:公有云、私有云和混合云。

私有云由专供一个企业或组织使用的云计算资源构成。私有云可在物理上位于组织的现场数据中心,也可由第三方服务提供商托管。而混合云,就是二者皆有。

网上很流行的一种比喻:男人找个女友或老婆是自建私有云,单身约p或者到娱乐场所消费是公有云服务,按需使用并可弹性扩容,已婚男人找二奶小蜜则属于混合云。

这种解释方式虽然比较俗,但却挺恰当,便于小白理解。

8.4.2 虚拟化是什么?

云计算,是为了提高资源的利用率,分配的灵活性而提出的一种解决方案。

而这个解决方案的底层,需要有技术支撑,目前主要是虚拟化技术和容器技术。

这次主要讲的是虚拟化。

你是不是又要蒙圈了,什么是虚拟化?

它是一种可以将计算机的实体资源(CPU,内存,存储,网络等)进行抽象转化,并提供分割,重新组合,以达到最大化利用资源的一种技术。

虚拟机使用过吧?

当你只有一个电脑装了windows系统,而你也想体验一下linux系统,如果不想折腾去装个双系统,最简单的方法就是用 VMWare 或者 VirtualBox 在你的电脑里用linux的镜像创建个虚拟机。

你有没有想过,这虚拟机是如何创建出来,怎么这么神奇,一台电脑上竟然可以同时运行着两个操作系统。

其实你后创建的这个虚拟机只是原物理机上的一个进程而已。只不过它从外观上、使用上看起来和你原来宿主机上的系统没有什么区别。这个虚拟机里有自己的内存,cpu,磁盘,网卡,这些都依赖虚拟化技术才得以实现的。

在虚拟机内部,如果要使用物理机上设备,除了虚拟化技术让其可以间接地使用物理设备,也可以使用设备的直通让虚拟机直接使用物理设备,这种直通技术,不需要经过VMM(虚拟机监控器,后面会介绍),所以性能会比虚拟化好。常见的有GPU直通。还有直通与虚拟化的结合,如SR-IOV,即单根IO虚拟化(Single-root I/O virtualization),将一个物理网卡(PF)虚拟化成多个虚拟网卡(VF),再将虚拟网卡直给挂给虚拟机使用。

8.4.3 VMM 是什么?

VMM,通常叫做 Hypervisor(下面我们也将以Hypervisor指代VMM),中文名:虚拟机监控器,英文全称:Virtual Machine Monitor。

Hypervisor 是为了实现虚拟化而引入的一个介于虚拟机操作系统和物理资源的软件层。

需要注意的是,Hypervisor并不是一款具体的软件,而是一类软件的统称。

当虚拟机要对物理资源进行操作时,Hypervisor将对其指令进行截取并且重定向,让虚拟机无感知地像物理操作系统一样使用物理资源。

常见的Hypervisor,有

  • KVM

  • Xen

  • Hyper-V

  • VMWare

8.4.4 虚拟化技术

1. KVM

KVM(Kernel-based Virtual Machine),意思是基于内核的虚拟机。

KVM是集成到Linux内核的Hypervisor,是X86架构且硬件支持虚拟化技术(Intel VT或AMD-V)的Linux的全虚拟化解决方案。它是Linux的一个很小的模块,利用Linux做大量的事,如任务调度、内存管理与硬件设备交互等。

2. Xen

Xen是第一类运行在裸机上的虚拟化管理程序。它支持全虚拟化和半虚拟化,Xen支持hypervisor和虚拟机互相通讯,而且提供在所有Linux版本上的免费产品,包括Red Hat Enterprise Linux和SUSE Linux Enterprise Server。Xen最重要的优势在于半虚拟化,此外未经修改的操作系统也可以直接在xen上运行(如Windows),能让虚拟机有效运行而不需要仿真,因此虚拟机能感知到hypervisor,而不需要模拟虚拟硬件,从而能实现高性能。

3. QEMU

QEMU是一套由Fabrice Bellard所编写的模拟处理器的自由软件。Qemu,其中关键字emu,全称emulator,模拟器,所以单纯使用qemu是采用的完全虚拟化的模式。

那QEMU有什么用?它和KVM是什么关系呢?

准确来说,KVM是Linux kernel的一个模块。可以用命令modprobe去加载KVM模块。加载了模块后,才能进一步通过其他工具创建虚拟机。

但仅有KVM模块是 远远不够的,KVM是最底层的hypervisor,它仅用来模拟CPU的运行,缺少了对network和周边I/O的支持,所以我们是没法直接用它的。

而QEMU-KVM就是一个完整的模拟器,它是基于KVM构建的,提供了完整的网络和I/O支持。

说到了QEMU,其实它也是一个虚拟化软件。作用是什么呢,它相当于一个路由器,当Guest OS的内核想要操作物理硬件时,必须先经由Qemu转发,将操作指令转给真实的硬件。由于所有的指令都要从Qemu里面过一手,因而性能比较差。

image3

总结

  1. KVM 和 Xen 都是免费的。

  2. KVM 需要硬件支持(Intel VT或AMD-V),集成在内核中,而Xen可在所有的Linux上运行,可不需要硬件支持。

4. libvirt

要解释libvirt是什么,只要知道为什么会需要libvirt就好了。

  • 虚拟化的底层可能是KVM,也可能是Xen,或者是其他市面上的Hypervisor,种类之繁多,如果没有一个统一的接口来管理它们,就太乱了,移植性非常差。

  • Hypervisor ,以 qemu-kvm 为例,它的命令行虚拟机管理工具参数众多,难于使用。需要有一个工具将这些参数进行封装。

这些都是在内核空间层做的事情,而我们用户创建、销毁虚拟机都是在用户空间层操作,这就尴尬了,我们没有权限。

这下该 libvirt 出场了,libvirt分为服务端各客户端。

服务端是libvirtd,而你所熟悉的virt,virt-install,virt-manager,virt-viewer 等都是libvirt的客户端。

目前,libvirt 已经成为使用最为广泛的对各种虚拟机进行管理的工具和应用程序接口(API),而且一些常用的虚拟机管理工具(如virsh、virt-install、virt-manager等)和云计算框架平台(如OpenStack、OpenNebula、Eucalyptus等)都在底层使用libvirt的应用程序接口。

image4

8.4.5 虚拟化分类

全虚拟化和半虚拟化

根据客户机系统是否需要修改定制可以分为 全虚拟化半虚拟化

1. 全虚拟化

全虚拟化(英语:Full virtualization),是需要依托于硬件虚拟化的。

在全虚拟化模式下,虚拟机的所有操作(CPU,内存,网络等)都需要经过一个运行在物理机上的虚拟化软件转发给物理机内核。而这个虚拟化软件,在windows上你常见且熟悉的有vmware,virtualbox。

允许未经修改的客操作系统(英语:Guest OS)隔离运行。在全虚拟化环境中,任何可以运行在裸机上的软件(通常是操作系统)都可以未经修改地运行在虚拟机中。

代表:VMWare(1998年),KVM

2. 半虚拟化

半虚拟化(英语:Paravirtualization)是另一种类似于全虚拟化的热门技术。

半虚拟化对比全虚拟化,就是有一些可以直接操作物理内核空间,而不需要全部经过虚拟化软件。这就大大提高了虚拟机的性能。

它在HOST上使用Hpervisor(虚拟机管理程序)提供便利的接口,使得Guest OS能够调用接口访问虚拟硬件。而条件是,Guest OS 内部需要部署安装相应的驱动和软件逻辑,需要对操作系统进行修改。

代表:Xen(2006)

  • Xen是一款虚拟化软件,支持半虚拟化和完全虚拟化。它在不支持VT技术的cpu上也能使用,但是只能以半虚拟化模式运行。

  • 半虚拟化的意思是需要修改被虚拟系统的内核,以实现系统能被完美的虚拟在Xen上面。完全虚拟化则是不需要修改系统内核则可以直接运行在Xen上面。

  • VMware是一款完全虚拟化软件。完全虚拟的弱点是效率不如半虚拟化的高。半虚拟化系统性能可以接近在裸机上的性能。

1型虚拟化和2型虚拟化

根据虚拟化层是直接位于硬件之上还是位于操作系统之上,可以分为 Type 1 虚拟化和 Type 2 虚拟化。

image5

Type 1:Xen,VMWare ESX

Type 2:KVM,WMWare Workstation

硬件虚拟化和软件虚拟化

1. 软件虚拟化

在硬件虚拟化出现之前,市场上都是使用的软件虚拟化。

软件虚拟化,就是通过软件来实现虚拟化,原理是把从虚拟机传来的操作指令进行截取翻译,并传递给真实的物理硬件。

由于每条指令都需要经过“截取” -> “翻译” -> “转发”,所以其虚拟化性能会差一点。

哪些属于虚拟化软件呢?

  • KVM:负责cpu和内存的虚拟化,但cpu必须支持硬件虚拟化。

  • QEMU:负责IO设备(网卡、磁盘)的虚拟化

2. 硬件虚拟化

硬件虚拟化,是指计算机硬件本身提供能力让客户机指令独立运行,而不需要Hypervisor 截获重定向。直接从硬件层面开始支持虚拟化。由硬件支持并提供多个虚拟硬件设备接口,这些设备由虚拟机内核驱动传递给虚拟机使用。使用这种方式,虚拟机能获得和宿主机一样的硬件功能,性能也和宿主机相近,同时原生操作系统本来就支持这项技术,因此无需对操作系统进行修改。

Intel 从2005年开始在 x86 cpu 上支持硬件虚拟化,大大推进了虚拟化的发展。

缺点就是,硬件要支持虚拟化功能,在以前这可能是缺点,但是现在随着虚拟化技术的发展,越来越多的硬件都已经支持虚拟化,成本也越来越低,所以硬件辅助虚拟化是目前最流行,使用最广泛的虚拟化技术。

KVM这种流行的虚拟化技术里,既有软件虚拟化,也有硬件虚拟化,软件虚拟化要基于硬件的虚拟化,二者是相辅的关系,而不是互斥。

8.4.6 KVM工具

有了虚拟化,就有了虚拟机,那如何对这些虚拟机进行管理呢。

在 Linux 下有许多的工具可以使用:

  • Virsh:基于 libvirt 的 命令行工具 (CLI)

  • Virt-Manager:基于 libvirt 的 GUI 工具

  • virt-v2v:虚机格式迁移工具

  • virt-* 工具:包括 Virt-install (创建KVM虚机的命令行工具), Virt-viewer (连接到虚机屏幕的工具),Virt-clone(虚机克隆工具),virt-top 等

  • libguestfs-tools:一组 Linux 下的 C 语言的 API ,用来访问/修改虚拟机的磁盘映像文件。

image6

8.4.7 创建虚拟机

手工创建

虚拟机的本质是宿主机上的一个进程,当你用OpenStack在界面,或者使用virsg 创建了一个虚拟机时。你可以使用ps -ef|grep kvm 看下这个虚拟机的进程,是下面这样子的。

参数多得让人头皮发麻。意思是,你可以使用这样一串命令才能创建一台虚拟机。

$ /usr/libexec/qemu-kvm \
-name guest=instance-00000035, debug-threads=on \
-S -object secret,id=masterKey0,format=raw,file=/var/lib/libvirt/qemu/domain-216-instance-00000035/master-key.aes \
-machine pc-i440fx-rhel7.5.0,accel=kvm,usb=off,dump-guest-core=off \
-cpu host \
-m 16384 \
-realtime mlock=off \
-smp 2,maxcpus=32,sockets=2,cores=16,threads=1 \
-uuid 31d70882-194f-469b-855e-fcfa6736550d \
-smbios type=1,manufacturer=RDO,product=OpenStack Compute,version=0.0.1-1.el7.centos,serial=bc147bfe8a204d06a09f98387e46b890,uuid=31d70882-194f-469b-855e-fcfa6736550d,family=Virtual Machine \
-display none \
-no-user-config -nodefaults \
-chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/domain-216-instance-00000035/monitor.sock,server,nowait \
-mon chardev=charmonitor,id=monitor,mode=control \
-rtc base=utc,driftfix=slew \
-global kvm-pit.lost_tick_policy=delay \
-no-hpet -no-shutdown -boot strict=on \

-device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 -drive file=/dev/hdd-volumes/31d70882-194f-469b-855e-fcfa6736550d_disk,format=raw,if=none,id=drive-virtio-disk0,cache=none,aio=native -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1 -drive file=/var/lib/nova/instances/31d70882-194f-469b-855e-fcfa6736550d/disk.config,format=raw,if=none,id=drive-ide0-0-0,readonly=on,cache=writeback \
-device ide-cd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \
-netdev tap,fds=28:35,id=hostnet0,vhost=on,vhostfds=36:37 \
-device virtio-net-pci,mq=on,vectors=6,netdev=hostnet0,id=net0,mac=fa:16:3e:69:63:18,bus=pci.0,addr=0x3 -chardev pty,id=charserial0 -device isa-serial,chardev=charserial0,id=serial0 -device qxl-vga,id=video0,ram_size=67108864,vram_size=67108864,vram64_size_mb=0,vgamem_mb=16,max_outputs=1,bus=pci.0,addr=0x2 \
-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x5 -msg timestamp=on

virsh 创建

前面我们看到,创建一台虚拟机需要诸多的参数。

如果一个一个去指定,非常不易于管理及复用。

如果可以在创建时,指定一个配置文件,这个配置文件里包含上述所有的参数,不就大大简化了虚拟机创建过程。

这时候就出现了virsh这个基于 libvirt 的 命令行工具 (CLI)。通过它我们可以指定一个 xml 配置文件来很轻松的创建一台虚拟机。

virsh define vm.xml
virsh start guest_vm

其中xml的内容如下

<domain type='kvm' id='200'>
  <name>guest_vm</name>
  <memory unit='KiB'>12582912</memory>
  <currentMemory unit='KiB'>12582912</currentMemory>
  <vcpu placement='static'>6</vcpu>
  <numatune>
    <memory mode='strict' nodeset='0-1'/>
    <memnode cellid='0' mode='strict' nodeset='0'/>
    <memnode cellid='1' mode='strict' nodeset='1'/>
  </numatune>
  <resource>
    <partition>/machine</partition>
  </resource>
  <os>
    <type arch='x86_64' machine='pc-i440fx-rhel7.0.0'>hvm</type>
    <boot dev='hd'/>
    <boot dev='cdrom'/>
  </os>
  <features>
    <acpi/>
    <apic/>
  </features>
  <clock offset='utc'>
    <timer name='rtc' tickpolicy='catchup'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
  </clock>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>restart</on_crash>
  <pm>
    <suspend-to-mem enabled='no'/>
    <suspend-to-disk enabled='no'/>
  </pm>
  <devices>
    <emulator>/usr/libexec/qemu-kvm</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='/path/to/test.qcow2'/>
      <backingStore/>
      <target dev='hda' bus='virtio'/>
      <alias name='virtio-disk0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
    </disk>
    <controller type='pci' index='0' model='pci-root'>
      <alias name='pci.0'/>
    </controller>
    <controller type='ide' index='0'>
      <alias name='ide'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
    </controller>
    <controller type='virtio-serial' index='0'>
      <alias name='virtio-serial0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
    </controller>
    <controller type='usb' index='0' model='piix3-uhci'>
      <alias name='usb'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
    </controller>
    <interface type='bridge'>
      <mac address='52:54:00:d2:81:b0'/>
      <source bridge='br0-ovs'/>
      <virtualport type='openvswitch'>
        <parameters interfaceid='abc10709-ebff-4d0f-8761-4b7fdaba0dc0'/>
      </virtualport>
      <target dev='vnet0'/>
      <model type='virtio'/>
      <alias name='net0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>
    <interface type='bridge'>
      <mac address='52:54:00:5e:91:38'/>
      <source bridge='br0-ovs'/>
      <virtualport type='openvswitch'>
        <parameters interfaceid='cceb5703-185a-4f6f-b2ce-a7e273e52bdc'/>
      </virtualport>
      <bandwidth>
        <inbound average='50000'/>
        <outbound average='50000'/>
      </bandwidth>
      <target dev='vnet1'/>
      <model type='virtio'/>
      <alias name='net1'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
    </interface>
    <interface type='bridge'>
      <mac address='52:54:00:b2:77:07'/>
      <source bridge='br0-ovs'/>
      <virtualport type='openvswitch'>
        <parameters interfaceid='674716ab-243d-4137-bc01-aa2c33cca21a'/>
      </virtualport>
      <target dev='vnet6'/>
      <model type='virtio'/>
      <alias name='net2'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/>
    </interface>
    <console type='pty' tty='/dev/pts/4'>
      <source path='/dev/pts/4'/>
      <target type='virtio' port='0'/>
      <alias name='console0'/>
    </console>
    <input type='mouse' bus='ps2'>
      <alias name='input0'/>
    </input>
    <input type='keyboard' bus='ps2'>
      <alias name='input1'/>
    </input>
    <sound model='ich6'>
      <alias name='sound0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
    </sound>
    <video>
      <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
      <alias name='video0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
    </video>
    <memballoon model='virtio'>
      <alias name='balloon0'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
    </memballoon>
  </devices>
  <seclabel type='dynamic' model='dac' relabel='yes'>
    <label>+0:+0</label>
    <imagelabel>+0:+0</imagelabel>
  </seclabel>
</domain>

OpenStack

使用 virsh 来指定xml进行创建虽然能对虚拟机进行生命周期的管理,但是无法对成百上千台的机器进行集中式的管理。

这时候,OpenStack 这个开源的云计算管理平台就出现了。

有了OpenStack,你可以使用 Horizon提供的界面进行虚拟机的管理

来源网络,侵删

来源网络,侵删

也可以使用nova 的 cli 命令进行创建。

nova boot <vm_name> \
--flavor <flavor_id> \
--nic net-id=<net_id>,v4-fixed-ip=<ip> \
--image <image_id/name> \
--config-drive True

说了半天,线于引出了OpenStack,我的工作基本60%的时间都是围绕着它转,OpenStack 是一个开源框架,是使用Python语言开发的最大的项目,具说有数百万行的代码量,是动态语言的一个优秀典范。

关于 OpenStck,你可能不太明白它是做什么的。这里引用我昨天看到的另一篇文章的一个说明:它有点像一个商店,负责管理所有的商品(计算资源、存储资源、网络资源等),卖给用户,但是它本身不制造商品(不具备虚拟化能力),它的商品来自KVM(当然也可以用Xen等其他Hypervisor)。