每天5分钟玩转OpenStack
上QQ阅读APP看书,第一时间看更新

第1章 虚拟化

虚拟化是云计算的基础。简单地说,虚拟化使得在一台物理的服务器上可以跑多台虚拟机,虚拟机共享物理机的CPU、内存、IO硬件资源,但逻辑上虚拟机之间是相互隔离的。

物理机我们一般称为宿主机(Host),宿主机上面的虚拟机称为客户机(Guest)。

那么Host是如何将自己的硬件资源虚拟化,并提供给Guest使用的呢?

这个主要是通过一个叫做Hypervisor的程序实现的。

根据Hypervisor的实现方式和所处的位置,虚拟化又分为两种:1型虚拟化和2型虚拟化。

1.1 1型虚拟化

Hypervisor直接安装在物理机上,多个虚拟机在Hypervisor上运行。Hypervisor实现方式一般是一个特殊定制的Linux系统。Xen和VMWare的ESXi都属于这个类型,如图1-1所示。

图1-1

1.2 2型虚拟化

物理机上首先安装常规的操作系统,比如Redhat、Ubuntu和Windows。Hypervisor作为OS上的一个程序模块运行,并对虚拟机进行管理。KVM、VirtualBox和VMWare Workstation都属于这个类型,如图1-2所示。

图1-2

理论上讲:

1.型虚拟化一般对硬件虚拟化功能进行了特别优化,性能上比2型要高;

2.型虚拟化因为基于普通的操作系统,会比较灵活,比如支持虚拟机嵌套。嵌套意味着可以在KVM虚拟机中再运行KVM。

1.3 KVM

下面重点介绍KVM这种2型虚拟化技术。

1.3.1 基本概念

在x86平台上最热门、运用最广泛的虚拟化方案莫过于KVM了。OpenStack对KVM支持得也最好,我们的教程也理所当然选择KVM作为实验环境的Hypervisor。

KVM全称是Kernel-Based Virtual Machine。也就是说KVM是基于Linux内核实现的。

KVM有一个内核模块叫kvm.ko,只用于管理虚拟CPU和内存。

那IO的虚拟化,比如存储和网络设备由谁实现呢?

这个就交给Linux内核和Qemu来实现。

说白了,作为一个Hypervisor,KVM本身只关注虚拟机调度和内存管理这两个方面。IO外设的任务交给Linux内核和Qemu。

Libvirt

大家在网上看KVM相关文章的时候肯定经常会看到Libvirt这个东西。

Libvirt是啥?

简单地说就是KVM的管理工具。

其实,Libvirt除了能管理KVM这种Hypervisor,还能管理Xen,VirtualBox等。

OpenStack底层也使用Libvirt,所以很有必要学习一下。

Libvirt包含3个东西:后台daemon程序libvirtd、API库和命令行工具virsh。

  • libvirtd是服务程序,接收和处理API请求;
  • API库使得其他人可以开发基于Libvirt的高级工具,比如virt-manager,这是个图形化的KVM管理工具,后面我们也会介绍;
  • virsh是我们经常要用的KVM命令行工具,后面会有使用的示例。

作为KVM和OpenStack的实施人员,virsh和virt-manager是一定要会用的。

今天5分钟差不多了,下一节我们来玩KVM。

1.3.2 KVM实操

1.准备KVM实验环境

上一节说了,KVM是2型虚拟化,是运行在操作系统之上的,所以先要装一个Linux。Ubuntu、Redhat、CentOS都可以,这里以Ubuntu14.04为例。

基本的Ubuntu操作系统装好之后,安装KVM需要的包:

        $ sudo apt-get install qemu-kvm qemu-system libvirt-bin virt-manager
bridge-utils vlan

通过这些安装包顺便复习一下上一节介绍的KVM的相关知识。

  • qemu-kvm和qemu-system是KVM和QEMU的核心包,提供CPU、内存和IO虚拟化功能。
  • libvirt-bin就是libvirt,用于管理KVM等Hypervisor。
  • virt-manager是KVM图形化管理工具。
  • bridge-utils和vlan,主要是网络虚拟化需要,KVM网络虚拟化的实现是基于linux-bridge和VLAN,后面我们会讨论。

Ubuntu默认不安装图形界面,手工安装一下:

      sudo apt-get install xinit sudo apt-get install gdm sudo apt-get install
kubuntu-desktop

apt默认会到官网上去下载安装包,速度很慢,我们可以使用国内的镜像站点。

配置/etc/apt/sources.list:

        deb http://mirrors.163.com/ubuntu/ trusty main restricted universe
multiverse
        deb http://mirrors.163.com/ubuntu/ trusty-security main restricted
universe multiverse
        deb http://mirrors.163.com/ubuntu/ trusty-updates main restricted
universe multiverse
        deb http://mirrors.163.com/ubuntu/ trusty-proposed main restricted
