Xen虚拟化初识

稍微复习了一下操作系统, 从今天真正开始接触虚拟化相关.

虚拟化技术的分类

当前虚拟化技术有这些:

  • 模拟: Emulation, 其中最著名的一个模拟器就是QEMU, PearPC和Bochs.
    • 模拟的最大特性就是上层运行的系统架构可以和下层或者说宿主的系统架构完全不同, 例如我们在x86平台上运行的ARM的安卓模拟器
  • 完全虚拟化, Full Virtualization, 或者说Native Virtualization
    • 这种虚拟化架构要求上层的Guest和Host同样的架构, 同样也正因为这种架构使得指令集的虚拟化更加简单(因为CPU的指令集架构完全相同)
    • 通过HVM硬件辅助虚拟化来提升性能.
    • VMware研发的BT技术(二进制翻译)
  • 半虚拟化, Para-Virtualization, 最著名的解决方案是XEN, UML(User-Mode Linux)
    • 通过HyperVisor的来进行Hyper Call进行调用, 这要求虚拟化的Guest OS的内核适应, 明确知道自己是运行在虚拟化环境下.
  • OS级别的虚拟化 ( 容器级别的虚拟化 )
    • 这一种虚拟化没有VMM, 只是将用户空间进行切割. 最著名的就是Docker啦, 还有OpenVZ, LXC(Linux Container)
  • 库级别的虚拟化, 例如Wine
  • 应用程序级别的虚拟化, 例如JVM, PVM(Python)

一型虚拟化

将我们的HyperVisor直接安装在硬件上(就是说宿主机没有OS), 这种叫做1型虚拟化.

二型虚拟化

将HyperVisor作为一个进程运行在操作系统上, 这是2型虚拟化.

这就很显然了. 1型虚拟化肯定是性能上更好的, 而2型的优点在于, 存在一个典型的操作系统, 比较灵活, 而且支持虚拟机的嵌套.

Xen

接着我们来看看Xen这个东西, 这玩意是由剑桥大学开发的. 原本的设计目标是为了在单机上跑128个虚拟机.

Xen是个HyperVisor, 直接运行在硬件上. 我们原来说过, 为了能够操作虚拟机, 包括创建删除等等, 我们一定需要一个管理接口才可以. Xen的特殊之处在于, 他只虚拟化了计算机五大部件的其中两个, 也就是CPU和内存, 对于IO设备, Xen并没有做虚拟化. 我们知道, 操作系统为了能够使用到硬件, 一定是需要驱动程序的. 而这些驱动程序通常由硬件厂商进行开发. 而某些硬件只为了windows进行开发, 例如显卡. Linux的驱动就少之又少了, 所以说, 能针对X环境进行开发的又能有多少呢. 而CPU和内存这种组件由内核直接驱动(作为通信的必要).

所以, Xen所虚拟化出来的第一个虚拟机, 这个虚拟机是Xen的一个工作辅助, 首先能够为用户提供一个管理接口, 为了能够管理, 这个虚拟机就需要拥有特别的权限才行. 这个虚拟机的内核是一个需要能够驱动各种IO设备的内核, 而Xen就是通过这个虚拟机的内核来进行IO设备的通信的. 也就是说, 其他的虚拟机的IO调用, 通过访问第一个虚拟机来进行. 这些虚拟机的IO设备是通过第一个虚拟机的软件来模拟的, 而这个软件通常就是我们刚刚所提到的: QEMU.

Xen的说法是, 每一个虚拟机被称为Domain0, 而第一个虚拟机就被称为Privileged Domain, 也叫做Dom0. 其他被Dom0管理的虚拟机就被称作DomainU, 或者说Unprivileged Domain. 无论启动多少个DomU, 他们都被称为DomU, 只不过他们会拥有一个唯一的ID, 被叫做DomainID.

根据运行环境的不同, Xen可以有多种虚拟化方案.

最首先的就是直接跑一个半虚拟化的环境(PV), 向我们之前说的, 这种技术要求内核需要做适应. 在硬件存在虚拟化辅助的情况下, Xen也可以使用全虚拟化(HVM), 这需要借助Intel的VT或者AMD-V的硬件扩展. 另外, 为了提升性能, Xen还可以将一个全虚拟化的虚拟机使用一些特殊的半虚拟化设备驱动跑起来(PV on HVM), 这样就可以对一些只支持全虚拟化的操作系统进行优化, 在这种情况下, CPU是HVM模式运行, IO设备是PV模式运行.

