用虚拟化技术已经很久了,虽然自己一直是一个应用层的码农,但是前段时间出差,和一群大拿们一起工作,也接触了一些虚拟化技术,这段时间正好肺炎肆虐,在家没事做,闲得无聊就学习下虚拟化的相关技术,做点笔记用于以后复习。
1. 前言&&概念
1.1 虚拟化管理程序
虚拟化技术浅薄个人的理解就是用 通过一个物理机,虚拟出多台虚拟机。 虚拟化的好处嘛,大家都懂得,这里就不写了。 既然虚拟多台虚拟机,那么首先要解决的问题就是计算资源CPU如何分配和调度?
这里就必须要提到一个概念,就是传统的操作系统是如何实现利用CPU的呢?这里就必须提到一个经典的图片:
CPU的Ring0的特权级最高,Ring3的特权级最低。
那么我们的操作系统这样来利用CPU的特权级的:
操作系统的内核都是运行在Ring0上,用户态的应用程序全部都是运行在CPU的Ring3特权级上的。 从上面的分析看,一个CPU好像只能为一个操作系统服务,因为操作系统是要绑定到CPU的Ring0特权级上去的。说白了就是,常规操作是做不到让一个CPU上运行多个操作系统的。 所以呢,前辈大牛们就引入了一个技术:虚拟化管理程序---Hypervisor(VMM)。
虚拟化管理程序:一种运行在基础物理服务器和操作系统之间的中间软件层,可以允许多个操作系统和应用共享硬件。
也叫做VMM(Virtual Machine Monitor),即虚拟化监视器。
1.2 虚拟化管理程序(Hypervisor)的几种实现方式
1.2.1 半虚拟化
半虚拟化有如下几个特点:
- 对客户操作系统的内核进行修改,将运行在Ring0上的指令转为调用hypervisor
- 一般VM的操作系统必须是开源的(如linux),否则就没办法对内核修改
- 半虚拟化的效率比较低
1.2.2 全虚拟化(硬件辅助虚拟化)
全虚拟化又叫做硬件辅助虚拟化,因为它需要CPU硬件的支持,我们一般上也都用这种虚拟化技术。 它有如下几个特点:
- CPU必须支持Inter VT或者AMD-V技术
- 客户操作系统可以直接使用Ring0特权级的CPU;
PS:目前基本上我们用的CPU都是支持这种技术的,可以使用下面的命令来查看CPU支持情况:
lscpu | grep vmx
lscpu | grep svm
or
cat /proc/cpuinfo | grep vmx
cat /proc/cpuinfo | grep svm
1.3 常见的几种虚拟化管理程序软件实现(Hypervisor)
当前很多厂商
1.3.1 QEMU
QEMU是一种半虚拟化的技术,但是QEMU除了支持计算虚拟化,还提供了存储,网络虚拟化的功能;
1.3.2 KVM
KVM是一种全虚拟化技术,在主流的Linux内核里面都支持。也是我们常用的虚拟化技术。
1.3.3 其它
其他还有很多类似的实现,如Xen等,这里就不作过多的描述了。
1.4 libvirt
因为这几个厂商太多,用起来也很不方便,开源组织又开始折腾了一套函数库,这就是大名鼎鼎的libvirt。 libvirt是一个C语言函数库,为常见的虚拟化技术提供了一套统一的接口,供上层调用。
可以看出我们常用的virsh就是使用这一套接口来管理虚机, OpenStack也是通过这套接口来管理虚机。
1.5 小结
- 通过Hyppervisor技术可以把一个CPU虚拟化为多个vCPU,从而使用vCPU来运行多个操作系统;
2. KVM
2.1 KVM介绍
KVM其实说白了就是一个Linux的内核模块。 KVM在内核中的地位:
CPU支持虚拟化技术+ KVM = 硬件辅助虚拟化。
2.2 KVM和QEMU的关系
KVM解决的是CPU资源的虚拟化,高大上的说法是计算资源虚拟化。但是像网络,硬盘,内存的虚拟化问题没有解决。 而前文提到QEMU也是一种虚拟化管理程序,它的计算资源是半虚拟化的,但是它还结局了网络,硬盘,内存的虚拟化问题。 所以总结来说就是:
CPU虚拟化:使用KVM 实现全虚拟化 内存/硬盘/网络虚拟化: 使用QEMU实现
2.3 KVM的安装和使用
2.3.1 安装前准备
前面讲了一大堆理论,用大白话总结下就是KVM类似于Windows下的VMWare或者VirtualBox软件,可以用它来创建虚机。 那么下面我们就利用这个KVM来创建虚机。当然了我们先要检查下当前的操作系统版本和硬件是否支持虚拟化技术。
首先:先检查当前CPU是否支持虚拟化技术:
lscpu | grep vmx
lscpu | grep svm
再检查内核是否支持kvm:
lsmod | grep kvm
接下来就要安装kvm相关的包:
yum install -y qemu-kvm libvirt
yum install -y virt-install
好了以后,就需要启动libvirt服务,libvirt服务就可以帮我们来创建虚拟机:
systemctl enable libvirtd
systemctl start libvirtd
可以看出来此时我们的这台Linux主机上就会多出来一个网卡virbr0:
[root@localhost yum.repos.d]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.100 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 2409:8a20:e1d:1960:20c:29ff:fe78:c9fb prefixlen 64 scopeid 0x0<global>
inet6 fe80::20c:29ff:fe78:c9fb prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:78:c9:fb txqueuelen 1000 (Ethernet)
RX packets 57276 bytes 83312571 (79.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 40939 bytes 3096415 (2.9 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 72 bytes 6256 (6.1 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 72 bytes 6256 (6.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
virbr0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255
ether 52:54:00:91:99:4a txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
2.3.2 使用KVM创建一个虚机
通过前文的描述我们知道如果要有虚机,首先要有硬盘,硬盘虚拟化是使用qemu来实现的。
2.3.3 通过qeum创建虚机硬盘
使用下面的命令:
2.3.4 通过libvirt接口创建kvm虚机
[root@localhost ~]# virt-install --virt-type kvm --name CentOS-7-x86_64 --ram 1024 --cdrom=/tmp/CentOS-7-x86_64-Minimal-1908.iso --disk path=/opt/CentOS-7-x86_64.raw --network network=default --graphics vnc,listen=0.0.0.0 --noautoconsole
Starting install...
Domain installation still in progress. You can reconnect to
the console to complete the installation process.
分析下上面这个命令,我们通过libvirt的virt-install 命令创建一个虚机。
- 虚机的虚拟化管理程序选用 : kvm
- 虚机名称: CentOS-7-x86_64
- 内存大小: 1024
- 光驱镜像: 使用/tmp/CentOS-7-x86_64-Minimal-1908.iso
- 硬盘: /opt/CentOS-7-x86_64.raw(刚才qemu创建的)
- 网络: 默认网络
- 图形界面: vnc方式
创建成功后再通过这个命令来查看:
[root@localhost ~]# virsh list --all
Id Name State
----------------------------------------------------
2 CentOS-7-x86_64 running
可以看到我创建的这个虚机现在已经是running状态了。此时通过VNC工具,使用宿主机的IP地址就可以控制这个虚机,从而来进行虚机安装。 实际操作的时候此处有几点注意:
- 当前的宿主机是需要关闭防火墙的,使用命令
systemctl stop firewalld
,如果不关闭,vnc工具无法联上去。 - vnc工具连接使用的IP地址是宿主机的IP地址,端口号默认是5900,如果创建第二个虚机,端口号就是5901,以此类推。
接下来一路下一步就可以完成虚机的操作系统的安装。
2.4 通过KVM管理虚机
2.4.1 VM在宿主机上到底是什么东西?
安装好VM后,在宿主机上观察,先通过下面的命令看
[root@localhost ~]# ps aux | grep kvm
root 649 0.0 0.0 0 0 ? S< 02:29 0:00 [kvm-irqfd-clean]
qemu 3474 91.7 8.5 1706028 84748 ? Rl 05:34 0:06 /usr/libexec/qemu-kvm -name CentOS-7-x86_64 -S -machine pc-i440fx-rhel7.0.0,accel=kvm,usb=off,dump-guest-core=off -cpu Broadwell-IBRS,+ssbd,-hle,-rtm,+spec-ctrl -m 1024 -realtime mlock=off -smp 1,sockets=1,cores=1,threads=1 -uuid 5ad4ff1c-5519-4366-a75e-08f8bc4cc640 -no-user-config -nodefaults -chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/domain-6-CentOS-7-x86_64/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 -global PIIX4_PM.disable_s3=1 -global PIIX4_PM.disable_s4=1 -boot strict=on -device ich9-usb-ehci1,id=usb,bus=pci.0,addr=0x4.0x7 -device ich9-usb-uhci1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4 -device ich9-usb-uhci2,masterbus=usb.0,firstport=2,bus=pci.0,addr=0x4.0x1 -device ich9-usb-uhci3,masterbus=usb.0,firstport=4,bus=pci.0,addr=0x4.0x2 -device virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x5 -drive file=/opt/CentOS-7-x86_64.raw,format=raw,if=none,id=drive-virtio-disk0 -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1 -drive if=none,id=drive-ide0-0-0,readonly=on -device ide-cd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 -netdev tap,fd=26,id=hostnet0,vhost=on,vhostfd=28 -device virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:96:4b:42,bus=pci.0,addr=0x3 -chardev pty,id=charserial0 -device isa-serial,chardev=charserial0,id=serial0 -chardev socket,id=charchannel0,path=/var/lib/libvirt/qemu/channel/target/domain-6-CentOS-7-x86_64/org.qemu.guest_agent.0,server,nowait -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=org.qemu.guest_agent.0 -device usb-tablet,id=input0,bus=usb.0,port=1 -vnc 0.0.0.0:0 -vga cirrus -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x7 -object rng-random,id=objrng0,filename=/dev/urandom -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pci.0,addr=0x8 -msg timestamp=on
root 3502 0.0 0.0 0 0 ? S 05:34 0:00 [kvm-pit/3474]
root 3512 0.0 0.0 112712 960 pts/1 R+ 05:34 0:00 grep --color=auto kvm
可以看的很清楚,KVM创建的虚拟机在宿主机上其实就是一个qemu用户创建的进程! 假若我此时在宿主机上kill掉这个进程,那么虚拟机应该就会shutdown掉。例如下实验:
[root@localhost ~]# kill 3474
[root@localhost ~]# virsh list --all
Id Name State
----------------------------------------------------
- CentOS-7-x86_64 shut off
果然如此! 好了,再把它起来:
[root@localhost ~]# virsh start CentOS-7-x86_64
Domain CentOS-7-x86_64 started
[root@localhost ~]# virsh list --all
Id Name State
----------------------------------------------------
7 CentOS-7-x86_64 running
2.4.2 libvirt到底是什么东西?
通过上面的实验,可以看出virsh提供了一套sh接口,可以很方便的来管理操作虚机。其实这就是libvirt的一个组件。 如下图:
libvirt组件主要由如下几个东西组成:
- virsh 管理工具
- 一个后台的Daemon进程;
- 一个libvirt的C语言函数库;
那么问题又来了,我如果把libvirt的进程给杀了,虚机会挂吗?答案是否定的。
[root@localhost ~]# ps aux | grep libvirt
root 2182 0.0 0.5 1600888 5136 ? Ssl 02:35 0:00 /usr/sbin/libvirtd
nobody 2272 0.0 0.0 53896 120 ? S 02:35 0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
root 2273 0.0 0.0 53868 4 ? S 02:35 0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
qemu 3669 2.0 32.9 1757240 327936 ? Sl 05:42 0:17 /usr/libexec/qemu-kvm -name CentOS-7-x86_64 -S -machine pc-i440fx-rhel7.0.0,accel=kvm,usb=off,dump-guest-core=off -cpu Broadwell-IBRS,+ssbd,-hle,-rtm,+spec-ctrl -m 1024 -realtime mlock=off -smp 1,sockets=1,cores=1,threads=1 -uuid 5ad4ff1c-5519-4366-a75e-08f8bc4cc640 -no-user-config -nodefaults -chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/domain-7-CentOS-7-x86_64/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 -global PIIX4_PM.disable_s3=1 -global PIIX4_PM.disable_s4=1 -boot strict=on -device ich9-usb-ehci1,id=usb,bus=pci.0,addr=0x4.0x7 -device ich9-usb-uhci1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4 -device ich9-usb-uhci2,masterbus=usb.0,firstport=2,bus=pci.0,addr=0x4.0x1 -device ich9-usb-uhci3,masterbus=usb.0,firstport=4,bus=pci.0,addr=0x4.0x2 -device virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x5 -drive file=/opt/CentOS-7-x86_64.raw,format=raw,if=none,id=drive-virtio-disk0 -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1 -drive if=none,id=drive-ide0-0-0,readonly=on -device ide-cd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 -netdev tap,fd=26,id=hostnet0,vhost=on,vhostfd=28 -device virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:96:4b:42,bus=pci.0,addr=0x3 -chardev pty,id=charserial0 -device isa-serial,chardev=charserial0,id=serial0 -chardev socket,id=charchannel0,path=/var/lib/libvirt/qemu/channel/target/domain-7-CentOS-7-x86_64/org.qemu.guest_agent.0,server,nowait -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=org.qemu.guest_agent.0 -device usb-tablet,id=input0,bus=usb.0,port=1 -vnc 0.0.0.0:0 -vga cirrus -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x7 -object rng-random,id=objrng0,filename=/dev/urandom -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pci.0,addr=0x8 -msg timestamp=on
root 3765 0.0 0.0 112716 960 pts/1 S+ 05:57 0:00 grep --color=auto libvirt
[root@localhost ~]# kill 2182
[root@localhost ~]# virsh list --all
error: failed to connect to the hypervisor
error: Failed to connect socket to '/var/run/libvirt/libvirt-sock': No such file or directory
可以看出来,现在virsh工具已经不能用了。 但是VNC连接这个虚机依然是好的。
2.4.3 libvirt的两个概念
- 节点(Node) :物理机;
- 域(Domain): 虚拟机,也叫实例(Instance),VM;
2.4.4 virsh常用的命令
查看虚机:
virsh list --all
启动虚机:
virsh start domain
关闭虚机:
virsh destory domain
删除虚机:
virsh undefine domain
查看虚拟机定义:
在宿主机的/etc/libvirt/qemu
目录
<!--
WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
OVERWRITTEN AND LOST. Changes to this xml configuration should be made using:
virsh edit CentOS-7-x86_64
or other application using the libvirt API.
-->
<domain type='kvm'>
<name>CentOS-7-x86_64</name>
<uuid>5ad4ff1c-5519-4366-a75e-08f8bc4cc640</uuid>
<memory unit='KiB'>1048576</memory>
<currentMemory unit='KiB'>1048576</currentMemory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='x86_64' machine='pc-i440fx-rhel7.0.0'>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
</features>
<cpu mode='custom' match='exact' check='partial'>
<model fallback='allow'>Broadwell-noTSX-IBRS</model>
<feature policy='require' name='spec-ctrl'/>
<feature policy='require' name='ssbd'/>
</cpu>
<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>destroy</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='raw'/>
<source file='/opt/CentOS-7-x86_64.raw'/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
</disk>
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<target dev='hda' bus='ide'/>
<readonly/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
<controller type='usb' index='0' model='ich9-ehci1'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x7'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci1'>
<master startport='0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0' multifunction='on'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci2'>
<master startport='2'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci3'>
<master startport='4'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x2'/>
</controller>
<controller type='pci' index='0' model='pci-root'/>
<controller type='ide' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
</controller>
<controller type='virtio-serial' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
</controller>
<interface type='network'>
<mac address='52:54:00:96:4b:42'/>
<source network='default'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
<serial type='pty'>
<target type='isa-serial' port='0'>
<model name='isa-serial'/>
</target>
</serial>
<console type='pty'>
<target type='serial' port='0'/>
</console>
<channel type='unix'>
<target type='virtio' name='org.qemu.guest_agent.0'/>
<address type='virtio-serial' controller='0' bus='0' port='1'/>
</channel>
<input type='tablet' bus='usb'>
<address type='usb' bus='0' port='1'/>
</input>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'>
<listen type='address' address='0.0.0.0'/>
</graphics>
<video>
<model type='cirrus' vram='16384' heads='1' primary='yes'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
</video>
<memballoon model='virtio'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</memballoon>
<rng model='virtio'>
<backend model='random'>/dev/urandom</backend>
<address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
</rng>
</devices>
</domain>
修改虚机配置:
virsh edit domain
这样我们就可以用这个命令来对虚机规格进行修改了。