universe multiverse
        deb http://mirrors.163.com/ubuntu/ trusty-backports main restricted
universe multiverse
        deb-src http://mirrors.163.com/ubuntu/ trusty main restricted universe
multiverse
        deb-src http://mirrors.163.com/ubuntu/ trusty-security main restricted
universe multiverse
        deb-src http://mirrors.163.com/ubuntu/ trusty-updates main restricted
universe multiverse
        deb-src http://mirrors.163.com/ubuntu/ trusty-proposed main restricted
universe multiverse
        deb-src http://mirrors.163.com/ubuntu/ trusty-backports main restricted
universe multiverse

然后执行下面命令更新安装包index:

     # apt update

Redhat和CentOS安装相对简单,安装过程中选择虚拟化和图形组件就可以了。

小窍门:Ubuntu默认是不允许root通过ssh直接登录的,可以修改/etc/ssh/sshd_config,设置:

     PermitRootLogin yes

然后重启ssh服务即可:

     # service ssh restart ssh
     stop/waiting ssh start/running, process 27639

在虚拟机上做实验

作为2型虚拟化的KVM,支持虚拟化嵌套,这使得我们可以在虚拟机中实验KVM。

比如我在VMWare Workstation中安装了一个Ubuntu14.04的虚拟机,为了让KVM能创建。

嵌套的虚机,要把CPU的虚拟化功能打开。如图1-3所示,在VMWare中设置以下CPU的模式。

图1-3

Ubuntu启动后,用以下命令确认CPU支持虚拟化:

     # egrep -o '(vmx|svm)' /proc/cpuinfo
     # vmx

确认Libvirtd服务已经启动:

     # service libvirt-bin status
     libvirt-bin start/running, process 1478

2.启动第一个KVM虚机

本节演示如何使用virt-manager启动KVM虚机。

首先通过命令virt-manager启动图形界面,如图1-4所示。

     # virt-manager

图1-4

单击图1-4中框选的图标创建虚机,如图1-5所示。

图1-5

给虚机命名为kvm1,这里选择从哪里启动虚机。如果是安装新的OS,可以选择第一项。如果已经有安装好的镜像文件,选最后一项(如图1-5所示)。

接下来需要告诉virt-manager镜像的位置,如图1-6所示。

图1-6

单击“Browser”,打开如图1-7所示的界面。

图1-7

在我的系统中存放了一个cirros-0.3.3-x86_64-disk.img镜像文件,单击“Choose Volume”,如图1-8所示。cirros是一个很小的linux镜像,非常适合测试用,大家可以到http://download.cirros-cloud.net/下载,然后放到/var/lib/libvirt/images/目录下,这是KVM默认查找镜像文件的地方。

图1-8

单击“Forward”,为虚拟机分配CPU和内存,如图1-9所示。

图1-9

单击“Forward”,再确认一下信息,就可以启动虚机了,如图1-10所示。

图1-10

单击“Finish”,virt-manager会打开虚机kvm1的控制台窗口,可以看到启动情况,如图1-11所示。

图1-11

virt-manager可以对虚机进行各种管理操作,界面直观友好,很容易上手,如图1-12所示。

图1-12

同时我们也可以用命令virsh管理虚机,比如查看宿主机上的虚机:

     root@ubuntu:~# virsh list
     Id Name State
     ---------------
     8 kvm1 running

至此,第一个虚机已经跑起来了,采用的都是默认设置,后面我们会逐步讨论有关虚机更细节的内容,比如存储和网卡的设置。

3.远程管理KMV虚机

上一节我们通过virt-manager在本地主机上创建并管理KVM虚机。其实virt-manager也可以管理其他宿主机上的虚机。只需要简单地将宿主机添加进来,如图1-13所示。

图1-13

填入宿主机的相关信息,确定即可,如图1-14所示。

图1-14

接下来,我们就可以像管理本地虚机一样去管理远程宿主机上的虚机了,如图1-15所示。

图1-15

这里其实有一个要配置的地方。

因为KVM(准确说是Libvirt)默认不接受远程管理,需要按下面的内容配置被管理宿主机中的两个文件:

     /etc/default/libvirt-bin
     start_libvirtd="yes"
     libvirtd_opts="-d -l"
     /etc/libvirt/libvirtd.conf
     listen_tls = 0
     listen_tcp = 1
     unix_sock_group = "libvirtd"
     unix_sock_ro_perms = "0777"
     unix_sock_rw_perms = "0770"
     auth_unix_ro = "none"
     auth_unix_rw = "none"
     auth_tcp = "none"

然后重启Libvirtd服务就可以远程管理了。

     service libvirt-bin restart

1.4 KVM虚拟化原理

前面我们成功地把KVM跑起来了,有了些感性认识,这个对于初学者非常重要。不过还不够,我们多少得了解一些KVM的实现机制,这对以后的工作会有帮助。

