在 Linux 系统之中,KVM 可以说是极为重要的一项虚拟化技术。如今我们所接触到的云服务器、私有云平台、虚拟化集群乃至大量企业级数据中心,背后都能够看到 KVM 的身影。
所谓虚拟化,简单来说,就是在一台真实的物理服务器之上,通过软件以及 CPU 硬件虚拟化能力,模拟出来多台彼此相互隔离的“虚拟计算机”。每一台虚拟机都能够拥有自身的 CPU、内存、磁盘以及网卡,并且还能够独立安装 Linux、Windows 等操作系统。
而 KVM 所做的事情,就是让 Linux 内核本身具备运行虚拟机的能力。
如果把一台物理服务器比作一栋办公大楼,那么 KVM 就好似负责划分空间以及管理基础设施的物业系统一般。它能够把真实 CPU、内存等资源进行组织以及调度,使得多个虚拟机能够在同一套物理硬件之上同时运行,并且彼此之间尽量保持隔离。
KVM 本质上是 Linux 内核提供的一套硬件虚拟化基础设施。
它通过 kvm.ko、kvm-intel.ko 或者 kvm-amd.ko 等内核模块,把 CPU 提供的 Intel VT-x、AMD-V 等硬件虚拟化能力暴露给用户空间,同时通过 /dev/kvm 给 QEMU 这一类 VMM 提供创建 VM、创建 vCPU、映射 Guest Memory、运行 vCPU 等能力。
一、为什么我们还在聊 KVM
1.1 虚拟化技术的演进与 KVM 的江湖地位
在传统的服务器运行模式之中,一台物理服务器一般直接运行一个操作系统。
例如:
物理服务器 │ └── Linux ├── Nginx ├── MySQL └── Redis
这种方式看起来比较简单,但是存在一个十分明显的问题。
假设这台服务器拥有:
64 核 CPU256 GB 内存4 TB SSD
可是某一个业务系统实际只使用:
8 核 CPU32 GB 内存
那么剩余的大量计算资源就处于闲置状态。
虚拟化技术的出现,就是为了让一套物理硬件同时承载多个彼此独立的操作系统。
物理服务器 │ ┌──────┴──────┐ │ Hypervisor │ └──────┬──────┘ │ ┌─────────────┼─────────────┐ │ │ │ VM1 VM2 VM3 Linux Windows Linux
这样一来,同一台物理服务器就能够被划分成为多个虚拟机,从而提高硬件资源的利用效率。
早期服务器虚拟化领域存在 VMware、Xen 等多种技术路线。
而 KVM 最大的特点在于:
它并不是在 Linux 外部再构建一套完全独立的 Hypervisor,而是直接把虚拟化能力集成进入 Linux 内核之中。
KVM 在 Linux 2.6.20 时代进入主线内核之后,Linux 本身就能够通过 KVM 模块获得硬件辅助虚拟化能力。
也就是说:
Linux ↓加载 KVM ↓Linux 内核具备 Hypervisor 能力
这也是 KVM 能够在 Linux 服务器以及云计算环境之中得到广泛应用的重要原因。
1.2 从 Xen、VMware 到 KVM:Linux 原生虚拟化的逆袭
VMware 很早就把虚拟化商业化,并且建立了成熟的企业级虚拟化生态。
Xen 则采用另外一种思路,其拥有自己的 Hypervisor,并且通过 Domain 0 等机制完成虚拟机管理。
KVM 的路线有所不同。
KVM 的设计思想可以简单理解成为:
既然 Linux 内核本身已经拥有成熟的 CPU 调度、内存管理、网络、文件系统以及设备驱动框架,那么为什么还要再单独实现一套类似的基础设施?
所以 KVM 选择直接依赖 Linux 内核现有能力。
例如:
虚拟机 vCPU ↓最终表现为 Linux 线程 ↓Linux Scheduler 调度 ↓运行在物理 CPU 上
虚拟机所使用的内存,本质之上也依赖 Linux 内存管理机制。
虚拟磁盘则可以依赖 Linux 文件系统、LVM、Ceph 等后端。
虚拟网络也能够直接运用 Linux Bridge、TAP、vhost-net 等 Linux 网络能力。
这使得 KVM 和 Linux 系统天然结合在了一块儿。
1.3 本文适用人群与阅读指南
本文主要适合下面几类开发者以及运维人员:
- • 需要准备 KVM、Linux 虚拟化相关面试的人;
我们将按照:
虚拟化基础 ↓KVM 内核原理 ↓QEMU + KVM ↓环境搭建 ↓虚拟机管理 ↓存储 ↓网络 ↓性能优化 ↓高级特性 ↓故障排查
这样的顺序一步一步进行解析。
二、KVM 核心原理:从内核模块到完整虚拟化
2.1 什么是 KVM:内核虚拟机的本质
KVM 的全称为:
Kernel-based Virtual Machine
也就是:
基于内核的虚拟机。
严格来说,KVM 自身并不是一个完整的虚拟机模拟器。
KVM 更核心的作用,是把 Linux 内核变成为一个能够运用 CPU 硬件虚拟化能力的 Hypervisor。
在 x86 平台之上,常见的模块包括:
kvm.kokvm_intel.ko或者kvm_amd.ko
其中:
kvm.ko
属于 KVM 通用核心模块。
而:
kvm_intel.ko
用于 Intel VT-x。
kvm_amd.ko
则用于 AMD-V。
我们能够通过下面的命令来查看:
lsmod | grep kvm
例如可能看到:
kvm_intelkvm
这就说明对应的 KVM 模块已经加载到了 Linux 内核之中。
2.2 CPU 硬件虚拟化:Intel VT-x 与 AMD-V 的底层支撑
KVM 想要高效运行虚拟机,一个非常重要的条件就是 CPU 提供硬件虚拟化能力。
Intel 对应的是:
Intel VT-x
AMD 对应的是:
AMD-V
在早期的软件虚拟化技术之中,如果 Guest OS 执行了一些敏感指令,Hypervisor 往往需要采用二进制翻译或者其他软件机制来进行处理。
这种方式复杂程度比较高,而且会产生比较明显的性能损耗。
CPU 硬件虚拟化出现之后,处理器增加了专门用于虚拟机运行的硬件机制。
以 Intel VT-x 为例,可以粗略理解成为 CPU 提供了:
VMX Root ModeVMX Non-root Mode
Host/Hypervisor 可以运行于 Root 相关环境,而 Guest 中的大部分代码则运行于 Non-root 环境。
当 Guest 执行某些需要 Hypervisor 介入的操作的时候,CPU 会产生:
VM Exit
把控制权交还给虚拟机监控程序。
处理完毕之后再通过:
VM Entry
重新进入 Guest。
整个过程可以简化成为:
Guest OS │ │ 普通指令 ↓直接在 CPU 上运行 │ │ 敏感操作 ↓ VM Exit │ ↓KVM │处理完成 ↓ VM Entry │ ↓Guest OS
这也正是硬件辅助虚拟化能够获得比较接近物理机性能的重要原因之一。
2.3 KVM 在内核中的角色:/dev/kvm 设备与 ioctl
当 KVM 模块成功加载之后,Linux 系统通常会出现:
/dev/kvm
这个设备节点乃是用户空间虚拟机程序进入 KVM 内核模块的重要入口。
这里有一个特别容易被误解的地方。
应用程序并不是通过普通的:
read()write()
去控制 /dev/kvm。
KVM 大量控制操作主要依靠:
ioctl()
来实现。
例如一个极度简化的 KVM 用户程序,其操作流程大体上是:
int kvm;int vm;int vcpu;kvm = open("/dev/kvm", O_RDWR);vm = ioctl(kvm, KVM_CREATE_VM, 0);vcpu = ioctl(vm, KVM_CREATE_VCPU, 0);
这三个步骤分别能够理解成为:
第一步:
open("/dev/kvm")
进入 KVM 内核接口。
第二步:
KVM_CREATE_VM
创建一个虚拟机对象。
第三步:
KVM_CREATE_VCPU
为这个虚拟机创建一个虚拟 CPU。
随后用户空间程序还需要去:
真正运行虚拟 CPU 的核心 ioctl 之一便是:
KVM_RUN
因此 KVM 的一个基本调用关系可以表示成为:
QEMU │ │ open() ↓/dev/kvm │ │ ioctl() ↓KVM 内核模块 │ ↓VT-x / AMD-V │ ↓Physical CPU
2.4 QEMU 与 KVM 的分工:用户态模拟与内核加速
初学 KVM 的时候,最容易搞混的问题就是:
KVM 和 QEMU 到底是什么关系?
可以先记住一句话:
KVM 负责让 CPU 高效运行虚拟机,而 QEMU 负责构造出一台完整的虚拟计算机。
KVM 主要解决的是:
CPU 虚拟化内存虚拟化中断虚拟化等核心能力
可是虚拟机不能只有 CPU。
一台真正的计算机还需要:
磁盘网卡USBPCI显卡BIOS/UEFI串口键盘鼠标
这些虚拟硬件设备很多都是由 QEMU 来提供的。
因此完整的结构可以理解成为:
Guest OS │ ┌─────────────┴─────────────┐ │ │ Guest CPU Guest Device │ │ ↓ ↓ KVM QEMU │ │ ↓ ↓ Hardware CPU Host Device / File
如果没有 KVM,QEMU 依旧可以通过软件方式模拟 CPU。
但是速度一般会比较慢。
如果 QEMU 结合 KVM:
qemu-system-x86_64 -enable-kvm ...
那么 Guest 中大量 CPU 指令可以直接借助硬件虚拟化机制运行。
因此:
QEMU + KVM
才是我们平时所说的 Linux KVM 虚拟机体系当中非常经典的组合。
2.5 全虚拟化 vs 半虚拟化:KVM 的技术路线选择
全虚拟化的特点,就是 Guest OS 基本不需要知道自己运行在虚拟机之中。
例如 Guest 看见一个虚拟网卡:
Intel E1000
它会觉得自己连接的就是一块真实的 E1000 网卡。
QEMU 则在用户空间模拟这块设备。
这种兼容性非常不错,但是性能并不是最优。
于是出现了另外一种思路:
virtio
virtio 是 Linux 虚拟化当中特别重要的半虚拟化 I/O 技术。
例如:
virtio-netvirtio-blkvirtio-scsivirtio-balloon
Guest OS 知道这些设备乃是虚拟设备,因此不用再完整模拟一块真实硬件。
例如传统模式可能是:
Guest ↓模拟 E1000 ↓QEMU ↓Host
而 virtio 则更接近:
Guest virtio driver ↓virtqueue ↓Host backend
这样能够显著降低大量硬件模拟所产生的额外开销。
所以 KVM 实际上常常是:
CPU 使用硬件辅助全虚拟化,I/O 则大量使用 virtio 半虚拟化。
这是理解现代 KVM 性能优化的关键所在。
三、从零搭建 KVM 虚拟化环境
3.1 硬件与系统前置检查
首先要确认 CPU 是否具备硬件虚拟化能力。
Intel CPU 可以检查:
grep -E 'vmx' /proc/cpuinfo
AMD CPU 则检查:
grep -E 'svm' /proc/cpuinfo
也可以统一执行:
egrep -c '(vmx|svm)' /proc/cpuinfo
要是结果大于:
0
通常说明 CPU 提供了对应的虚拟化指令集。
接下来检查:
ls -l /dev/kvm
如果存在:
/dev/kvm
说明 KVM 设备节点已经创建。
随后可以查看:
lsmod | grep kvm
进一步确认 KVM 内核模块。
需要注意的是,即便 CPU 本身支持 VT-x 或者 AMD-V,也可能因为 BIOS/UEFI 当中关闭了:
Intel Virtualization Technology或者SVM Mode
导致操作系统无法使用硬件虚拟化能力。
3.2 安装 KVM 核心组件
以 Ubuntu/Debian 系统为例,可以安装:
sudo apt updatesudo apt install \ qemu-kvm \ libvirt-daemon-system \ libvirt-clients \ bridge-utils \ virt-manager
这些组件所承担的职责并不完全相同。
qemu-kvm
提供 QEMU/KVM 相关虚拟机能力。
libvirt
用于对虚拟机进行统一管理。
它把不同 Hypervisor 的管理接口进行了进一步封装。
virsh
乃是 libvirt 提供的命令行管理工具。
virt-manager
提供图形化虚拟机管理界面。
所以常见的软件层次是:
virt-managervirsh │ ↓ libvirt │ ↓QEMU + KVM │ ↓Linux Kernel
3.3 服务启动与权限配置
可以检查 libvirt 服务状态。
不同发行版和版本的服务组织方式可能有所不同,常见环境中可以先执行:
systemctl status libvirtd
需要启动的时候可以使用:
sudo systemctl enable --now libvirtd
如果希望普通用户能够管理虚拟机,可以按照系统实际配置将用户加入相应用户组,例如:
sudo usermod -aG libvirt $USERsudo usermod -aG kvm $USER
完成之后一般需要重新登录会话,使新的用户组权限生效。
3.4 验证安装
首先运行:
virsh list --all
如果能够正常连接 libvirt,则会显示虚拟机列表。
还可以使用:
virsh capabilities
查看宿主机虚拟化能力。
图形环境下能够直接运行:
virt-manager
如果能够正常进入 Virtual Machine Manager,那么基础环境通常就已经搭建完成。
四、KVM 虚拟机生命周期管理
4.1 虚拟机创建:virt-install 实战
创建 KVM 虚拟机可以使用:
virt-install
例如:
sudo virt-install \ --name ubuntu-test \ --memory 4096 \ --vcpus 2 \ --disk path=/var/lib/libvirt/images/ubuntu-test.qcow2,size=40,format=qcow2 \ --cdrom /data/ubuntu.iso \ --network network=default,model=virtio \ --graphics spice
其中:
--name
指定虚拟机名字。
--memory 4096
分配 4GB 内存。
--vcpus 2
分配两个虚拟 CPU。
--disk
用于创建虚拟磁盘。
--network
用于配置虚拟机网络。
model=virtio
表示使用 virtio 半虚拟化网卡。
4.2 虚拟机基本操作
查看全部虚拟机:
virsh list --all
启动:
virsh start ubuntu-test
正常关机:
virsh shutdown ubuntu-test
强制断电:
virsh destroy ubuntu-test
需要特别注意:
destroy
并不是“删除虚拟机”。
它更类似于:
直接拔掉服务器电源。
暂停:
virsh suspend ubuntu-test
恢复:
virsh resume ubuntu-test
重启:
virsh reboot ubuntu-test
4.3 克隆与快照
克隆能够快速复制出一台新的虚拟机。
例如可以使用:
virt-clone \ --original ubuntu-test \ --name ubuntu-test-02 \ --file /var/lib/libvirt/images/ubuntu-test-02.qcow2
快照则用于记录某一个时刻虚拟机的状态。
例如:
virsh snapshot-create-as ubuntu-test before-update
查看:
virsh snapshot-list ubuntu-test
当软件升级失败之后,可以根据虚拟机配置以及快照类型进行回滚。
快照十分方便,可是并不能简单把它当作正式备份来使用。
尤其是在数据库、高写入业务以及长期运行环境当中,必须认真考虑数据一致性以及快照链增长等问题。
4.4 CPU、内存与设备配置修改
查看虚拟机 XML:
virsh dumpxml ubuntu-test
修改:
virsh edit ubuntu-test
KVM/libvirt 很多配置最终都会体现在 XML 当中。
例如:
<vcpu placement='static'>4</vcpu><memory unit='GiB'>8</memory>
也能够使用:
virsh setvcpusvirsh setmem
等命令进行部分资源调整。
某些虚拟硬件还可以通过:
virsh attach-devicevirsh detach-device
实现热插拔。
不过到底能否在线调整,要同时取决于:
是否共同支持。
4.5 虚拟机删除与资源清理
仅执行:
virsh undefine ubuntu-test
主要删除的是虚拟机定义。
磁盘文件可能依旧存在。
如果确定虚拟磁盘也不再需要,可以结合实际情况使用:
virsh undefine ubuntu-test --remove-all-storage
生产环境之中执行这一类命令的时候一定得格外谨慎。
因为真正宝贵的数据往往就在虚拟磁盘之中。
五、KVM 存储体系详解
5.1 存储池的概念
libvirt 提供:
Storage Pool
也就是存储池的概念。
存储池可以理解成为:
给虚拟机磁盘提供统一管理能力的一块存储资源区域。
常见后端包括:
DirectoryLVMNFSiSCSICeph RBD
例如最简单的目录池:
/var/lib/libvirt/images/
虚拟机磁盘镜像就能够直接保存到里面。
5.2 raw、qcow2 与 vmdk
raw
raw 格式比较直接。
例如:
qemu-img create -f raw vm.raw 100G
其结构简单,额外格式开销比较少。
qcow2
qcow2 乃是 QEMU 环境之中非常常见的一种磁盘镜像格式。
例如:
qemu-img create -f qcow2 vm.qcow2 100G
其能够支持:
因此实验以及通用虚拟化环境当中非常常见。
vmdk
VMDK 更多会在 VMware 相关生态之中见到。
进行 VMware 到 KVM 的迁移时经常需要接触。
例如能够使用:
qemu-img convert
完成格式转换。
5.3 磁盘扩容与精简置备
例如对 qcow2 镜像增加 20GB:
qemu-img resize vm.qcow2 +20G
但是必须明白:
这一步只是扩大了:
虚拟磁盘
Guest OS 内部的:
分区LVM文件系统
一般仍然需要进一步扩容。
因此完整流程可能是:
qemu-img resize ↓Guest 识别磁盘扩大 ↓扩展分区 ↓扩展 PV/LV ↓扩展文件系统
5.4 virtio-blk 与磁盘性能优化
如果采用传统 IDE 或者 SATA 设备模拟,QEMU 需要模拟较多真实硬件行为。
而使用:
virtio-blk
或者:
virtio-scsi
则能够通过半虚拟化方式减少这类开销。
典型数据链路可以理解成为:
Guest Filesystem ↓Guest Block Layer ↓virtio-blk ↓virtqueue ↓QEMU / vhost ↓Host Storage
除此之外,磁盘缓存模式也会影响性能以及数据可靠性。
常见模式包括:
nonewritebackwritethroughdirectsyncunsafe
究竟选择哪一个不能只看性能数字,还需要结合:
来综合确定。
六、KVM 网络模式深度解析
6.1 NAT、桥接、仅主机与直通
KVM 虚拟机常见网络方式可以从使用目的上理解成为四类。
NAT
虚拟机经过 Host 转发访问外部网络。
类似:
VM ↓virbr0 ↓Host NAT ↓Physical NIC ↓Internet
这种方式配置简单,非常适合开发测试环境。
Bridge 桥接
虚拟机直接挂载到 Linux Bridge。
例如:
br0 / | \ / | \ Host VM1 VM2 │ eth0 │ Physical LAN
此时 VM 在局域网之中能够表现得更加像一台独立的真实服务器。
Host-only
虚拟机主要和宿主机以及指定虚拟网络内的其他虚拟机通信,不直接访问外部网络。
这种方式经常用于实验环境。
PCI/SR-IOV 直通
把物理网卡或者 VF 直接分配给虚拟机。
此时数据路径能够进一步绕过大量软件网络栈。
例如:
Guest ↓VF Driver ↓Physical NIC VF
这种方案性能很高,但是会牺牲部分灵活性。
6.2 Linux Bridge 配置
传统资料经常能够看到:
brctl
例如:
brctl show
不过在现代 Linux 环境之中,更推荐使用:
ip
以及 NetworkManager、systemd-networkd、Netplan 等系统自身的网络配置方案。
查看网桥:
ip link show type bridge
创建一个简单网桥:
sudo ip link add br0 type bridgesudo ip link set br0 up
把物理接口加入:
sudo ip link set eth0 master br0
生产服务器当中切换网桥配置的时候一定要谨慎,因为错误操作非常容易导致远程 SSH 直接断开。
6.3 virtio-net 与 vhost-net
如果给虚拟机模拟一张传统网卡:
E1000
每次虚拟网络 I/O 都可能产生比较多的设备模拟开销。
virtio-net 则让 Guest 使用专门的半虚拟化驱动。
进一步还可以借助:
vhost-net
把部分 virtio 数据处理从 QEMU 用户空间进一步下沉到内核。
简单来看:
普通 QEMU 网络:
Guest ↓QEMU ↓Host Kernel ↓NIC
virtio + vhost-net:
Guest virtio-net ↓vhost-net ↓Host Networking ↓NIC
这样能够减少部分用户态和内核态之间的切换开销。
6.4 多网卡与 VLAN 隔离
企业环境里面,一台虚拟机往往并不仅仅只有一块网卡。
例如:
eth0 → 管理网络eth1 → 业务网络eth2 → 存储网络
再结合 VLAN:
VLAN 10 → ManagementVLAN 20 → ServiceVLAN 30 → Storage
就能够将不同类型的流量进行隔离。
在 OpenStack、私有云以及 NFV 环境当中,这种网络规划是非常常见的。
七、KVM 性能优化实战
7.1 CPU 优化:NUMA 与 vCPU 绑定
在大型双路或者多路服务器之中,CPU 和内存通常采用:
NUMA
架构。
所谓 NUMA,可以粗略理解成为:
CPU 访问距离自身比较近的本地内存速度更快,而访问其他 NUMA Node 的远程内存成本更高。
查看:
numactl --hardware
可以观察宿主机 NUMA 拓扑。
如果一台高性能虚拟机的 vCPU 在:
NUMA Node 0
可是大量内存却被分配在:
NUMA Node 1
那么就可能产生比较多的跨 NUMA 内存访问。
因此可以进行:
vCPU Pinning
也就是 vCPU 绑定。
例如:
virsh vcpupin vm01
查看当前 vCPU 亲和性。
大型数据库、高性能计算以及低延迟业务尤其需要关注 NUMA。
7.2 内存优化:Balloon、HugePage 与 KSM
Ballooning
virtio-balloon 可以让 Host 和 Guest 在一定范围之内动态调整虚拟机实际占用的内存。
其核心思想可以理解成为:
Host 需要回收内存 ↓Balloon 在 Guest 中膨胀 ↓占据 Guest 内存页 ↓对应物理页被 Host 回收
HugePage
普通 x86 Linux 常见基础页大小为:
4KB
大页则能够使用:
2MB
乃至更大的页。
大页能够减少页表规模以及部分 TLB 压力。
对于大内存虚拟机、数据库等场景能够产生明显价值。
KSM
KSM:
Kernel Samepage Merging
可以扫描相同内容的匿名内存页,并尝试将它们合并。
例如一台 Host 同时运行很多几乎相同的 Linux 虚拟机:
VM1 内存页 AVM2 内存页 AVM3 内存页 A
KSM 有机会将相同内容合并,从而节约物理内存。
不过 KSM 本身同样存在扫描成本以及安全方面的考量,所以生产环境是否开启需要根据实际场景判断。
7.3 I/O 优化:virtio 与 IOThread
虚拟机磁盘性能优化的第一原则通常是:
尽量避免没有必要的传统设备模拟。
常见优化手段包括:
virtio-blkvirtio-scsiIOThread合理缓存模式多队列底层 NVMe/SSD
其中 IOThread 可以让某些虚拟磁盘 I/O 在独立线程里面进行处理,从而减少主 QEMU 线程承担的压力。
7.4 网络优化:vhost-net 与多队列
对于高吞吐虚拟机,可以考虑:
virtio-net+vhost-net+multiqueue
传统单队列网卡可能会形成:
一个队列 ↓一个热点 vCPU
多队列则能够让多个虚拟 CPU 分担网络包处理。
在高并发网络服务器、虚拟化网络功能以及高速数据平面场景之中具有比较重要的意义。
7.5 性能监控
常见工具包括:
virsh domstats vm01
查看虚拟机统计信息。
virsh domifstat vm01 vnet0
查看网卡数据。
virsh domblkstat vm01 vda
查看磁盘数据。
还可以使用:
virt-top
观察虚拟机资源占用。
如果需要进入 Linux 内核以及 CPU 层面进一步定位,则可以使用:
perf
例如查看:
CPU hotspot上下文切换锁竞争调度cache miss
等性能问题。
八、KVM 高级特性与企业级玩法
8.1 动态迁移:虚拟机在线热迁移
热迁移是企业级虚拟化环境之中非常重要的一项能力。
例如当前有:
Host A └── VM01
需要维护 Host A,但是不希望关闭 VM01。
就可以把虚拟机迁移到:
Host B
整个过程大体上可以理解成为:
Host A Host BVM 正常运行 │ ├──复制内存────────────→ │内存继续变化 │ ├──复制脏页────────────→ │短暂停顿 │ └──CPU 状态/剩余页面──→ │ ↓ VM 恢复运行
这类机制常被称作:
Pre-copy Migration
在大量情况下,虚拟机只需要经历比较短的停顿时间。
不过热迁移需要关注:
等等。
所以真正的在线迁移并不是简单执行一条命令就万事大吉。
8.2 PCI 设备直通:GPU 与网卡直通
如果希望虚拟机直接使用真实 PCI 设备,则可以使用:
VFIO
相关机制。
例如:
GPU PassthroughNIC PassthroughNVMe Passthrough
其结构大概是:
Guest ↓Guest Driver ↓VFIO ↓IOMMU ↓Physical PCIe Device
其中:
IOMMU
非常关键。
Intel 平台常见:
VT-d
AMD 平台也具备相应的 IOMMU 能力。
IOMMU 能够限制 DMA 访问的地址范围,从而避免某一个直通设备随意访问整个 Host 的物理内存。
这乃是设备直通隔离的重要基础。
8.3 嵌套虚拟化
嵌套虚拟化可以理解成为:
Physical Host ↓KVM VM ↓VM 内部再次运行 KVM ↓Nested VM
即:
L0 ↓L1 ↓L2
这类技术经常用于:
不过嵌套虚拟化一般会产生更多性能损耗,因此不能简单把它等同于直接运行在物理服务器之上的 KVM。
8.4 SELinux 与虚拟机隔离
虚拟机安全并不只是:
Guest A 不能访问 Guest B
这么简单。
Host 上还需要限制 QEMU 进程到底能够访问哪些:
磁盘目录设备网络资源
SELinux 可以结合 libvirt 对 QEMU 虚拟机进程进一步进行访问控制。
例如即便某一个 QEMU 进程出现安全问题,也尽可能限制它能够读取以及修改的 Host 资源。
企业虚拟化环境需要同时考虑:
KVM 隔离Linux 权限SELinuxcgroupnamespacesVirtIOMMU网络隔离
多个层次,而不是单单依靠其中某一种技术。
8.5 virsh 与 Ansible 自动化
当虚拟机只有两三台的时候:
virt-manager
非常方便。
可是如果需要维护:
10 台100 台1000 台
虚拟机,再一个一个点击图形界面就不现实了。
这个时候就需要自动化。
例如使用 virsh:
for vm in vm01 vm02 vm03do virsh start "$vm"done
还能够进一步使用:
AnsibleTerraformOpenStack自研管理平台
对 KVM 虚拟化资源进行批量管理。
所以企业虚拟化的发展路径通常会经历:
手工 QEMU ↓virsh / libvirt ↓脚本 ↓Ansible ↓云管理平台
九、常见问题排查与踩坑总结
9.1 虚拟机无法启动
遇到虚拟机启动失败之后,不要第一时间删除虚拟机重新创建。
首先执行:
virsh start vm01
查看报错。
然后:
virsh dominfo vm01
检查虚拟机状态。
还要重点查看:
libvirt 日志QEMU 日志journalctl
例如:
journalctl -u libvirtd
常见问题包括:
/dev/kvm 权限不足KVM 模块没有加载VT-x/AMD-V 没打开磁盘镜像不存在XML 配置错误端口冲突设备直通失败内存不足
定位问题的时候最好按照:
硬件 ↓Host Kernel ↓KVM ↓QEMU ↓libvirt ↓Guest
从下往上逐层进行排查。
9.2 网络不通
虚拟机网络不通的时候,可以把问题拆成多个层级。
第一步:
Guest 自己有没有网卡?
ip addr
第二步:
Guest 有没有路由?
ip route
第三步:
Host 有没有:
vnetXtap
接口。
第四步:
虚拟接口有没有正确加入:
virbr0br0
第五步:
Host 防火墙以及 NAT 规则是否正确。
第六步:
物理网络是否允许对应 MAC、VLAN 以及桥接流量。
所以不要一看到虚拟机 ping 不通,就直接认定:
KVM 网络坏了。
真正的问题可能处于任意一层。
9.3 磁盘 IO 异常
如果虚拟机磁盘非常慢,需要同时观察 Guest 和 Host。
Guest:
iostat -x
Host:
iostat -x
再结合:
virsh domblkstat
去判断问题到底出在:
Guest FilesystemLVMvirtioQEMUHost FilesystemRAIDSSDSANCeph
哪一层。
虚拟化系统最大的排障特点之一,就是:
同一个性能问题常常横跨 Guest 和 Host 两套操作系统。
因此不能只盯着 Guest 内部的数据。
9.4 迁移失败
虚拟机迁移失败是企业环境当中非常典型的问题。
重点检查:
CPU 是否兼容
Host A 和 Host B CPU 能力差异太大,有可能造成迁移失败。
存储是否能够访问
共享存储模式下面,目标 Host 必须能够访问虚拟机磁盘。
网络是否一致
迁移之后 Guest 的网络接口必须仍然能够连接到对应网络。
PCI 直通设备
如果 VM 使用了某块本地物理 GPU 或网卡直通,那么热迁移就会变得更加复杂,很多情况下不能像普通纯虚拟设备那样直接迁移。
所以在构建大规模 KVM 集群的时候,迁移能力必须从:
CPU网络存储虚拟设备Host 软件版本
多个层面统一进行规划。
十、总结与进阶路线
10.1 KVM 技术栈全景回顾
学习到这里,我们再从整体来看一遍 KVM。
最底层乃是:
Physical Hardware
包括:
CPUMemoryDiskNICPCIe Device
CPU 通过:
Intel VT-xAMD-V
提供硬件虚拟化能力。
再往上是 Linux 内核中的:
KVM
KVM 通过:
/dev/kvm
向用户空间提供接口。
用户空间当中的:
QEMU
负责构造一台完整虚拟计算机。
进一步:
libvirt
对 QEMU/KVM 进行统一管理。
再往上:
virshvirt-managerOpenStack
则提供命令行、图形以及云平台级别的管理能力。
完整技术栈可以概括成为:
OpenStack │ virt-manager virsh │ libvirt │ QEMU │ /dev/kvm │ KVM │ Linux Kernel │ VT-x / AMD-V │ Hardware
与此同时,虚拟机磁盘、网络和设备 I/O 又会涉及:
virtiovhostLinux BridgeTAPLVMqcow2CephVFIOIOMMUNUMAHugePage
等大量 Linux 技术。
所以真正掌握 KVM,其实并不仅仅是在学习一个虚拟机软件。
而是在学习:
Linux 内核、CPU、内存、网络、存储以及设备虚拟化如何共同协作。
10.2 从 KVM 到 OpenStack、Docker
很多初学者还容易产生另外一个问题:
KVM 和 Docker 是不是同一种东西?
实际上二者解决问题的层次并不相同。
KVM 虚拟的是:
硬件
每一台虚拟机里面能够运行自己独立的 Guest Kernel。
结构大概是:
Hardware ↓Host Linux ↓KVM ↓VM ↓Guest Kernel ↓Application
Docker 这一类容器技术更多是:
操作系统级虚拟化
多个容器共享 Host Kernel。
Hardware ↓Linux Kernel ↓Container Runtime ↓Container
因此 KVM 的隔离边界通常更完整,但是资源开销也会更大。
容器启动更加快速、密度更高,但是依赖共享宿主机内核。
而 OpenStack 则不是另外一种 Hypervisor。
OpenStack 更像是一套云计算管理平台。
它能够对大量:
ComputeStorageNetworkImageIdentity
资源进行统一管理。
其中计算节点底层就经常通过:
Nova ↓libvirt ↓QEMU ↓KVM
来运行真正的虚拟机。
因此三者可以理解成为:
KVM负责虚拟机底层运行OpenStack负责大量虚拟化资源管理Docker/Kubernetes负责容器化应用运行与编排
它们并不是简单互相替代的关系。
10.3 KVM 后续学习路线
如果已经掌握本文里面这些知识,下一步建议继续沿着下面这条路线深入。
第一阶段先掌握:
QEMUKVMlibvirtvirshvirt-install
能够独立完成虚拟机的:
创建启动停止克隆快照网络磁盘
管理。
第二阶段深入:
Linux BridgeTAP/TUNvirtiovhostqcow2LVMNUMAHugePage
理解 KVM 的网络、存储以及性能优化。
第三阶段继续研究:
VFIOIOMMUSR-IOVGPU PassthroughLive MigrationNested Virtualization
掌握企业级虚拟化能力。
第四阶段进入:
OpenStackCephOVSOVNDPDKKubernetes
从单机虚拟化进入真正的数据中心以及云计算技术栈。
当我们从最开始的:
/dev/kvm
一路看到:
KVMQEMUlibvirtvirtioVFIOOpenStack
之后就会发现,KVM 真正有意思的地方并不仅仅是:
“能够在 Linux 上创建一个虚拟机。”
其更加重要的意义在于,它把 Linux 已经具备的调度、内存、网络、存储以及设备管理能力全部连接了起来,再借助 CPU 硬件虚拟化技术,为 Guest OS 构造出了一个完整的虚拟计算环境。
从这一点来看,KVM 就好像是站在 Linux 内核和虚拟机世界之间的一座桥梁一般。
向下,它连接着真正的:
CPU、内存、磁盘、网卡以及 PCIe 设备。
向上,它承载着:
QEMU、libvirt、OpenStack 以及一台又一台虚拟服务器。
真正理解了这一整套数据流以及控制流之后,我们对于 Linux 虚拟化的认识,也就不再只是停留在使用 virt-manager 创建一台虚拟机的层面,而是能够真正看明白:
一台 KVM 虚拟机究竟是怎样从 Linux 内核里面“跑起来”的。