Xen的硬件虚拟化

现在我们来说说对于Xen, 一些硬件的虚拟化方案.

首先对于CPU和内存就比较好说了. 对于每一个虚拟机上的CPU, 我们可以使用Xen HyperVisor的一个线程或者进程就好了, 假设我们开了10台拥有双核CPU的虚拟机, 映射下来, 其实就相当于是20个HyperVisor的线程.

而内存就是将物理内存上的分片(可能连续可能分散)划分到虚拟机上然后把它们当做是(对于虚拟机而言的)一段连续的内存空间(相当于是物理内存) ,至于如何再去使用那就是虚拟机自己的事情了.

ok, CPU和内存就比较好解决了, 现在我们来说说关于硬盘的虚拟化. 刚刚我们提到了对于IO设备, Xen的解决方法是使用第一个虚拟机来进行通信, 那么假设我们现在通过硬盘映像文件作为虚拟机的物理磁盘的话, 就需要在DOM0的用户空间借助QEMU虚拟出控制器芯片来欺骗虚拟机, 这里虚拟出来的控制器型号自然会是那种比较通用的. 那么接下来我们来进行一次梳理:

  1. 虚拟机发送写请求, 进行系统调用
  2. 内核驱动程序向设备发送信号(这里的设备是DOM0的QEMU虚拟的), 进行转换
  3. 由于控制器的指令不一定一样, DOM0需要再次向内核发送系统调用进行转换
  4. 写物理设备

可以看出来, 这里进行了两次的系统调用, 这样子的话就会显得比较繁琐并且降低性能. 为此, 一种解决方法是使用分段的处理方法, 通过前半段来简化第1, 2两步. 我们直接告诉虚拟机这个设备是虚拟的, 这样就可以省略掉硬件信号的转化和寻道等等过程. 这样就要求我们需要一个配套的前半段和后半段来进行通信.

除了这个问题, 当出现多个虚拟机同时发送大量的IO请求的时候, 所有的真实的物理读写都发生在我们真实的物理硬件上, 这样的压力是很大的. 为此, Xen解决这个问题的方案是, 通过设计一个缓冲环, 一个虚拟机的请求到了之后就在这个环上排着, 当环的空间没有了之后(也就是排满了), 下一次的请求就会被阻塞, 告诉请求者设备繁忙.

硬盘如此, 网卡等类似设备也是差不多的做法.

Xen的几种虚拟化技术

  • Xen的半虚拟化(PV)技术

我们说过, 半虚拟化要求Guest主机的内核必须知晓自己运行在虚拟化环境, 并且不要求CPU有虚拟化支持. Linux的2.6.24+的版本就可以运行在DomU中.

  • HVM技术

依赖于Intel的VT技术或者AMD的AMD-V, 还要依赖QEMU来模拟IO设备, 几乎所有支持此X86平台的OS都可在DomU中运行.

  • PV on HVM技术

就像我们刚刚说的, CPU在HVM的模式下运行, 而IO设备在PV环境中运行. 只要OS能够驱动PV接口类型的IO设备.

Xen的管理工具栈

ToolStacks.png

上面的这张图就是我们进行Xen操作的相关接口, 其中XAPI/XE是Xen的子项目, 而XL是一个基于libxenlight的轻量级命令行管理工具, 这个工具现在是Xen默认自行携带的. 至于Libvirt是由红帽推出的一整套解决虚拟化API的工具, 通过在虚拟机上运行一个libvirtd进程来进行通信, 所以不仅可以管理Xen, 也可以是KVM等等, 通过virsh来进行交互.

在虚拟机上安装并使用Xen

首先说明, 因为我手头没有物理机, 所以只能使用虚拟机来做实验, 值得一提的是, 在这种情况下, 我们在默认的虚拟机配置上只能使用半虚拟化技术, 而使用的Vmware平台可以通过将虚拟CPU的VT-x打开来实现在虚拟机上的HVM模式.