1.4.1 CPU虚拟化

KVM的虚拟化是需要CPU硬件支持的。还记得我们在前面的章节讲过用命令来查看CPU是否支持KVM虚拟化吗?

     root@ubuntu:~# egrep -o '(vmx|svm)' /proc/cpuinfo
     vmx

如果有输出vmx或者svm,就说明当前的CPU支持KVM。CPU厂商Intel和AMD都支持虚拟化了,除非是非常老的CPU。

一个KVM虚机在宿主机中其实是一个qemu-kvm进程,与其他Linux进程一样被调度。

比如,在我的实验机上运行的虚机kvm1在宿主机中ps能看到相应的进程,如图1-16所示。

图1-16

虚机中的每一个虚拟vCPU则对应qemu-kvm进程中的一个线程,如图1-17所示。

图1-17

在这个例子中,宿主机有两个物理CPU,上面起了两个虚机VM1和VM2。

VM1有两个vCPU,VM2有4个vCPU。可以看到VM1和VM2分别有两个和4个线程在两个物理CPU上调度。

这里也演示了另一个知识点,即虚机的vCPU总数可以超过物理CPU数量,这个叫CPU overcommit(超配)。

KVM允许overcommit,这个特性使得虚机能够充分利用宿主机的CPU资源,但前提是在同一时刻,不是所有的虚机都满负荷运行。

当然,如果每个虚机都很忙,反而会影响整体性能,所以在使用overcommit的时候,需要对虚机的负载情况有所了解,需要测试。

1.4.2 内存虚拟化

KVM通过内存虚拟化共享物理系统内存,动态分配给虚拟机,如图1-18所示。

图1-18

为了在一台机器上运行多个虚拟机,KVM需要实现VA(虚拟内存)→ PA(物理内存)→ MA(机器内存)之间的地址转换。虚机OS控制虚拟地址到客户内存物理地址的映射(VA →PA),但是虚机OS不能直接访问实际机器内存,因此KVM需要负责映射客户物理内存到实际机器内存(PA → MA)。具体的实现就不做过多介绍了,大家有兴趣可以查查资料。

还有一点提醒大家,内存也是可以overcommit的,即所有虚机的内存之和可以超过宿主机的物理内存。但使用时也需要充分测试,否则性能会受影响。

1.4.3 存储虚拟化

KVM的存储虚拟化是通过存储池(Storage Pool)和卷(Volume)来管理的。

Storage Pool是宿主机上可以看到的一片存储空间,可以是多种类型,后面会详细讨论。Volume是在Storage Pool中划分出的一块空间,宿主机将Volume分配给虚拟机,Volume在虚拟机中看到的就是一块硬盘。

下面我们学习不同类型的Storage Pool。

1.目录类型的Storage Pool

文件目录是最常用的Storage Pool类型。KVM将宿主机目录/var/lib/libvirt/images/作为默认的Storage Pool。

那么Volume是什么呢?

答案就是该目录下面的文件了,一个文件就是一个Volume。

大家是否还记得我们之前创建第一个虚机kvm1的时候,就是将镜像文件cirros-0.3.3-x86_64-disk.img放到了这个目录下。文件cirros-0.3.3-x86_64-disk.img也就是Volume,对于kvm1来说,就是它的启动磁盘了。如图1-19所示。

图1-19

那KVM是怎么知道要把/var/lib/libvirt/images目录当作默认Storage Pool的呢?

实际上KVM所有可以使用的Storage Pool都定义在宿主机的/etc/libvirt/storage目录下,每个Pool一个xml文件,默认有一个default.xml,其内容如图1-20所示。

图1-20

注意:Storage Pool的类型是“dir”,目录的路径就是/var/lib/libvirt/images。

下面我们为虚机kvm1添加一个新的磁盘,看看有什么变化。

在virt-manager中打开kvm1的配置页面,右键添加新硬件,如图1-21所示。

图1-21

在默认Pool中创建一个8GB的卷,如图1-22所示。

图1-22

单击“Finish”,可以看到新磁盘的信息,如图1-23所示。

图1-23

在/var/lib/libvirt/images/下多了一个8GB的文件kvm1.img:

        root@ubuntu:~# ls -l /var/lib/libvirt/images/
        total 14044
        -rw-r--r-- 1 root root   14417920 Sep  4 11:24
cirros-0.3.3-x86_64-disk.img
        -rw------- 1 root root 8589934592 Sep  4 21:39 kvm1.img

使用文件做Volume有很多优点:存储方便、移植性好、可复制、可远程访问。

前面几个优点都很好理解,这里对“可远程访问”多解释一下。

远程访问的意思是镜像文件不一定都放置到宿主机本地文件系统中,也可以存储在通过网络连接的远程文件系统,比如NFS,或者是分布式文件系统中,比如GlusterFS。

