虚拟机内核说:我要在CPU上跑一个指令!
虚拟化软件说:没问题,你是内核嘛,可以跑!
虚拟化软件转过头去找物理机内核说:报告,我管理的虚拟机里面的一个要执行一个CPU指令,帮忙来一小段时间空闲的CPU时间,让我代它跑个指令。
物理机内核说:你等着,另一个跑着呢。(过了一会儿)它跑完了,该你了。
虚拟化软件说:我代它跑,终于跑完了,出来结果了。
虚拟化软件转头给虚拟机内核说:哥们儿,跑完了,结果是这个。我说你是内核吧,绝对有权限,没问题,下次跑指令找我啊!
虚拟机内核说:看来我真的是内核呢,可是,哥,好像这点儿指令跑得有点慢啊!
虚拟化软件说:这就不错啦,好几个排着队跑呢!
虚拟机内核说:我启动需要4G内存,我好分给我上面的应用。
虚拟化软件说:没问题,才4G,你是内核嘛,我马上申请好。
虚拟化软件转头给物理机内核说:报告,我启动了一个虚拟机,需要4G内存,给我4个房间呗。
物理机内核:怎么又一个虚拟机啊!好吧,给你90、91、92、93四个房间。
虚拟化软件转头给虚拟机内核说:哥们,内存有了,0、1、2、3这个四个房间都是你的。你看,你是内核嘛,独占资源,从0编号的就是你的。
虚拟机内核说:看来我真的是内核啊,能从头开始用。那好,我就在房间2的第三个柜子里面放个东西吧!
虚拟化软件说:要放东西啊,没问题。但是,它心里想:我查查看,这个虚拟机是90号房间开头的,它要在房间2放东西,那就相当于在房间92放东西。
虚拟化软件转头给物理机内核说:报告,我上面的虚拟机要在92号房间的第三个柜子里面放个东西。
按照上面的介绍,完全虚拟化是非常慢的,所以要使用硬件辅助虚拟化技术Intel-VT,AMD-V,所以需要CPU硬件开启这个标志位,一般在BIOS里面设置。
当确认开始了标志位之后,通过KVM,GuestOS的CPU指令不用经过Qemu转译,直接运行,大大提高了速度。
所以,KVM在内核里面需要有一个模块,来设置当前CPU是Guest OS在用,还是Host OS在用。
下面,我们来查看内核模块中是否含有kvm, lsmod | grep kvm。
KVM内核模块通过/dev/kvm暴露接口,用户态程序可以通过ioctl来访问这个接口。例如,你可以通过下面的流程编写程序。
Qemu将KVM整合进来,将有关CPU指令的部分交由内核模块来做,就是qemu-kvm (qemu-system-XXX)。
qemu和kvm整合之后,CPU的性能问题解决了。另外Qemu还会模拟其他的硬件,如网络和硬盘。同样,全虚拟化的方式也会影响这些设备的性能。
于是,qemu采取半虚拟化的方式,让Guest OS加载特殊的驱动来做这件事情。
例如,网络需要加载virtio_net,存储需要加载virtio_blk,Guest需要安装这些半虚拟化驱动,GuestOS知道自己是虚拟机,所以数据会直接发送给半虚拟化设备,经过特殊处理(例如排队、缓存、批量处理等性能优化方式),最终发送给真正的硬件。这在一定程度上提高了性能。
至此,整个关系如下图所示。
## 创建虚拟机
了解了qemu-kvm的工作原理之后,下面我们来看一下,如何使用qemu-kvm创建一个能够上网的虚拟机。
如果使用VirtualBox创建过虚拟机,通过界面点点就能创建一个能够上网的虚拟机。如果使用qemu-kvm,就没有这么简单了。一切都得自己来做,不过这个过程可以了解KVM虚拟机的创建原理。
首先,我们要给虚拟机起一个名字,在KVM里面就是-name ubuntutest。
设置一个内存大小,在KVM里面就是-m 1024。
创建一个虚拟硬盘,对于VirtualBox是VDI格式,对于KVM则不同。
硬盘有两种格式,一个是动态分配,也即开始创建的时候,看起来很大,其实占用的空间很少,真实有多少数据,才真的占用多少空间。一个是固定大小,一开始就占用指定的大小。
比如,我这台电脑,硬盘的大小为8G。
在KVM中,创建一个虚拟机镜像,大小为8G,其中qcow2格式为动态分配,raw格式为固定大小。
```
qemu-img create -f qcow2 ubuntutest.img 8G
```
我们将Ubuntu的ISO挂载为光盘,在KVM里面-cdrom [ubuntu-xxx-server-amd64.iso](http://ubuntu-xxx-server-amd64.iso)。
创建一个网络,有时候会选择桥接网络,有时候会选择NAT网络,这个在KVM里面只有自己配置了。
接下来Virtualbox就会有一个界面,可以看到安装的整个过程,在KVM里面,我们用VNC来做。参数为-vnc :19
于是,我们也可以创建KVM虚拟机了,可以用下面的命令:
```
qemu-system-x86_64 -enable-kvm -name ubuntutest -m 2048 -hda ubuntutest.img -cdrom ubuntu-14.04-server-amd64.iso -boot d -vnc :19
```
启动了虚拟机后,连接VNC,我们也能看到安装的过程。
按照普通安装Ubuntu的流程安装好Ubuntu,然后shutdown -h now,关闭虚拟机。
接下来,我们可以对KVM创建桥接网络了。这个要模拟virtualbox的桥接网络模式。
如果在桌面虚拟化软件上选择桥接网络,在你的笔记本电脑上,就会形成下面的结构。
每个虚拟机都会有虚拟网卡,在你的笔记本电脑上,会发现多了几个网卡,其实是虚拟交换机。这个虚拟交换机将虚拟机连接在一起。在桥接模式下,物理网卡也连接到这个虚拟交换机上。物理网卡在桌面虚拟化软件的“界面名称”那里选定。
如果使用桥接网络,当你登录虚拟机里看IP地址时会发现,你的虚拟机的地址和你的笔记本电脑的地址,以及你旁边的同事的电脑的网段是一个网段。这是为什么呢?这其实相当于将物理机和虚拟机放在同一个网桥上,相当于这个网桥上有三台机器,是一个网段的,全部打平了。
在数据中心里面,采取的也是类似的技术,连接方式如下图所示,只不过是Linux在每台机器上都创建网桥br0,虚拟机的网卡都连到br0上,物理网卡也连到br0上,所有的br0都通过物理网卡连接到物理交换机上。
同样我们换一个角度看待这个拓扑图。同样是将网络打平,虚拟机会和物理网络具有相同的网段,就相当于两个虚拟交换机、一个物理交换机,一共三个交换机连在一起。两组四个虚拟机和两台物理机都是在一个二层网络里面的。
qemu-kvm如何才能创建一个这样的桥接网络呢?
1.在Host机器上创建bridge br0。
```
brctl addbr br0
```
2.将br0设为up。
```
ip link set br0 up
```
3.创建tap device。
```
tunctl -b
```
4.将tap0设为up。
```
ip link set tap0 up
```
5.将tap0加入到br0上。
```
brctl addif br0 tap0
```
6.启动虚拟机, 虚拟机连接tap0、tap0连接br0。
```
qemu-system-x86_64 -enable-kvm -name ubuntutest -m 2048 -hda ubuntutest.qcow2 -vnc :19 -net nic,model=virtio -nettap,ifname=tap0,script=no,downscript=no
```
7.虚拟机启动后,网卡没有配置,所以无法连接外网,先给br0设置一个ip。
```
ifconfig br0 192.168.57.1/24
```
8.VNC连上虚拟机,给网卡设置地址,重启虚拟机,可ping通br0。
9.要想访问外网,在Host上设置NAT,并且enable ip forwarding,可以ping通外网网关。
```
# sysctl -p
net.ipv4.ip_forward = 1
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
```
10.如果DNS没配错,可以进行apt-get update。
在这里,请记住qemu-system-x86_64的启动命令,这里面有CPU虚拟化KVM,有内存虚拟化、硬盘虚拟化、网络虚拟化。接下来的章节,我们会看内核是如何进行虚拟化的。
## 总结时刻
今天我们讲了虚拟化的基本原理,并且手动创建一个可以上网的虚拟机。请记住下面这一点,非常重要,理解虚拟机启动的参数就是理解虚拟化技术的入口。学会创建虚拟机,在后面做内核相关实验的时候就会非常方便。
具体到知识点上,这一节你需要需要记住下面的这些知识点:
- 虚拟化的本质是用qemu的软件模拟硬件,但是模拟方式比较慢,需要加速;
- 虚拟化主要模拟CPU、内存、网络、存储,分别有不同的加速办法;
- CPU和内存主要使用硬件辅助虚拟化进行加速,需要配备特殊的硬件才能工作;
- 网络和存储主要使用特殊的半虚拟化驱动加速,需要加载特殊的驱动程序。
## 课堂练习
请你务必自己使用qemu,按照上面我写的步骤创建一台虚拟机。
欢迎留言和我分享你的疑惑和见解,也欢迎可以收藏本节内容,反复研读。你也可以把今天的内容分享给你的朋友,和他一起学习和进步。