
1. 从一台闲置物理机说起为什么我又回头折腾KVM手里攒了几台退役的塔式服务器配置放在今天看不算亮眼双路四核、32G内存、一块SSD加两块机械盘但就这么闲置着实在可惜。我最初的想法很简单把它们合并成一台能跑多个独立环境的大机器用来做测试、跑一些内部服务、偶尔给团队做演示环境。这个需求听起来像是虚拟机该干的活于是我又一次回到了KVM这个老朋友面前。KVM全称Kernel-based Virtual Machine直译就是“基于内核的虚拟机”。它不是一个独立的软件而是Linux内核自带的一个虚拟化模块。只要你的CPU支持硬件虚拟化指令集加载这个模块之后Linux本身就能变成一个Hypervisor也就是虚拟机监视器。这件事的意义在于你不需要额外安装一套笨重的虚拟化操作系统宿主机该干嘛还干嘛虚拟化能力是“长”在系统里的。我这次要做的就是把一台物理机改造成KVM宿主机在上面跑起若干台虚拟机并且让这些虚拟机之间、虚拟机和宿主机之间、虚拟机和外部网络之间都能按我的意图通信。这个过程中涉及的东西不少CPU虚拟化指令的确认、内核模块的加载、用户态管理工具的选择、存储池的规划、网络桥接的配置、虚拟机的创建与调优。每一环都有坑而且很多坑是文档里不会写的。这篇文章适合谁看如果你手里有闲置的Linux机器想把它变成一台能跑多台虚拟机的实验平台或者你正在学习云计算底层想搞清楚那些云主机到底是怎么被“造”出来的又或者你只是单纯对虚拟化好奇想亲手摸一摸KVM的脾气那接下来的内容应该对你有用。我会从最底层的硬件检查开始一路讲到虚拟机的网络调优中间穿插我自己踩过的坑和实测有效的参数。需要提前说明的是KVM这套东西在Linux世界里已经非常成熟但成熟不代表没有门槛。它的门槛不在于操作有多复杂而在于概念之间的关联需要理清楚内核模块、QEMU、libvirt、virsh、virt-manager、桥接网络、存储池这些名词如果只是零散地记很容易晕。我会尽量把它们串成一条线让你知道每一步在干什么、为什么这么干。2. 动手之前把KVM的技术底座摸清楚2.1 KVM、QEMU、libvirt到底是什么关系很多人第一次接触KVM会被这几个名词绕晕我当初也是。这里用一个生活化的类比把它讲透。想象你要开一家“虚拟机旅馆”每个虚拟机就是一位客人客人需要独立的房间CPU、内存、磁盘、网卡。KVM扮演的角色是旅馆的“硬件基础设施”——它利用CPU的硬件虚拟化能力把物理资源切分成可以分配给客人的虚拟资源。但KVM本身只提供了最底层的虚拟化能力它不会主动去“接待客人”。真正负责接待和安排的是QEMU。QEMU是一个用户态的模拟器它可以模拟出完整的硬件环境主板、芯片组、显卡、网卡、磁盘控制器等等。当QEMU和KVM结合使用时QEMU负责设备模拟和虚拟机生命周期管理KVM负责CPU和内存的硬件加速。所以严格来说你启动一台KVM虚拟机实际运行的是“QEMU进程 KVM内核模块”的组合。那libvirt又是什么它是旅馆的“前台管理系统”。直接调用QEMU的命令行参数非常繁琐而且不同版本的参数还不一样。libvirt提供了一套统一的API和工具把QEMU的复杂性封装起来。你用virsh或者virt-manager操作虚拟机底层其实是libvirt在帮你生成QEMU命令、管理进程、维护状态。libvirt还引入了“域domain”这个概念来指代虚拟机引入了“存储池storage pool”和“网络network”来统一管理资源。理清这三者的关系后面操作时就不会迷茫内核模块提供能力QEMU提供设备模拟和进程libvirt提供管理接口。2.2 硬件虚拟化你的CPU到底支不支持KVM依赖CPU的硬件虚拟化扩展。Intel平台叫VT-xAMD平台叫AMD-V。绝大多数2010年之后的服务器和工作站CPU都支持但不是所有都默认开启。检查方法很简单在Linux终端执行grep -Eoc (vmx|svm) /proc/cpuinfo如果输出大于0说明CPU支持。vmx对应Intel的VT-xsvm对应AMD的AMD-V。如果输出是0那要么CPU不支持要么主板BIOS里没开启虚拟化选项。后者更常见尤其是品牌整机BIOS里默认可能是关闭的。我遇到过一台机器CPU明明是支持VT-x的但/proc/cpuinfo里就是看不到vmx标志。进BIOS翻了一圈发现虚拟化选项藏在“Security”菜单下的“Virtualization Technology”里默认Disabled。开启之后保存重启标志就出来了。还有一个容易忽略的点如果你是在虚拟机里再跑KVM也就是嵌套虚拟化需要宿主机层面也开启嵌套支持。Intel平台需要给kvm_intel模块加nested1参数AMD平台对应kvm_amd模块。这个场景在实验环境里很常见比如你在云主机里想再开虚拟机测试。2.3 内核模块加载与确认确认CPU支持之后加载KVM模块modprobe kvm modprobe kvm_intel # Intel平台 # 或 modprobe kvm_amd # AMD平台加载完成后用lsmod | grep kvm确认。正常应该看到kvm和kvm_intel或kvm_amd两个模块。如果kvm_intel加载失败常见原因是BIOS没开VT-x或者和某些其他虚拟化软件冲突。这里有个细节kvm模块是核心模块kvm_intel和kvm_amd是平台相关的硬件加速模块。有些发行版会把它们编译进内核而不是作为模块这种情况下lsmod看不到但功能是正常的。可以用dmesg | grep kvm看内核日志确认。提示如果你在容器环境里操作比如某些受限的容器可能无法加载内核模块。KVM需要真正的宿主机权限容器里通常跑不了。2.4 用户态工具链的选型管理KVM虚拟机工具链有几套选择我列个表对比一下工具类型适用场景我的评价qemu-system-x86_64命令行快速测试、脚本化参数极多不适合日常管理virsh命令行服务器环境、自动化功能完整脚本友好首选virt-manager图形界面桌面环境、初学者直观易用适合快速上手virt-install命令行创建虚拟机配合virsh使用创建流程清晰我的建议是服务器环境用virsh virt-install桌面环境可以装virt-manager辅助。virt-manager底层也是调libvirt和virsh操作的是同一套东西两者可以混用。比如你用virt-manager创建了一台虚拟机用virsh list照样能看到。安装这些工具不同发行版包名略有差异。以常见的两个系为例# Debian/Ubuntu系 apt install qemu-kvm libvirt-daemon-system libvirt-clients virtinst bridge-utils # RHEL/CentOS系 yum install qemu-kvm libvirt libvirt-client virt-install bridge-utils安装完成后启动libvirtd服务并设置开机自启systemctl enable --now libvirtd systemctl status libvirtd服务起来之后用virsh list --all测试一下如果能看到空的虚拟机列表说明libvirt工作正常。3. 存储与网络虚拟机跑起来的两条腿3.1 存储池规划别把镜像和磁盘混在一起虚拟机的存储分两部分一部分是安装镜像ISO文件一部分是虚拟机的磁盘镜像qcow2或raw格式。这两类文件建议分开存放原因后面讲。libvirt用“存储池”来管理存储资源。存储池可以是一个目录、一个LVM卷组、一个NFS共享等等。最常用的是目录类型的存储池。我一般会规划两个池iso-pool存放安装镜像路径比如/data/libvirt/isovm-pool存放虚拟机磁盘路径比如/data/libvirt/images创建目录并定义存储池mkdir -p /data/libvirt/iso /data/libvirt/images virsh pool-define-as iso-pool dir --target /data/libvirt/iso virsh pool-build iso-pool virsh pool-start iso-pool virsh pool-autostart iso-pool virsh pool-define-as vm-pool dir --target /data/libvirt/images virsh pool-build vm-pool virsh pool-start vm-pool virsh pool-autostart vm-pool用virsh pool-list --all确认两个池都是active状态。为什么建议分开因为ISO文件通常很大且只读虚拟机磁盘是频繁读写的。如果放在同一个目录备份和迁移时不好区分。另外如果磁盘空间紧张你可以单独清理ISO池而不影响运行中的虚拟机。3.2 磁盘格式选择qcow2还是raw创建虚拟机磁盘时libvirt默认用qcow2格式。qcow2支持稀疏分配、快照、压缩、加密功能丰富。raw格式则是裸格式性能略好但不支持快照空间是预分配的。我实测下来的感受是对于大多数测试和开发场景qcow2完全够用性能差异在日常使用中几乎感知不到。只有在高IO压力的生产场景raw格式的优势才会体现。而且qcow2的稀疏特性很实用——你创建一个100G的磁盘实际占用可能只有几百K随着数据写入才逐渐增长。创建磁盘的命令virsh vol-create-as vm-pool test-vm.qcow2 20G --format qcow2这条命令在vm-pool里创建一个20G的qcow2磁盘。注意20G是虚拟大小实际占用取决于写入数据量。注意qcow2磁盘长期使用后可能出现碎片影响性能。可以用qemu-img convert做一次压缩整理把稀疏块合并。操作前务必关闭虚拟机。3.3 网络桥接让虚拟机和宿主机平起平坐KVM虚拟机的网络模式有好几种最常用的是NAT模式和桥接模式。NAT模式下libvirt默认创建一个virbr0网桥虚拟机通过它做地址转换访问外网。虚拟机可以访问外部但外部无法直接访问虚拟机除非做端口转发。这种模式配置简单适合个人测试。桥接模式下虚拟机直接接入物理网络和宿主机处于同一网段拥有独立的IP外部可以直接访问。这种模式适合需要对外提供服务的场景。我这次的需求是让虚拟机能被局域网其他机器访问所以选了桥接模式。配置桥接需要修改宿主机网络配置不同发行版方式不同。以NetworkManager管理的系统为例可以用nmcli创建桥接nmcli con add type bridge ifname br0 con-name br0 nmcli con add type bridge-slave ifname eth0 master br0 nmcli con modify br0 ipv4.addresses 192.168.1.10/24 nmcli con modify br0 ipv4.gateway 192.168.1.1 nmcli con modify br0 ipv4.dns 192.168.1.1 nmcli con modify br0 ipv4.method manual nmcli con up br0这里把物理网卡eth0桥接到br0上br0拥有IP地址。虚拟机创建时指定桥接网络就会接入br0从而和宿主机在同一网段。提示桥接配置有个经典坑——如果你是通过SSH远程操作宿主机修改网络配置时可能把自己踢下线。建议在本地控制台操作或者用nmcli的交互模式确认配置无误再应用。我吃过这个亏远程改桥接网卡一重启SSH就断了只能跑机房。3.4 网络模式对比与选择建议模式虚拟机访问外网外部访问虚拟机配置复杂度适用场景NAT可以需端口转发低个人测试、临时环境桥接可以可以直接中对外服务、集群实验仅主机不可以宿主机可以低隔离测试直通取决于物理网络取决于物理网络高高性能网络场景我的经验是实验环境优先用NAT省事需要对外服务时再切桥接。桥接虽然方便但会占用局域网IP如果虚拟机数量多IP管理是个负担。另外某些企业网络对桥接有限制交换机端口可能只允许一个MAC地址这种情况下桥接会失败需要联系网络管理员调整。4. 创建第一台虚拟机从命令行到系统安装4.1 virt-install参数详解创建虚拟机用virt-install命令。这个命令的参数很多但常用的就那么几个。我以创建一台2核4G、20G磁盘的虚拟机为例virt-install \ --name test-vm01 \ --memory 4096 \ --vcpus 2 \ --cpu host-passthrough \ --disk volvm-pool/test-vm01.qcow2,formatqcow2,busvirtio \ --cdrom /data/libvirt/iso/install.iso \ --network bridgebr0,modelvirtio \ --graphics vnc,listen0.0.0.0,port5901 \ --os-variant generic \ --boot cdrom,hd \ --noautoconsole逐条解释关键参数--name虚拟机名称在libvirt里唯一。--memory内存大小单位MB。--vcpus虚拟CPU数量。--cpu host-passthroughCPU模式让虚拟机看到宿主机的真实CPU特性性能最好。如果要做虚拟机迁移建议用host-model。--disk磁盘配置。volvm-pool/test-vm01.qcow2指定存储池和卷名busvirtio使用virtio总线性能远好于IDE。--cdrom安装镜像路径。--network网络配置bridgebr0接入桥接modelvirtio使用virtio网卡。--graphics图形输出方式VNC监听所有地址端口5901。--os-variant操作系统类型帮助libvirt优化默认配置。可以用osinfo-query os查询支持的列表。--boot启动顺序先光驱后硬盘。--noautoconsole不自动打开控制台适合脚本化。4.2 连接VNC完成系统安装虚拟机启动后用VNC客户端连接宿主机的5901端口就能看到安装界面。如果宿主机有防火墙需要放行对应端口firewall-cmd --add-port5901/tcp --permanent firewall-cmd --reload安装过程和物理机装系统没区别分区、设置用户、装软件包。唯一需要注意的是安装完成后重启时记得把启动顺序改成硬盘优先或者直接弹出ISO。用virsh可以修改virsh change-media test-vm01 sda --eject或者直接编辑虚拟机配置virsh edit test-vm01把boot order1/从cdrom移到disk上。4.3 安装后的基础优化系统装好之后有几件事建议立刻做第一安装virtio驱动。虽然现代Linux内核自带virtio驱动但Windows系统需要额外安装。Linux系统确认一下lsmod | grep virtio应该能看到virtio_net、virtio_blk、virtio_pci等模块。第二安装qemu-guest-agent。这个代理程序让宿主机能获取虚拟机的内部信息比如IP地址、文件系统使用情况还支持优雅关机。安装命令# Debian/Ubuntu apt install qemu-guest-agent # RHEL/CentOS yum install qemu-guest-agent systemctl enable --now qemu-guest-agent装好之后在宿主机用virsh domifaddr test-vm01就能直接看到虚拟机的IP不用登录进去查。第三调整磁盘缓存模式。默认的缓存模式是none对于qcow2磁盘我一般改成writeback写入性能更好但断电时可能丢数据。如果虚拟机里跑的是数据库建议保持none或writethrough。这个取舍看场景。4.4 虚拟机生命周期管理常用命令日常管理虚拟机这几个virsh命令用得最多virsh list --all # 列出所有虚拟机 virsh start test-vm01 # 启动 virsh shutdown test-vm01 # 优雅关机 virsh destroy test-vm01 # 强制断电 virsh reboot test-vm01 # 重启 virsh suspend test-vm01 # 暂停 virsh resume test-vm01 # 恢复 virsh undefine test-vm01 # 删除虚拟机定义 virsh console test-vm01 # 连接串口控制台virsh console需要虚拟机内部配置了串口终端否则连上去是黑的。配置方法是在虚拟机里修改内核参数加上consolettyS0并启用getty服务。这个在调试无图形界面的虚拟机时很有用。5. 性能调优与常见问题排查5.1 CPU和内存的性能调优CPU方面host-passthrough模式性能最好但牺牲了迁移兼容性。如果虚拟机需要在不同CPU的宿主机之间迁移用host-model。另外可以给虚拟机绑定物理CPU核心减少上下文切换virsh vcpupin test-vm01 0 2 virsh vcpupin test-vm01 1 3这条命令把虚拟机的vCPU 0绑定到物理CPU 2vCPU 1绑定到物理CPU 3。绑定前先用lscpu或virsh nodecpustats了解物理CPU拓扑。内存方面KVM支持内存气球balloon技术可以动态调整虚拟机内存。启用方法是在虚拟机配置里加memoryBacking memballoon modelvirtio/ /memoryBacking然后在宿主机用virsh setmem test-vm01 2048动态调整。不过气球技术需要虚拟机内部驱动配合且调整有下限不能低于系统运行所需的最小内存。5.2 磁盘IO性能优化磁盘性能是虚拟机最容易出现瓶颈的地方。几个优化方向第一使用virtio-blk或virtio-scsi总线。virtio-scsi支持更多设备且性能更好。配置方法disk typevolume devicedisk driver nameqemu typeqcow2 cachewriteback ionative/ source poolvm-pool volumetest-vm01.qcow2/ target devsda busscsi/ /diskionative启用Linux原生异步IOcachewriteback启用写缓存。第二考虑使用LVM或裸设备作为虚拟机磁盘。qcow2虽然方便但多了一层文件系统抽象性能有损耗。如果追求极致性能可以在LVM卷上直接创建虚拟机磁盘。第三定期整理qcow2碎片。用qemu-img convert把磁盘转换一次可以合并稀疏块、压缩空间qemu-img convert -O qcow2 -c test-vm01.qcow2 test-vm01-compact.qcow2-c参数启用压缩。转换完成后替换原文件注意先关闭虚拟机。5.3 常见问题速查表问题现象可能原因排查方法解决方法虚拟机启动失败提示KVM不可用CPU虚拟化未开启grep vmx /proc/cpuinfo进BIOS开启VT-x/AMD-V虚拟机网络不通桥接配置错误brctl show查看桥接检查物理网卡是否加入桥接VNC连接黑屏显卡模式不兼容查看虚拟机日志改用cirrus或vga显卡磁盘IO极慢缓存模式不当virsh dumpxml查看配置调整cache和io参数虚拟机无法关机未装guest-agentvirsh domifaddr测试安装qemu-guest-agent迁移失败CPU模式不兼容对比宿主机CPU特性改用host-model或自定义CPU5.4 我踩过的几个坑第一个坑桥接网络配置后宿主机失联。前面提过远程改网络配置风险很大。后来我的做法是先用nmcli创建一个新的桥接连接但不立即启用确认配置无误后用nmcli con up br0切换同时保留一个备用SSH连接。如果切换后失联备用连接还能救回来。第二个坑qcow2磁盘空间只增不减。虚拟机里删了文件宿主机上的qcow2文件大小不变。这是因为qcow2不会自动回收未使用的块。解决方法是用virt-sparsify工具做稀疏化virt-sparsify --compress test-vm01.qcow2 test-vm01-sparse.qcow2这个操作需要虚拟机停机且耗时较长但能显著回收空间。第三个坑虚拟机时间不同步。KVM虚拟机默认使用UTC时间如果虚拟机里设置的是本地时间会出现时间偏移。解决方法是在虚拟机配置里加clock offsetlocaltime/或者在虚拟机内部配置NTP同步。第四个坑大页内存未启用导致性能不佳。对于内存密集型应用启用大页内存能减少TLB miss提升性能。宿主机上配置echo 1024 /proc/sys/vm/nr_hugepages然后在虚拟机配置里加memoryBacking hugepages/ /memoryBacking注意大页内存需要预分配且虚拟机内存必须是2M的整数倍。6. 从单机到实验平台后续扩展思路一台宿主机跑几台虚拟机只是起点。如果想把这套东西扩展成一个像样的实验平台有几个方向可以走。第一个方向是集中管理。libvirt支持远程连接在管理机上装virt-manager通过qemussh://宿主机IP/system连接到多台宿主机统一管理所有虚拟机。这样不用每台机器都跑图形界面。第二个方向是模板化。把装好的虚拟机做成模板后续创建新虚拟机时直接克隆省去重复安装系统的时间。克隆命令virt-clone --original test-vm01 --name test-vm02 --file /data/libvirt/images/test-vm02.qcow2克隆前需要把模板虚拟机里的机器ID、MAC地址等唯一信息清理掉否则克隆出来的虚拟机会冲突。Linux系统可以用virt-sysprep工具自动清理。第三个方向是自动化编排。libvirt有Python绑定可以写脚本批量创建、启动、销毁虚拟机。配合Ansible等配置管理工具能实现整套实验环境的自动化部署。我试过用Python的libvirt模块写了一个小脚本根据配置文件批量创建虚拟机几十台机器的环境几分钟就能拉起来。第四个方向是结合容器。KVM虚拟机和容器不是对立的可以混合使用。比如用虚拟机跑需要完整内核的环境用容器跑轻量级服务两者通过桥接网络互通。这种混合架构在实际生产中很常见。最后分享一个我在实际使用中的体会KVM的稳定性非常高我有一台宿主机连续运行了大半年没重启上面的虚拟机也一直稳定工作。但稳定不代表可以不管定期检查磁盘空间、查看虚拟机日志、更新宿主机内核和QEMU版本这些日常维护不能省。尤其是QEMU的安全更新涉及虚拟机逃逸漏洞的补丁要及时打。另外如果你打算在生产环境用KVM建议先在小规模环境充分测试特别是存储和网络的性能表现。不同硬件组合下的表现差异可能很大别人的调优参数不一定适合你的机器。多测、多调、多记录慢慢就能摸清自己这套环境的脾气。