这个配置就是Vmware的这里:

vmware虚拟化CPU配置

另外由于我们运行的这个是个DomU, 还需要运行Dom0, 所以建议将内存稍微分配的大一点.

这里使用的到的内核版本:

1
2
3
4
5
6
[root@VM-master ~]# lsb_release -a
LSB Version: :core-4.1-amd64:core-4.1-noarch
Distributor ID: CentOS
Description: CentOS Linux release 7.4.1708 (Core)
Release: 7.4.1708
Codename: Core

安装Xen环境很简单, 只需要下载使用centos-release-xen这个包, 然后直接安装xen即可.

1
yum install centos-release-xen
1
yum install xen

接着通过安装生成的脚本进行引导的设置

1
/usr/bin/grub-bootxen.sh

如果做到这一步之后你的grub菜单里没有出现xen的菜单入口 , 那么你可以还需要更新一下内核. 另外据说CentOS6存在一个Bug, 那就是grub菜单的initramfs没有写, 需要手动写一下.

这些做完之后直接重启机器就可以看到Xen的启动菜单了, 登陆进去可以来验证一下:

1
2
3
4
5
[root@VM-master ~]# uname -r
4.9.127-32.el7.x86_64
[root@VM-master ~]# xl list
Name ID Mem VCPUs State Time(s)
Domain-0 0 1024 4 r----- 350.4

Dom0已经存在了.

来看一下这个输出信息, 其中Name就是当前虚拟机的名称, ID就是这个虚拟机的唯一标识. 如果这个虚拟机被删除, 下一个创建的仍然会继续累加的, Mem和VCPUs就是这个虚拟机的内存和虚拟CPU的情况. Time就是当前运行的时长.

值得说的, 是这个状态信息, Xen的虚拟机有这些状态:

  • r: running, 正在运行(占据内存)
  • b: blocked, 被阻塞
  • p: pause, 暂停. 注意这个暂停和我们Vmware的Suspend不是一回事. 暂停的虚拟机, 他的内存空间仍然还在占据, 而不像VMware的挂起, 那是将运行环境转移到了硬盘的.
  • s: stop, 停止
  • c: crashed, 崩溃
  • d: dying, 正在关闭的过程中

这个xl有很多的子命令, 当我们列出来的时候有一个警告:

1
WARNING: xl now has better capability to manage domain configuration, avoid using this command when possible

也就是说, 在管理域的时候, 尽量避开使用命令, 而是通过配置来处理.

1
2
3
4
5
6
7
8
9
10
xl full list of subcommands:

create Create a domain from config file <filename>
config-update Update a running domain's saved configuration, used when rebuilding the domain after reboot.
WARNING: xl now has better capability to manage domain configuration, avoid using this command when possible
list List information about all/some domains
destroy Terminate a domain immediately
shutdown Issue a shutdown signal to a domain
reboot Issue a reboot signal to a domain
...(omitted)

其中这里的destroy就相当于是直接拔掉电源.

创建一个新的虚拟机

创建新的虚拟机需要使用到一个配置文件, 这个配置文件的默认语法可以通过man xl.cfg来获取.

常规的一些配置如下:

  1. name 域的名字, 这个每一个都需要是独一无二的(must be unique)
  2. type 指明虚拟机的类型, 过去的指令叫做builder(xm的)
    • generic
    • hvm
    • pvh
  3. vcpus: 虚拟的CPU个数
  4. maxcpus: 最大的虚拟CPU个数
  5. cpus: 虚拟CPU可以运行在其上面的物理CPU列表
  6. memory: 内存大小
  7. maxmem: 可以使用的最大内存空间
  8. on_*: 各种事件, 就像我们的触发器一样, 下面的除了on_reboot的处理是restart, 其他默认的处理都是destroy
    1. on_poweroff
    2. on_reboot
    3. on_watchdog
    4. on_crash
  9. uuid: DomU的唯一标识
  10. disk: 一个DISK_SPEC_STRING数组, 指明磁盘设备的列表
  11. vif: 一个NET_SPEC_STRING数组, 指明网络接口的列表
  12. vfb: 一个VFB_SPEC_STRING的数组, 指明半虚拟化的帧缓冲(framebuffer)设备列表, 这个设备其实就是我们常见的VGA视频输出设备.
  13. pci: 一个PCI_SPEC_STRING数组, 指明IO透传设备