这样镜像文件就可以在多个宿主机之间共享,便于虚机在不同宿主机之间做Live Migration;如果是分布式文件系统,多副本的特性还可以保证镜像文件的高可用。

KVM支持多种Volume文件格式,在添加Volume时可以选择,如图1-24所示。

图1-24

raw是默认格式,即原始磁盘镜像格式,移植性好,性能好,但大小固定,不能节省磁盘空间。

qcow2是推荐使用的格式,cow表示copy on write,能够节省磁盘空间,支持AES加密,支持zlib压缩,支持多快照,功能很多。

vmdk是VMWare的虚拟磁盘格式,也就是说VMWare虚机可以直接在KVM上运行。

下一节介绍LVM类型的Storage Pool。

2.LVM类型的Storage Pool

不仅一个文件可以分配给客户机作为虚拟磁盘,宿主机上VG中的LV也可以作为虚拟磁盘分配给虚拟机使用。

不过,LV由于没有磁盘的MBR引导记录,不能作为虚拟机的启动盘,只能作为数据盘使用。

这种配置下,宿主机上的VG就是一个Storage Pool,VG中的LV就是Volume。

LV的优点是有较好的性能;不足的地方是管理和移动性方面不如镜像文件,而且不能通过网络远程使用。

下面举个例子。

首先,在宿主机上创建了一个容量为10GB的VG,命名为HostVG,如图1-25所示。

图1-25

然后创建了一个Storage Pool的定义文件/etc/libvirt/storage/HostVG.xml,如图1-26所示。

图1-26

然后通过virsh命令创建新的Storage Pool“HostVG”,如图1-27所示。

图1-27

并启用这个HostVG,如图1-28所示。

图1-28

现在我们可以在virt-manager中为虚机kvm1添加LV的虚拟磁盘了。如图1-29所示。

图1-29

单击“Browse”,如图1-30所示。

图1-30

可以看到HostVG已经在Stroage Pool的列表中了,选择HostVG,如图1-31所示。

图1-31

单击“New Volume”,为volume命名为newlv,并设置大小100MB,如图1-32所示。

图1-32

单击“Finish”,newlv创建成功,如图1-33所示。

图1-33

单击“Choose Volume”,如图1-34所示。

图1-34

单击“Finish”,确认将newlv作为volume添加到kvm1,如图1-35所示。

图1-35

新volume添加成功。在宿主机上则多了一个命名为newlv的LV,如图1-36所示。

图1-36

3.其他类型的Storage Pool

KVM还支持iSCSI、Ceph等多种类型的Storage Pool,这里就不一一介绍了,最常用的就是目录类型,其他类型可以参考文档http://libvirt.org/storage.html

下一节我们将开始讨论KVM的网络虚拟化原理。

1.5 网络虚拟化

网络虚拟化是虚拟化技术中最复杂的部分,学习难度最大。但因为网络是虚拟化中非常重要的资源,所以再硬的骨头也必须要把它啃下来。为了让大家对虚拟化网络的复杂程度有一个直观的认识,请看图1-37所示。

图1-37

这是OpenStack官网上给出的计算节点(可以理解为KVM的宿主机)虚拟网络的逻辑图,上面的网络设备很多,层次也很复杂,我第一次看到这张图,也着实被吓了一跳。

不过大家也不要怕,万丈高楼平地起,虚拟网络再复杂,也是由一些基础的组件构成的。只要我们将这些基础组件的概念和它们之间的逻辑关系搞清楚了,就能深刻理解虚拟网络的架构,那么云环境下的虚拟化网络也就不在话下了。

下面我们来学习网络虚拟化中最重要的两个东西:Linux Bridge和VLAN。

1.5.1 Linux Bridge

1.基本概念

假设宿主机有1块与外网连接的物理网卡eth0,上面跑了1个虚机VM1,现在有个问题是:

如何让VM1能够访问外网?

至少有两种方案:

(1)将物理网卡eth0直接分配给VM1。

但实施这个方案随之带来的问题很多:宿主机就没有网卡,无法访问了;新的虚机,比如VM2也没有网卡。

(2)给VM1分配一个虚拟网卡vnet0,通过Linux Bridge br0将eth0和vnet0连接起来,如图1-38所示。这个是我们推荐的方案。

图1-38

Linux Bridge是Linux上用来做TCP/IP二层协议交换的设备,其功能大家可以简单地理解为是一个二层交换机或者Hub。多个网络设备可以连接到同一个Linux Bridge,当某个设备收到数据包时,Linux Bridge会将数据转发给其他设备。

在上面这个例子中,当有数据到达eth0时,br0会将数据转发给vnet0,这样VM1就能接收到来自外网的数据。

反过来,VM1发送数据给vnet0,br0也会将数据转发到eth0,从而实现了VM1与外网的通信。