接下来我们来说一下关于如何制定一个磁盘到DomU.

按照刚刚说的, 这是一个数组, 其中DISK_SEPC_STRING可以有多种格式, 其中过一种比较常见的是:

1
["<target>,<format>,<vdev>,<access>", "", "", ...]

其中target是指磁盘映像文件的路径或者是设备文件的路径, format标识磁盘格式, 如果是映像文件可能会有多种格式, 例如, 我们Vmware使用的是vmdk, VirtualBox使用的是vdi, 还有默认dd工具打包出来的raw格式.

可以通过qemu-img来打包, 支持的格式有:

1
Supported formats: vvfat vpc vmdk vhdx vdi ssh sheepdog rbd raw host_cdrom host_floppy host_device file qed qcow2 qcow parallels nbd iscsi gluster dmg tftp ftps ftp https http cloop bochs blkverify blkdebug

vdev是在指定这个设备在DomU中被识别成的硬件类型, 支持hd[x], xvd[x], sd[x] 其中, xvd是xen virtual disk. 最后一个access指定访问的权限, 这个就很简单了: ro, r: 只读; rw, w: 读写 默认的权限设置除了cdrom都是读写.

举个例子, 一种合法的硬盘配置如下:

1
disk=["images/xen/linux.img,raw,xvda,rw"]

接下来我们就来使用busybox和qemu-img来创建一个DomU试试!

先来创建一个磁盘映像文件:

1
2
3
4
5
6
7
8
9
[root@VM-master ~]# qemu-img create -f raw /images/xen/busybox.img 2G
Formatting '/images/xen/busybox.img', fmt=raw size=2147483648
[root@VM-master ~]# ls /images/xen/
busybox.img
[root@VM-master ~]# ls /images/xen/ -lh
total 0
-rw-r--r-- 1 root root 2.0G Dec 9 23:01 busybox.img
[root@VM-master ~]# du -h /images/xen/busybox.img
0 /images/xen/busybox.img

这是一个稀疏类型的磁盘映像文件. 最大的大小是2G, 只不过当前是空的, 我们直接进行格式化.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@VM-master ~]# mke2fs -t ext2 /images/xen/busybox.img 
mke2fs 1.42.9 (28-Dec-2013)
/images/xen/busybox.img is not a block special device.
Proceed anyway? (y,n) y
Discarding device blocks: done
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
131072 inodes, 524288 blocks
26214 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=536870912
16 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912

Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done

[root@VM-master ~]# du -sh /images/xen/busybox.img
33M /images/xen/busybox.img

接下来我们就通过busybox来快速的创建一个根文件系统:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@VM-master busybox]# ls _install/
bin linuxrc sbin usr
[root@VM-master busybox]# mount -o loop /images/xen/busybox.img /mnt/
[root@VM-master busybox]# ls /mnt/
lost+found
[root@VM-master busybox]# cp -a _install/* /mnt/
[root@VM-master busybox]# cd /mnt/
[root@VM-master mnt]# ls
bin linuxrc lost+found sbin usr
[root@VM-master mnt]# mkdir proc sys dev etc var boot home
[root@VM-master mnt]# ll
total 56
drwxr-xr-x 2 root root 4096 Dec 9 23:16 bin
drwxr-xr-x 2 root root 4096 Dec 9 23:19 boot
drwxr-xr-x 2 root root 4096 Dec 9 23:19 dev
drwxr-xr-x 2 root root 4096 Dec 9 23:19 etc
drwxr-xr-x 2 root root 4096 Dec 9 23:19 home
lrwxrwxrwx 1 root root 11 Dec 9 23:16 linuxrc -> bin/busybox
drwx------ 2 root root 16384 Dec 9 23:03 lost+found
drwxr-xr-x 2 root root 4096 Dec 9 23:19 proc
drwxr-xr-x 2 root root 4096 Dec 9 23:16 sbin
drwxr-xr-x 2 root root 4096 Dec 9 23:19 sys
drwxr-xr-x 4 root root 4096 Dec 9 23:16 usr
drwxr-xr-x 2 root root 4096 Dec 9 23:19 var

接下来我们就要进行内核和ramfs的创建, 我们可以直接使用本机的, 做个链接就行了.

最后生成的xl配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[root@VM-master xen]# cat busybox 
# =====================================================================
# Example PV Linux guest configuration
# =====================================================================
#
# This is a fairly minimal example of what is required for a
# Paravirtualised Linux guest. For a more complete guide see xl.cfg(5)

# Guest name
name = "busybox"

# 128-bit UUID for the domain as a hexadecimal number.
# Use "uuidgen" to generate one if required.
# The default behavior is to generate a new UUID each time the guest is started.
#uuid = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"

# Kernel image to boot
kernel = "/boot/vmlinuz"

# Ramdisk (optional)
ramdisk = "/boot/initramfs"

# Kernel command line options
extra = "selinux=0 init=/bin/sh"

# Initial memory allocation (MB)
memory = 256

# Maximum memory (MB)
# If this is greater than `memory' then the slack will start ballooned
# (this assumes guest kernel support for ballooning)
maxmem = 256

# Number of VCPUS
vcpus = 2

# Network devices
# A list of 'vifspec' entries as described in
# docs/misc/xl-network-configuration.markdown
#vif = [ '' ]

# Disk Devices
# A list of `diskspec' entries as described in
# docs/misc/xl-disk-configuration.txt
disk = [ '/images/xen/busybox.img,raw,xvda,rw' ]
root = "/dev/xvda ro"

然后就通过这个配置文件进行创建就行了, 命令像这样:

1
xl -v create /etc/xen/busybox

在后面可以加上-n进行dry-run.


隔了好久更新的分割线, 受不了在CentOS7上搞这个Xen了. 太麻烦了.

问题主要出在那个QEMU的前后端模拟上, 这个Hypervisor的模块服务总是加载失败. 这个xen_scsi_processor总是提示No such device. 这个东西就应该编译进内核! 官方的解决办法简直了, 不搞了.

我去CentOS6了.


终于! 在CentOS6上终于跑起来了, 我们的虚拟机!

1
2
3
4
5
6
[root@localhost ~]# xl list
Name ID Mem VCPUs State Time(s)
Domain-0 0 1024 4 r----- 1359.9
[root@localhost ~]# xl create /etc/xen/busybox
Parsing config from /etc/xen/busybox

进入终端看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@localhost ~]# xl console busybox
[ 0.000000] Linux version 4.9.127-32.el6.x86_64 (mockbuild@c1bj.rdu2.centos.org) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-23) (GCC) ) #1 SMP Mon Sep 17 13:48:11 UTC 2018
[ 0.000000] Command line: root=/dev/xvda ro selinux=0 init=/bin/sh
...(omitted)
[ 0.000000] Booting paravirtualized kernel on Xen
[ 0.000000] Xen version: 4.6.6-12.el6 (preserve-AD)
...(omitted)
[ 2.781378] dracut: Mounted root filesystem /dev/xvda
[ 2.911023] dracut: Switching root
/bin/sh: can't access tty; job control turned off
/ # ls
bin dev home lost+found sbin usr
boot etc linuxrc proc sys var
/ # uname -a
Linux (none) 4.9.127-32.el6.x86_64 #1 SMP Mon Sep 17 13:48:11 UTC 2018 x86_64 GNU/Linux
/ #

从虚拟机的终端切换到我们的主机只需要使用Ctrl+]就可以了.

虚拟机网络

接下来我们为我们的虚拟机加上网络功能.

磁盘一样, 我们的网卡配置格式也和磁盘基本类似, 就是使用一些指定网卡参数配置的数组来创建虚拟网卡设备, 常见的网络属性有这些:

  • mac, 指明MAC地址, Xen的前缀是(00:16:3e).
  • bridge=< bridge >: 指定此网络接口在Dom0上被关联到哪个设备上.
  • model=< MODEL >: 指定模拟的网卡模型, 可以是rt18139, 也可以是e1000啥的.
  • vifname: 接口名称, 仅仅在Dom0中.
  • script: 创建接口的接口.
  • ip: 指定IP地址, 会注入到DomU中
  • rate, 指明设备的传输速率. 通常为”#UNIT/s”
    • GB, MB, KB, B for bytes.
    • Gb, Mb, Kb, b for bits.