现在我们增加一个虚机VM2,如图1-39所示。

图1-39

VM2的虚拟网卡vnet1也连接到了br0上。现在VM1和VM2之间可以通信,同时VM1和VM2也都可以与外网通信。

有了上面的基础支持,下一节将演示如何在实验环境中实现这套虚拟网络。

2.动手实践虚拟网络

本节将演示如何在实验环境中实现如图1-40所示的虚拟网络。

图1-40

3.配置Linux Bridge br0

编辑/etc/network/interfaces,配置br0。

下面用vmdiff展示了对/etc/network/interfaces的修改,如图1-41所示。

图1-41

有两点需要注意:

  • 之前宿主机的IP是通过dhcp配置在eth0上的;创建Linux Bridge之后,IP就必须放到br0上了。
  • 在br0的配置信息中请注意最后一行bridge_ports eth0,其作用就是将eth0挂到br0上。

重启宿主机,查看IP配置,可以看到IP已经放到br0上了。

        # ifconfig
        br0       Link encap:Ethernet  HWaddr 00:0c:29:8d:ec:be
                  inet                     addr:192.168.111.107            Bcast:192.168.111.255
Mask:255.255.255.0
                  inet6 addr: fe80::20c:29ff:fe8d:ecbe/64 Scope:Link
                  UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
                  RX packets:22573 errors:0 dropped:0 overruns:0 frame:0
                  TX packets:2757 errors:0 dropped:0 overruns:0 carrier:0
                  collisions:0 txqueuelen:0
                  RX bytes:4927580 (4.9 MB)  TX bytes:368895 (368.8 KB)
        eth0      Link encap:Ethernet  HWaddr 00:0c:29:8d:ec:be
                  inet6 addr: fe80::20c:29ff:fe8d:ecbe/64 Scope:Link
                  UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
                  RX packets:24388 errors:0 dropped:0 overruns:0 frame:0
                  TX packets:2816 errors:0 dropped:0 overruns:0 carrier:0
                  collisions:0 txqueuelen:1000
                  RX bytes:5590438 (5.5 MB)  TX bytes:411558 (411.5 KB)
        lo        Link encap:Local Loopback
                  inet addr:127.0.0.1  Mask:255.0.0.0
                  inet6 addr: ::1/128 Scope:Host
                  UP LOOPBACK RUNNING  MTU:65536  Metric:1
                  RX packets:146 errors:0 dropped:0 overruns:0 frame:0
                  TX packets:146 errors:0 dropped:0 overruns:0 carrier:0
                  collisions:0 txqueuelen:0
                  RX bytes:10521 (10.5 KB)  TX bytes:10521 (10.5 KB)
        virbr0    Link encap:Ethernet  HWaddr 72:db:fb:f2:19:91
                  inet                      addr:192.168.122.1             Bcast:192.168.122.255
Mask:255.255.255.0
                  UP BROADCAST MULTICAST  MTU:1500  Metric:1
                  RX packets:0 errors:0 dropped:0 overruns:0 frame:0
                  TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
                  collisions:0 txqueuelen:0
                  RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

用brctl show查看当前Linux Bridge的配置。eth0已经挂到br0上了。

     # brctl show
     bridge name   bridge id          STP enabled  interfaces
     br0           8000.000c298decbe  no                 eth0
     virbr0        8000.000000000000  yes

除了br0,大家应该注意到还有一个virbr0的Bridge,而且virbr0上已经配置了IP地址192.168.122.1。

virbr0的作用我们会在后面介绍。

在宿主机中,CloudMan(作者)已经提前创建好了虚机VM1和VM2,现在都处于关机状态。

     # virsh list --all
       Id    Name                           State
     ----------------------------------------------------
       -     VM1                            shut off
       -     VM2                            shut off

4.配置VM1

下面我们在virt-manager中查看一下VM1的网卡配置,如图1-42所示。为了使大家能够熟练使用命令行工具virsh和图形工具virt-manager,CloudMan在演示的时候会同时用到它们,两个工具都很重要。

图1-42

可以看到虚拟网卡的source device,我们选择的是br0。

下面我们启动VM1,看会发生什么?

       # virsh start VM1
       Domain VM1 started
       # brctl show
       bridge name     bridge id          STP enabled  interfaces
       br0             8000.000c298decbe  no                 eth0
vnet0
      virbr0  8000.000000000000  yes

brctl show告诉我们,br0下面添加了一个vnet0设备,通过virsh确认这就是VM1的虚拟网卡。

      # virsh domiflist VM1
      Interface  Type       Source     Model       MAC
      -------------------------------------------------------
      vnet0      bridge     br0        rtl8139     52:54:00:75:dd:1a