现在我们就先通过修改配置文件的方式来创建一个桥.

我们修改配置文件到这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@xen_node ~]# cd /etc/sysconfig/network-scripts/
[root@xen_node network-scripts]# cat ifcfg-xenbr0
DEVICE="xenbr0"
BOOTPROTO="static"
IPADDR="192.168.16.200"
NETMASK="255.255.255.0"
GATEWAY="192.168.16.254"
NM_CONTROLLED="no"
ONBOOT="yes"
TYPE="Bridge"
DNS1=223.5.5.5
[root@xen_node network-scripts]# cat ifcfg-eth0
DEVICE="eth0"
BOOTPROTO="static"
HWADDR="00:0C:29:BD:62:0E"
NM_CONTROLLED="no"
TYPE="Ethernet"
ONBOOT="yes"
BRIDGE=xenbr0

这里有个问题, 一定要注意关闭NetworkManager, 接着重启网络服务, 这么一个网桥就搭建好了.

使用命令建桥的过程也差不多, 我们需要使用brctl命令.

简单的几个步骤就可以创建了,

  • 首先取掉当前网卡的地址
  • 接着新建一个网桥
  • 给新建的网桥绑定设备和网络信息(建议开启生成树)
  • 大功告成!

过程展示就像下面的那样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
[root@xen_node network-scripts]# ifconfig eth0 0
[root@xen_node network-scripts]# ifconfig
eth0 Link encap:Ethernet HWaddr 00:0C:29:BD:62:0E
inet6 addr: fe80::20c:29ff:febd:620e/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:461 errors:0 dropped:0 overruns:0 frame:0
TX packets:378 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:51417 (50.2 KiB) TX bytes:48884 (47.7 KiB)

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:16 errors:0 dropped:0 overruns:0 frame:0
TX packets:16 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:992 (992.0 b) TX bytes:992 (992.0 b)

[root@xen_node network-scripts]# brctl show
bridge name bridge id STP enabled interfaces
[root@xen_node network-scripts]# brctl addbr xenbr0
[root@xen_node network-scripts]# brctl show
bridge name bridge id STP enabled interfaces
xenbr0 8000.000000000000 no
[root@xen_node network-scripts]# brctl addif xenbr0 eth0
[root@xen_node network-scripts]# brctl show
bridge name bridge id STP enabled interfaces
xenbr0 8000.000c29bd620e no eth0
[root@xen_node network-scripts]# brctl stp xenbr0 on
[root@xen_node network-scripts]# ifconfig xenbr0 192.168.16.200/24 up
[root@xen_node network-scripts]# ifconfig
eth0 Link encap:Ethernet HWaddr 00:0C:29:BD:62:0E
inet6 addr: fe80::20c:29ff:febd:620e/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:478 errors:0 dropped:0 overruns:0 frame:0
TX packets:379 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:52803 (51.5 KiB) TX bytes:48944 (47.7 KiB)

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:16 errors:0 dropped:0 overruns:0 frame:0
TX packets:16 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:992 (992.0 b) TX bytes:992 (992.0 b)

xenbr0 Link encap:Ethernet HWaddr 00:0C:29:BD:62:0E
inet addr:192.168.16.200 Bcast:192.168.16.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:febd:620e/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:21 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:3692 (3.6 KiB)

[root@xen_node network-scripts]# ping 192.168.16.254
PING 192.168.16.254 (192.168.16.254) 56(84) bytes of data.
64 bytes from 192.168.16.254: icmp_seq=1 ttl=128 time=1.94 ms
64 bytes from 192.168.16.254: icmp_seq=2 ttl=128 time=0.280 ms
64 bytes from 192.168.16.254: icmp_seq=3 ttl=128 time=0.280 ms
64 bytes from 192.168.16.254: icmp_seq=4 ttl=128 time=0.218 ms
^C
--- 192.168.16.254 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3126ms
rtt min/avg/max/mdev = 0.218/0.679/1.940/0.728 ms