VM1的IP是DHCP获得的(设置静态IP当然也可以),通过virt-manager控制台登录VM1,查看IP。

        # ifconfig
        eth0      Link encap:Ethernet  HWaddr 52:54:00:75:dd:1a
                  inet                     addr:192.168.111.106           Bcast:192.168.111.255
Mask:255.255.255.0
                  inet6 addr: fe80::5054:ff:fe75:dd1a/64 Scope:Link
                  UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
                  RX packets:400 errors:0 dropped:0 overruns:0 frame:0
                  TX packets:101 errors:0 dropped:0 overruns:0 carrier:0
                  collisions:0 txqueuelen:1000
                  RX bytes:41950 (41.9 KB)  TX bytes:12583 (12.5 KB)

VM1通过DHCP拿到的IP是192.168.111.106,与宿主机(IP为192.168.111.107)是同一个网段。Ping一下外网。

     root@VM1:~# ping www.baidu.com
     PING www.a.shifen.com (180.97.33.108) 56(84) bytes of data.
     64 bytes from 180.97.33.108: icmpseq=1 ttl=53 time=34.9 ms
     64 bytes from 180.97.33.108: icmpseq=2 ttl=53 time=36.2 ms
     64 bytes from 180.97.33.108: icmpseq=3 ttl=53 time=38.8 ms

没问题,可以访问。

另外,在VM1中虚拟网卡是eth0,并不是vnet0。

vnet0是该虚拟网卡在宿主机中对应的设备名称,其类型是TAP设备,这里需要注意一下。

5.配置VM2

跟VM1一样,VM2的虚拟网卡也挂在br0上,启动VM2,查看网卡信息。

     # virsh start VM2
     Domain VM2 started
     # brctl show
     bridge name   bridge id          STP enabled  interfaces
     br0           8000.000c298decbe  no                 eth0
                                                          vnet0
                                                          vnet1
     virbr0        8000.000000000000  yes

br0下面多了vnet1,通过virsh确认这就是VM2的虚拟网卡。

     # virsh domiflist VM2
     Interface  Type       Source     Model       MAC
     -------------------------------------------------------
     vnet1      bridge     br0        rtl8139     52:54:00:cf:33:a1

VM2通过DHCP拿到的IP是192.168.111.108,登录VM2,验证网络的连通性Ping VM1:

     root@VM2:~# ping VM1
     PING VM1 (192.168.111.106) 56(84) bytes of data.
     64 bytes from 192.168.111.106: icmpseq=1 ttl=64 time=4.54 ms
     64 bytes from 192.168.111.106: icmpseq=2 ttl=64 time=1.63 ms
     64 bytes from 192.168.111.106: icmpseq=3 ttl=64 time=2.16 ms

Ping宿主机:

     root@VM2:~# ping 192.168.111.107
     PING 192.168.111.107 (192.168.111.107) 56(84) bytes of data.
     64 bytes from 192.168.111.107: icmpseq=1 ttl=64 time=1.02 ms
     64 bytes from 192.168.111.107: icmpseq=2 ttl=64 time=0.052 ms
     64 bytes from 192.168.111.107: icmpseq=3 ttl=64 time=0.064 ms

Ping外网:

     root@VM2:~# ping www.baidu.com
     PING www.a.shifen.com (180.97.33.107) 56(84) bytes of data.
     64 bytes from 180.97.33.107: icmpseq=1 ttl=53 time=53.9 ms
     64 bytes from 180.97.33.107: icmpseq=2 ttl=53 time=45.0 ms
     64 bytes from 180.97.33.107: icmpseq=3 ttl=53 time=44.2 ms

可见,通过br0这个Linux Bridge,我们实现了VM1、VM2、宿主机和外网这四者之间的数据通信。

6.理解virbr0

virbr0是KVM默认创建的一个Bridge,其作用是为连接其上的虚机网卡提供NAT访问外网的功能。

virbr0默认分配了一个IP 192.168.122.1,并为连接其上的其他虚拟网卡提供DHCP服务。

下面我们演示如何使用virbr0。

在virt-manager打开VM1的配置界面,网卡Source device选择“default”,将VM1的网卡挂在virbr0上,如图1-43所示。

图1-43

启动VM1,brctl show可以查看到vnet0已经挂在了virbr0上。

     # brctl show
     bridge name   bridge id          STP enabled  interfaces
     br0           8000.000c298decbe  no                 eth0
     virbr0        8000.fe540075dd1a  yes                vnet0

用virsh命令确认vnet就是VM1的虚拟网卡。

     # virsh domiflist VM1
     Interface  Type       Source     Model       MAC
     -------------------------------------------------------
     vnet0      network    default    rtl8139     52:54:00:75:dd:1a

virbr0使用dnsmasq提供DHCP服务,可以在宿主机中查看该进程信息。

        # ps -elf|grep dnsmasq
        5 S libvirt+  2422     1  0  80   0 -  7054 poll_s 11:26 ?   00:00:00
/usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf

在/var/lib/libvirt/dnsmasq/目录下有一个default.leases文件,当VM1成功获得DHCP的IP后,可以在该文件中查看到相应的信息。

     # cat /var/lib/libvirt/dnsmasq/default.leases
     1441525677 52:54:00:75:dd:1a 192.168.122.6 ubuntu *

上面显示192.168.122.6已经分配给MAC地址为52:54:00:75:dd:1a的网卡,这正是vnet0的MAC。之后就可以使用该IP访问VM1了。

        # ssh 192.168.122.6
        root@192.168.122.6's password:
        Welcome to Ubuntu 14.04.2 LTS (GNU/Linux 3.16.0-30-generic x86_64)
        Last login: Sun Sep  6 01:30:23 2015
        root@VM1:~# ifconfig
        eth0      Link encap:Ethernet  HWaddr 52:54:00:75:dd:1a
                  inet                     addr:192.168.122.6            Bcast:192.168.122.255
Mask:255.255.255.0
                  inet6 addr: fe80::5054:ff:fe75:dd1a/64 Scope:Link
                  UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
                  RX packets:61 errors:0 dropped:0 overruns:0 frame:0
                  TX packets:66 errors:0 dropped:0 overruns:0 carrier:0
                  collisions:0 txqueuelen:1000
                  RX bytes:7453 (7.4 KB)  TX bytes:8649 (8.6 KB)

Ping一下外网。

     root@VM1:~# ping www.baidu.com
     PING www.a.shifen.com (180.97.33.107) 56(84) bytes of data.
     64 bytes from 180.97.33.107: icmpseq=1 ttl=52 time=36.9 ms
     64 bytes from 180.97.33.107: icmpseq=2 ttl=52 time=119 ms
     64 bytes from 180.97.33.107: icmpseq=3 ttl=52 time=88.5 ms
     64 bytes from 180.97.33.107: icmpseq=4 ttl=52 time=38.0 ms
     64 bytes from 180.97.33.107: icmpseq=5 ttl=52 time=122 ms

没有问题,可以访问外网,说明NAT起作用了。

需要说明的是,使用NAT的虚机VM1可以访问外网,但外网无法直接访问VM1。

因为VM1发出的网络包源地址并不是192.168.122.6,而是被NAT替换为宿主机的IP地址了。

这个与使用br0不一样,在br0的情况下,VM1通过自己的IP直接与外网通信,不会经过NAT地址转换。

1.5.2 VLAN

1.基本概念

LAN表示Local Area Network,本地局域网,通常使用Hub和Switch来连接LAN中的计算机。

一般来说,两台计算机连入同一个Hub或者Switch时,它们就在同一个LAN中。

一个LAN表示一个广播域,其含义是:LAN中的所有成员都会收到任意一个成员发出的广播包。

VLAN表示Virtual LAN。一个带有VLAN功能的switch能够将自己的端口划分出多个LAN。

计算机发出的广播包可以被同一个LAN中其他计算机收到,但位于其他LAN的计算机则无法收到。

简单地说,VLAN将一个交换机分成了多个交换机,限制了广播的范围,在二层上将计算机隔离到不同的VLAN中。

比方说,有两组机器,Group A和B。我们想配置成Group A中的机器可以相互访问,Group B中的机器也可以相互访问,但是A和B中的机器无法互相访问。

一种方法是使用两个交换机,A和B分别接到一个交换机。

另一种方法是使用一个带VLAN功能的交换机,将A和B的机器分别放到不同的VLAN中。

请注意,VLAN的隔离是二层上的隔离,A和B无法相互访问指的是二层广播包(比如arp)无法跨越VLAN的边界。

但在三层上(比如IP)是可以通过路由器让A和B互通的。概念上一定要分清。

现在的交换机几乎都是支持VLAN的。

通常交换机的端口有两种配置模式:Access和Trunk,如图1-44所示。

图1-44

  • Access口

这些端口被打上了VLAN的标签,表明该端口属于哪个VLAN。

不同VLAN用VLAN ID来区分,VLAN ID的范围是1~4096。

Access口都是直接与计算机网卡相连的,这样从该网卡出来的数据包流入Access口后,就会被打上了所在VLAN的标签。

Access口只能属于一个VLAN。

  • Trunk口

假设有两个交换机A和B。

A上有VLAN1(红)、VLAN2(黄)、VLAN3(蓝);B上也有VLAN1、VLAN 2、VLAN 3。

那如何让A和B上相同VLAN之间能够通信呢?办法是将A和B连起来,而且连接A和B的端口要允许VLAN1、2、3三个VLAN的数据都能够通过。这样的端口就是Trunk口了。

VLAN1、2、3的数据包在通过Trunk口到达对方交换机的过程中始终带着自己的VLAN标签。

了解了VLAN的概念之后,我们来看KVM虚拟化环境下是如何实现VLAN的。还是先看图,如图1-45所示。

图1-45

eth0是宿主机上的物理网卡,有一个命名为eth0.10的子设备与之相连。

eth0.10就是VLAN设备了,其VLAN ID就是VLAN 10。

eth0.10挂在命名为brvlan10的Linux Bridge上,虚机VM1的虚拟网卡vent0也挂在brvlan10上。

这样的配置,其效果就是:

  • 宿主机用软件实现了一个交换机(当然是虚拟的),上面定义了一个VLAN10。
  • eth0.10,brvlan10和vnet0都分别接到VLAN10的Access口上。而eth0就是一个Trunk口。
  • VM1通过vnet0发出来的数据包会被打上VLAN10的标签。

eth0.10的作用是:定义了VLAN10。

brvlan10的作用是:Bridge上的其他网络设备自动加入到VLAN10中。

我们再增加一个VLAN20,如图1-46所示。

图1-46

这样虚拟交换机就有两个VLAN了,VM1和VM2分别属于VLAN10和VLAN20。

对于新创建的虚机,只需要将其虚拟网卡放入相应的Bridge,就能控制其所属的VLAN。

VLAN设备总是以母子关系出现,母子设备之间是一对多的关系。

一个母设备(eth0)可以有多个子设备(eth0.10,eth0.20 ……),而一个子设备只有一个母设备。

下面我们来看如何在实验环境中实施和配置VLAN网络。

2.配置VLAN

编辑/etc/network/interfaces,配置eth0.10、brvlan10、eth0.20和brvlan20。

下面用vmdiff展示了对/etc/network/interfaces的修改,如图1-47所示。

图1-47

重启宿主机,ifconfig各个网络接口,如图1-48所示。

图1-48

用brctl show查看当前Linux Bridge的配置。

eth0.10和eth0.20分别挂在brvlan10和brvlan20上了,如图1-49所示。

图1-49

在宿主机中已经提前创建好了虚机VM1和VM2,现在都处于关机状态,如图1-50所示。

图1-50

3.配置VM1

在virt-manager中,将VM1的虚拟网卡挂到brvlan10上,如图1-51所示。

图1-51

启动VM1,如图1-52所示。

图1-52

查看Bridge,发现brvlan10已经连接了一个vnet0设备,如图1-53所示。

图1-53

通过virsh确认这就是VM1的虚拟网卡,如图1-54所示。

图1-54

4.配置VM2

类似的,将VM2的网卡挂在brvlan20上,如图1-55所示。

图1-55

启动VM2,如图1-56所示。

图1-56

查看Bridge,发现brvlan20已经连接了一个vnet1设备,如图1-57所示。

图1-57

通过virsh确认这就是VM2的虚拟网卡,如图1-58所示。

图1-58

5.验证VLAN的隔离性

为了验证VLAN10和VLAN20之间的隔离,我们为VM1和VM2配置同一网段的IP。

配置VM1的IP,如图1-59所示。

图1-59

配置VM2的IP,如图1-60所示。

图1-60

Ping测试结果:VM1与VM2是不通的,如图1-61所示。

图1-61

原因如下:

(1)VM2向VM1发Ping包之前,需要知道VM1的IP 192.168.100.10所对应的MAC地址。VM2会在网络上广播ARP包,其作用就是询问“谁知道192.168.100.10的MAC地址是多少?”

(2)ARP是二层协议,VLAN的隔离作用使得ARP只能在VLAN20范围内广播,只有brvlan20和eth0.20能收到,VLAN10里的设备是收不到的。VM1无法应答VM2发出的ARP包。

(3)VM2拿不到VM1 vnet0的MAC地址,也就Ping不到VM1。

1.5.3 Linux Bridge + VLAN =虚拟交换机

现在对KVM的网络虚拟化做个总结。

(1)物理交换机存在多个VLAN,每个VLAN拥有多个端口。

同一VLAN端口之间可以交换转发,不同VLAN端口之间隔离。所以交换机包含两层功能:交换与隔离。

(2)Linux的VLAN设备实现的是隔离功能,但没有交换功能。

一个VLAN母设备(比如eth0)不能拥有两个相同ID的VLAN子设备,因此也就不可能出现数据交换情况。

(3)Linux Bridge专门实现交换功能。

将同一VLAN的子设备都挂载到一个Bridge上,设备之间就可以交换数据了。

总结起来,Linux Bridge加VLAN在功能层面完整模拟现实世界里的二层交换机。

eth0相当于虚拟交换机上的trunk口,允许vlan10和vlan20的数据通过。

eth0.10,vnet0和brvlan10都可以看着vlan10的access口。

eth0.20,vnet1和brvlan20都可以看着vlan20的access口。