那么在我们的虚拟机中, 可能这个网卡就算我们填写了配置文件也没有作用的, 原因就是因为我们的busybox文件系统中没有响应的网卡驱动, 因此我们需要从本机中把响应的驱动模块丢进去, 然后在虚拟机中哥把这些模块insert才能有用.

当然新版本的有可能是自动装载模块的, 这就没问题了, 打开虚拟机直接就能看到网卡.

另外, 当我们的虚拟机启动了网络之后, 我们的Supervisor会多出来一个新的网络适配器:

1
2
3
4
5
6
7
8
9
10
11
[root@xen_node network-scripts]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master xenbr0 state UP qlen 1000
link/ether 00:0c:29:bd:62:0e brd ff:ff:ff:ff:ff:ff
3: pan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 7e:4d:a1:bf:e5:c3 brd ff:ff:ff:ff:ff:ff
4: xenbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
link/ether 00:0c:29:bd:62:0e brd ff:ff:ff:ff:ff:ff
5: vif1.0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master xenbr0 state UP qlen 32
link/ether fe:ff:ff:ff:ff:ff brd ff:ff:ff:ff:ff:ff

也就是那个vif1.0这是个啥, 我们之前说过, Xen使用的前后端IO设备. 这个就是那个后半段设备. 为啥有一个1.0的后缀, 因为我们当前的虚拟机ID是1:

1
2
3
4
5
[root@xen_node network-scripts]# xl list
Name ID Mem VCPUs State Time(s)
Domain-0 0 1022 4 r----- 297.8
busybox 1 256 1 -b---- 3.5

那么我们的前后端是否可以进行通信呢, 来看一下:

1
2
3
4
5
[root@xen_node network-scripts]# brctl show
bridge name bridge id STP enabled interfaces
pan0 8000.000000000000 no
xenbr0 8000.000c29bd620e no eth0
vif1.0

确实是桥接到我们的xenbr0上的, 接下来我们就来装载一个网络地址来试试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/ # ifconfig -a
eth0 Link encap:Ethernet HWaddr 00:16:3E:63:95:71
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:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

/ # ifconfig eth0 192.168.16.201 up
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 00:16:3E:63:95:71
inet addr:192.168.16.201 Bcast:192.168.16.255 Mask:255.255.255.0
inet6 addr: fe80::216:3eff:fe63:9571/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:125 errors:0 dropped:0 overruns:0 frame:0
TX packets:5 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:12713 (12.4 KiB) TX bytes:418 (418.0 B)

/ # ping 192.168.16.254
PING 192.168.16.254 (192.168.16.254): 56 data bytes
64 bytes from 192.168.16.254: seq=0 ttl=128 time=1.937 ms
64 bytes from 192.168.16.254: seq=1 ttl=128 time=0.470 ms

通了!

让虚拟机拥有自己的内核

我们之前的虚拟机创建, 是通过使用本机(也就是Hypervisor)的vmlinuz和initramfs, 并且使用的根文件目录是通过busybox实现的.

现在我们让虚拟机能够自行引导自己的内核, 由于一个完整的系统容量还是有点大, 这边还是使用busybox来进行根文件系统的模拟, 但是这一次我们要使用pygrub来进行引导, 并且让虚拟机使用自己的内核.

首先我们要先来说个loop设备的管理工具, losetup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@xen_node ~]# losetup --help

Usage:
losetup loop_device give info
losetup -a | --all list all used
losetup -d | --detach <loopdev> [<loopdev> ...] delete
losetup -f | --find find unused
losetup -c | --set-capacity <loopdev> resize
losetup -j | --associated <file> [-o <num>] list all associated with <file>
losetup [ options ] {-f|--find|loopdev} <file> setup

Options:
-e | --encryption <type> enable data encryption with specified <name/num>
-h | --help this help
-o | --offset <num> start at offset <num> into file
--sizelimit <num> loop limited to only <num> bytes of the file
-p | --pass-fd <num> read passphrase from file descriptor <num>
-r | --read-only setup read-only loop device
--show print device name (with -f <file>)
-v | --verbose verbose mode

我们要通过这个来进行qemu创建出的磁盘映像进行分区和文件系统的创建.

首先还是先创建映像:

1
2
[root@xen_node ~]# qemu-img create -f raw /images/xen/busybox2.img 2G
Formatting '/images/xen/busybox2.img', fmt=raw size=2147483648

接着和我们的loop建立关联:

1
2
3
4
[root@xen_node ~]# losetup -a
[root@xen_node ~]# losetup /dev/loop0 /images/xen/busybox2.img
[root@xen_node ~]# losetup -a
/dev/loop0: [0802]:782261 (/images/xen/busybox2.img)

接着我们就可以对loop0进行分区操作了, 分区操作很简单, 就略过了, 分区结束之后写入:

1
2
3
[root@xen_node mapper]# kpartx -av /dev/loop0
add map loop0p1 (253:0): 0 112392 linear /dev/loop0 63
add map loop0p2 (253:1): 0 2120580 linear /dev/loop0 112455

接下来我们对两个分区进行格式化:

1
2
3
[root@xen_node ~]# ls /dev/mapper/
control loop0p1 loop0p2
(格式化略...)

OK, 离胜利很近了, 接下来我们写入必要的文件, 我们就使用CentOS6自带的2.6.32版本的内核:

1
2
3
4
5
6
7
[root@xen_node ~]# cp /boot/vmlinuz-2.6.32-754.9.1.el6.x86_64 /mnt/boot/vmlinuz
[root@xen_node ~]# cp /boot/initramfs-2.6.32-754.9.1.el6.x86_64.img /mnt/boot/initramfs.img
[root@xen_node ~]# grub-install --root-directory=/mnt/ /dev/loop0
Probing devices to guess BIOS drives. This may take a long time.
/dev/loop0 does not have any corresponding BIOS drive.
[root@xen_node ~]# ls /mnt/boot/
grub initramfs.img lost+found vmlinuz

接下来创建我们的grub.conf文件:

1
2
3
4
5
6
7
[root@xen_node ~]# cat /mnt/boot/grub/grub.conf
default=0
timeout=5
title BusyBox(Kernel-2.6.32)
root (hd0,0)
kernel /vmlinuz root=/dev/xvda1 selinux=0 init=/bin/sh console=tty
initrd /initramfs.img

接着还是我们的busybox来构建文件系统, 完成了之后我们就可以开始写虚拟机的配置文件了.

对了, 这个时候别忘记了拆除loop和img映像文件的关联, 另外, 拆除之前, 要先umount

这里我们需要一个叫做pygrub的引导程序, 它会随着我们的xen环境一同被安装

新版本的配置文件可以把kernel, initramfs, extra_cmd, root等等参数都注释掉了, 只需要额外再写一个bootloader指向我们的pygrub就行了.

启动虚拟机, 直接附加console, 我们就能看到这个grub的引导页面了.

pygrub_1

但是, 这里似乎存在问题, 那就是console会报错, 看样子是应该配置一下虚拟显卡等等. 暂时还没有研究, 就先跳过了.

使用libvirt来管理Xen虚拟机

除了使用xl, 我们还可以使用libvirt这个工具栈, 为了使用libvirt. 我们需要在虚拟机中运行libvirtd进程就可以进行远程的管理了.

通过安装libvirt这个包, 并且和xen搭配的daemon也可以安装上.

安装完成后会生成一个叫做virsh的客户端程序, 我们就是通过使用这个来进行操作的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@xen_node ~]# virsh list
Id Name State
----------------------------------------------------
0 Domain-0 running

[root@xen_node ~]# virsh nodeinfo
CPU model: x86_64
CPU(s): 4
CPU frequency: 2400 MHz
CPU socket(s): 1
Core(s) per socket: 2
Thread(s) per core: 1
NUMA cell(s): 1
Memory size: 2096632 KiB

如果你运行这个命令出现了错误:

1
2
3
4
[root@xen_node ~]# virsh list
error: failed to connect to the hypervisor
error: Failed to connect socket to '/var/run/libvirt/libvirt-sock': No such file or directory

那是因为没有启动服务, 启动一下服务就可以了, 这和MySQL类似, 也是一个使用socket来通信的程序.

更多的关于virsh这个工具, 我们之后还会用到, 就先这样子了. ( 而且Xen似乎并不是很好用哈哈