说句实在话在Linux环境下搞虚拟化迟早绕不过QEMU和KVM这个组合。KVM负责把CPU和内存的虚拟化能力拉到内核态QEMU在用户态模拟各种设备两个东西协作几乎能把物理机的性能榨到极限。这篇文章就围绕“QEMU KVM环境搭建”这个主题从方案选择、环境检查、安装步骤、网络配置到性能调优和问题排查完整走一遍实操流程。不管你是第一次接触虚拟化的新手还是想从VirtualBox换到原生方案的老人都能从里面找到直接能用的命令和思路。先说一下我为什么特别推荐这套组合。在Linux上你有很多虚拟化选项但QEMUKVM有它不可替代的位置它是Linux内核自带的能力不依赖闭源驱动它跟libvirt生态结合非常紧密virt-manager、virsh、Ansible这些工具都能直接管理更重要的是它代表了一套完整的模块化设计——CPU虚拟化、存储虚拟化、网络虚拟化各自独立出了问题可以一层一层排查。这篇文章既然以“环境搭建”为核心我就把从零到把虚拟机跑起来的过程包括中间会踩的那些坑全部拆开讲清楚。1. 为什么偏偏是QEMU和KVM1.1 QEMU和KVM各自干了什么活很多人在刚接触这套方案时会困惑既然KVM是内核模块QEMU又是用户态程序那到底谁在“虚拟化”这里我用人话解释一下。KVMKernel-based Virtual Machine本身不是一个完整的模拟器它只负责把宿主机的CPU和内存虚拟化能力暴露出来。CPU厂商Intel和AMD在硬件里实现了VT-x和AMD-V指令集KVM模块接手这些指令集为虚拟机提供接近原生性能的CPU执行环境。没有KVM的话QEMU只能靠纯软件模拟CPU性能掉得非常厉害跑一个编译任务能慢到让你怀疑人生。QEMU则负责模拟整台机器里除CPU核心计算之外的所有东西——主板、BIOS、磁盘控制器、网卡、显卡、USB控制器等等。所以你在虚拟机里看到的那个-machine pc或者-machine q35本质上是QEMU提供的机器模型。KVM处理计算部分QEMU处理设备模拟部分两者通过特殊机制比如/dev/kvm接口沟通协作这就是为什么命令行里常见到-accel kvm这个参数。1.2 跟VirtualBox、VMware和纯软件模拟的对比把QEMUKVM和其他方案对比你就能理解为什么在Linux服务器上这套组合几乎是标配。方案CPU性能设备模拟管理生态适合场景QEMUKVM接近原生QEMU模拟virtio可优化libvirt/virsh/virt-managerLinux服务器、多租户、自动化VirtualBox一般依赖内核模块自带模拟设备图形界面为主命令行受限桌面开发、测试、Windows兼容VMware Workstation较好商业驱动设备兼容较好商用收费跨平台桌面虚拟化纯QEMU软件模拟极慢完整模拟命令行为主跨架构开发、逆向如ARM模拟这个对比里核心区别在“CPU性能”这一栏。KVM让CPU虚拟化从“解释执行”变成了“硬件加速执行”指令直接在物理CPU上跑只是有一层语义检查。如果你只是在本机开个虚拟机体验一下Linux桌面VirtualBox完全够用但如果你要在服务器上跑几十台虚拟机或者把虚拟化能力开放给其他团队作为基础设施那KVM/QEMU这套才是正路。1.3 从项目需求角度倒推技术选型回到“QEMU KVM环境搭建”这个标题本身它背后通常对应着几类真实需求。一类是个人学习场景你想深入理解Linux的虚拟化机制、libvirt的管理方式或者准备考虚拟化相关的认证需要亲手搭一遍环境。另一类是开发环境需求你需要在干净的虚拟机里测试多节点部署、滚动升级或故障恢复要求隔离性好、可重复创建。还有一类是服务器整合场景某台物理机上资源利用率不高想把多个服务合并成几台虚拟机跑节约硬件成本。这三种场景QEMUKVM都压得住。因为它内置于Linux内核不依赖于某个公司的商业驱动不做集群管理也有单机libvirt兜底做集群管理时可以扩展到OpenStack这类架构。这个选型逻辑很重要——你选择的不只是一个虚拟化组件而是一整套可扩展的生态。2. 动手之前先看清硬件和环境2.1 确认处理器是否支持硬件虚拟化环境搭建前最怕的是一件小事CPU不支持虚拟化安装完成后KVM模块怎么都加载不上虚拟机一启动就报错你还会怀疑是命令敲错了。所以第一步永远是检查硬件。Intel和AMD的CPU在十几年前就基本都带虚拟化扩展了但有些主板或者服务器固件会把特性关闭。Intel平台叫VT-xAMD平台叫AMD-V。在Linux终端里用下面方式确认grep -E -o (vmx|svm) /proc/cpuinfo | sort -u如果输出里有vmx你在Intel机器上有svm就是AMD机器。如果什么都不输出大概率是BIOS/UEFI里把虚拟化关掉了。进到固件设置里找“Intel Virtualization Technology”或“SVM Mode”这类选项打开后重启再试一次。另一个关键工具是lscpu它会显示虚拟化相关的Flags。里面包含vmx或svm并且包含ept或npt说明CPU还支持扩展页表这个对内存性能影响很大。没有ept的情况下KVM只能做传统的内存影子页表内存压力大的虚拟机性能会明显下降。2.2 宿主机系统与内核版本的要求QEMUKVM这套方案跟内核版本强相关。KVM模块在Linux内核里维护了很久很老的内核也能用但如果你想用新特性比如嵌套虚拟化、NUMA调优、virtiofs那内核版本就得跟上来。我建议宿主系统至少使用仍在维护的发行版比如Debian 12以上、Ubuntu 22.04 LTS以上、RHEL 8/9系或者对应的社区版本。在这些发行版上内核和软件包配合默契libvirt、qemu-kvm这些包编译参数也都是经过测试的。太老的内核经常会在启动virt-manager时弹各种版本告警浪费你去排查的时间。检查内核版本直接一条命令uname -r内核版本只要不低于4.15基本都能满足常见需求。如果你要用的是virtiofs这种较新的共享文件系统建议内核5.4以上。宿主系统选择这个事看起来跟“环境搭建”关系不大但一旦选错后面每个步骤都会遇到包版本冲突所以值得在开头讲清楚。2.3 内核模块加载前的基础设置正常情况下你的发行版内核已经把KVM相关模块编进去了。你可以看到模块是否存在lsmod | grep kvm没输出也别慌先试着加载sudo modprobe kvm sudo modprobe kvm_intel # Intel平台 # 或者 sudo modprobe kvm_amd # AMD平台加载后再次检查/dev/kvm这个设备节点是否出现ls -l /dev/kvm如果/dev/kvm不存在后续的qemu-system-x86_64即使能启动虚拟机也会退化成纯软件模拟性能差距巨大。一个常见的原因是权限问题比如设备节点存在但你当前的用户不在libvirt或kvm用户组里所以虚拟机启动时报Permission denied。这不在本节展开后面排查部分会详细说。另外如果你的用途是跑嵌套虚拟化——也就是在虚拟机里再开KVM虚拟机——需要在模块加载时打开嵌套选项。写在/etc/modprobe.d/kvm.conf里。3. 从零到一把第一台虚拟机跑起来3.1 安装依赖包与工具链不同发行版的包管理方式不同但核心要装的软件是同一套逻辑KVM模块内核自带、QEMU用户态工具、libvirt管理栈再加一个方便操作的命令行工具和可选图形界面。我用Debian系和RHEL系各给一套完整命令。Debian/Ubuntu系sudo apt update sudo apt install qemu-system-x86 qemu-utils libvirt-daemon-system libvirt-clients virtinst virt-managerRHEL/Rocky/AlmaLinux系sudo dnf install qemu-kvm qemu-img libvirt virt-install virt-manager装完以后有个细节值得说明qemu-system-x86_64这个二进制是QEMU的主程序qemu-img负责创建和转换磁盘镜像virt-install是libvirt提供的半自动化安装工具适合用命令行创建虚拟机。这些工具各司其职写脚本时你会同时用到它们。3.2 启动libvirtd服务并设置权限libvirtd是libvirt的守护进程virt-manager、virsh、virt-install都要通过它来跟KVM/QEMU交互。启动它sudo systemctl enable --now libvirtd然后把你自己的用户加入libvirt和kvm组这样以后执行virsh命令不用一直加sudosudo usermod -aG libvirt,kvm $USER注意这个操作需要重新登录会话才生效。我之前在这件事上吃了亏——加完组没重新登录然后一直怀疑权限配置写错了排查半天才发现是Shell会话没有刷新用户组信息。启动完成后用virsh list --all验证一下能不能正常连接管理接口。只要不出错输出的列表是空的也没关系说明libvirt在正常工作。3.3 用命令行创建一台最小化虚拟机这里我不依赖virt-manager的图形向导直接用virt-install把一台虚拟机建出来因为命令行方式在脚本化和自动化场景里更通用也更适合写博客演示。先准备一个磁盘镜像文件。如果你希望虚拟机性能更好用raw格式如果你关心磁盘空间利用率和快照能力用qcow2格式。首次创建通常选qcow2因为不会一次性占满宿主磁盘空间qemu-img create -f qcow2 /var/lib/libvirt/images/mytest-vm.qcow2 20G然后通过virt-install创建虚拟机并挂载安装ISO。下面命令里的--location表示从镜像引导安装virt-install \ --name mytest-vm \ --ram 2048 \ --vcpus 2 \ --disk path/var/lib/libvirt/images/mytest-vm.qcow2,formatqcow2,busvirtio \ --cdrom /path/to/linux-install.iso \ --os-variant linux2022 \ --network networkdefault,modelvirtio \ --graphics vnc,listen0.0.0.0 \ --noautoconsole那一长串参数逐个拆开解释一下。--ram 2048是给虚拟机2GB内存--vcpus 2是2个vCPU--disk里用busvirtio告诉QEMU用virtio半虚拟化磁盘而不是模拟IDE或SATA。--network networkdefault是使用libvirt创建的默认NAT网络modelvirtio也是半虚拟化网卡。--graphics vnc是让虚拟机显示输出走VNC服务方便远程连接。创建后虚拟机会在后台启动。用virsh list --all就能看到它处于运行状态。VNC通常是端口5900用VNC客户端连上去就能看到图形安装界面。3.4 安装系统时的三个关键细节第一个细节是存储格式。如果你打算以后做快照、做增量备份qcow2是必须的raw格式虽然I/O性能略好但那点性能差异在普通应用里很难感知。而且qcow2还可以在创建后通过qemu-img转换成其他格式灵活性更好。第二个细节是网络模型。建议始终使用virtio而不是默认的e1000或rtl8139。virtio是半虚拟化设备规范几块网卡都可以通过virtio连接到宿主机的vhost虚拟交换机性能成倍提升。你可以在创建虚拟机后用virsh edit修改配置把网卡模型改成virtio但更好的办法是从创建时就定好。第三个细节是安装源。用--cdrom指定网络ISO或者本机ISO都行但如果ISO放在宿主机的非标准路径记得给libvirt用户的读取权限。很多人卡在这里虚拟机启动后黑屏排查半天发现是ISO文件权限不对QEMU进程根本没有权限读那个镜像。4. 网络配置理解默认NAT再迈向桥接4.1 默认NAT网络的工作机制virt-install里使用networkdefault时libvirt会自动创建一个名为virbr0的虚拟网桥。这个网桥的IP通常是192.168.122.1虚拟机的网卡通过它访问宿主机和外部网络而宿主机再通过iptables做NAT转发外部设备无法直接访问虚拟机。这种模式的好处是零配置、安全性好。你在自己电脑上开一台虚拟机完全不想管外部访问用默认NAT就够了。但如果要搭建真实服务比如宿主机上开一个开发集群多个虚拟机需要互相通信或者外部网络需要直接访问虚拟机那NAT就不够用。查看当前网络定义virsh net-list --all virsh net-dumpxml default你能在XML定义里看到forward modenat/和DHCP地址范围这些参数是可以直接修改的。4.2 手动创建桥接网络的完整流程桥接网络的目标是让虚拟机网卡直接挂在物理网络里让虚拟机和宿主机以及局域网中的其他设备处于同一个二层网络。这样虚拟机自己获取IP外部设备可以直接访问虚拟机。先检查宿主机上是否装了bridge-utils或者iproute2现代系统推荐用iproute2操作网桥sudo ip link add name br0 type bridge然后把宿主机物理网卡以从属方式加入桥接。这里有个坑如果你把eth0直接或ip link set eth0 master br0原IP会从eth0上消失需要再配置到br0上。操作前务必备份网络配置不然SSH会断。以Ubuntu的netplan为例桥接大概是这样network: version: 2 ethernets: eth0: dhcp4: false bridges: br0: interfaces: [eth0] dhcp4: true dhcp6: trueRHEL系用NetworkManager的话可以用nmclinmcli con add type bridge ifname br0 con-name br0 nmcli con add type ethernet ifname eth0 master br0 con-name bridge-port-eth0 nmcli con up br0网桥建好后在libvirt里定义一个桥接网络或者直接用现有网桥。最简单的方式是用XML文件定义cat /tmp/br0-network.xml EOF network namebr0-host/name forward modebridge/ bridge namebr0/ /network EOF virsh net-define /tmp/br0-network.xml virsh net-start br0-host virsh net-autostart br0-host之后创建或编辑虚拟机时把网卡指向这个网络--network networkbr0-host,modelvirtio4.3 静态IP与公共服务的访问管理使用桥接网络后虚拟机向DHCP服务器获取IP跟宿主机没有父子关系。这带来一个好处虚拟机可以直接对外提供服务。但也带来一个安全问题虚拟机暴露在局域网里补丁没打或者端口乱开风险由你自己承担。为防止服务地址频繁变动我习惯给重要虚拟机配静态IP。在虚拟机内部用对应系统的方式设置固定IP再配合宿主机侧的防火墙规则限制入站流量。比如只允许特定来源访问SSH端口其他一律丢弃。桥接模式和NAT模式的运维思路完全不同从默认NAT切到桥接时务必先想清楚暴露面。一个折中做法是对外服务用桥接管理面保持默认NAT。运营级环境更复杂但单机环境下这个思路够用。5. 性能调优让虚拟机更接近物理机5.1 CPU热点与NUMA架构下的配置KVM默认给虚拟机分配vCPU时没有绑定到特定物理CPU。对于低负载场景没问题但高负载应用里vCPU在物理核心之间频繁跳变缓存和TLB命中率会下降性能损失可达5%到20%。绑定vCPU到固定的物理核心叫做CPU pinning。你可以通过virsh vcpupin命令完成。先查看宿主机拓扑lscpu -e找出空闲的核心编号比如0、2、4、6属于某个NUMA节点把它们分配给虚拟机的vCPU。然后执行virsh vcpupin mytest-vm 0 0 virsh vcpupin mytest-vm 1 2NUMA方面多路服务器上内存访问也有远近之分。如果虚拟机需要大量内存尽量把虚拟机的内存和vCPU分配在同一个NUMA节点。numactl --hardware能展示NUMA节点布局。创建虚拟机时可以用--numatune参数指定节点比如--numatune 0表示内存优先从NUMA节点0分配。这个概念对普通个人电脑影响不大但服务器上差异很明显。5.2 磁盘I/O和存储后端选择磁盘I/O在虚拟化里往往是最明显的瓶颈。QEMU提供了很多后端选择最常见的组合是文件后端就是qcow2/raw格式放在宿主机文件系统里和块设备后端直接使用宿主机的物理分区或逻辑卷。文件后端里qcow2的开销比raw高一些因为它要处理COW写时复制和元数据。追求极致性能的虚拟机比如数据库实例通常把系统盘和数据盘分开数据盘使用raw或直接的LVM逻辑卷qemu-img create -f raw /dev/vg0/vm-data 100G然后创建虚拟机时直接指定这个块设备--disk path/dev/vg0/vm-data,formatraw,busvirtio,cachenonecachenone这个参数很重要它告诉QEMU不要用宿主机Page Cache缓存虚拟机的数据而是直接通过O_DIRECT方式读写宿主机块设备。这样在虚拟机关机场景下数据更安全也不会占满宿主机内存。不过raw裸设备无法用qcow2快照需要根据应用需求取舍。在写场景下virtio-blk配合多队列multi-queue也是值得尝试的参数。在多队列模式下虚拟机每个vCPU可以对应一个IO队列并发能力有更明显的提升。配置方法是在虚拟机XML的controller typevirtio-serial或者磁盘设备里增加queues4属性。5.3 内存大页(HugePages)的配置普通的内存页大小是4KB虚拟机用到的物理内存页数量很大TLB快表很容易击穿。启用大页内存后页大小变为2MB或1GB每次内存地址翻译可以覆盖更大的连续区域TLB命中率大幅提升。配置分为两步。第一步设置系统的大页池在登录Shell执行sudo sysctl -w vm.nr_hugepages10241024个2MB页就是2GB预留内存。要永久生效写进/etc/sysctl.conf。第二步让QEMU使用大页。使用libvirt时在虚拟机XML的memoryBacking节点里加memoryBacking hugepages/ /memoryBacking大页内存会导致虚拟机的内存管理跟普通进程不同QEMU进程需要足够的权限而且宿主机必须预留对应数量的内存。如果预留不足虚拟机启动时会直接报内存分配失败。启用大页后虚拟机内存不会使用Swap机制物理内存不足时风险也需要考虑。5.4 一个简单基准测试的实操演示性能调优不能靠感觉简单跑个基准就能看出差异。我常用sysbench测CPU和内存用fio测磁盘。CPU测试sysbench cpu run在物理机和虚拟机里分别跑一次次数、线程数保持一致看events/s的差异。磁盘测试要指定文件大小、读写模式fio --namerandread --ioenginelibaio --rwrandread --bs4k --size1G --numjobs4 --directory/data --runtime30对比开启virtio、cachenone和大页前后的IOPS数据。实测下来正确的存储配置可以比默认模拟磁盘高出好几倍的IOPS。这些数据不是用来证明某个配置绝对好而是用来帮你确认哪一层是瓶颈。6. 高频问题与排查思路实战6.1 /dev/kvm不存在时的处理这是新手最容易碰到的报错。virt-manager或qemu-system-x86_64启动虚拟机时弹出一句Could not access KVM kernel module: No such file or directory。排查顺序固定如下。先看内核模块有没有加载lsmod | grep kvm没有就用modprobe kvm_intel或modprobe kvm_amd加载。加载失败的话极大概率是BIOS/UEFI里关闭了硬件虚拟化扩展回到本文开头说的检查步骤重做一遍。如果模块已经加载确认/dev/kvm是否存在且权限正确ls -l /dev/kvm有些发行版在udev规则里没有自动设置组权限你可以手动改一下设备节点属组sudo chown root:kvm /dev/kvm sudo chmod 0660 /dev/kvm最后确认当前用户是否在kvm组里不在就加进去sudo usermod -aG kvm $USER改完重新登录再试一次。6.2 虚拟机启动后一直黑屏启动黑屏的原因分三类。第一类是ISO权限问题QEMU进程在启动时无法读取安装光盘但不报错界面就一直停在无响应的状态。确保ISO文件路径允许libvirt用户访问chmod 644至少要有。第二类是VNC监听地址问题。VNC默认监听127.0.0.1你在远程访问时当然连不上。启动参数里加--graphics vnc,listen0.0.0.0如果需要连外网还要在防火墙放行对应端口。但作为调试手段更推荐在宿主机上用SSH隧道方式访问VNC而不是直接暴露VNC端口。第三类是显卡模型问题。有些系统的安装程序跟默认的-vga cirrus适配不好换成-vga virtio或-vga qxl可以解决。修改虚拟机XML里video节点即可。6.3 网络通但虚拟机无法访问外网默认NAT网络下虚拟机可以访问局域网但外网出不去多半是宿主机的防火墙规则或IP转发没开。检查sysctl net.ipv4.ip_forward返回0说明转发关闭开启它sudo sysctl -w net.ipv4.ip_forward1如果是SELinux或者nftables规则拦了转发流量还得看FirewallD或者iptables的FORWARD链。有些发行版默认不允许libvirt的转发流量需要专门放行sudo firewall-cmd --permanent --zonelibvirt --change-interfacevirbr0 sudo firewall-cmd --reload这步做完虚拟机里应该能ping通外网了。如果还是不通检查虚拟机内部默认路由和DNS。在NAT网络里DNS设置错误也是个高频原因。6.4 嵌套虚拟化、快照和更进一步的扩展嵌套虚拟化对于测试KVM本身很有用。加载模块前在/etc/modprobe.d/kvm.conf写入参数Intel平台options kvm_intel nested1之后通过cat /sys/module/kvm_intel/parameters/nested确认输出为Y虚拟机里就能再开KVM虚拟机。快照是qcow2格式最实用的功能之一。创建快照回滚到指定时间点都非常方便virsh snapshot-create-as mytest-vm snap01 first snapshot virsh snapshot-list mytest-vm virsh snapshot-revert mytest-vm snap01注意运行状态下的虚拟机快照会包含内存状态磁盘占用会变大不加--memory参数的话只保存磁盘状态。两种做法各有适用点测试环境随便用生产环境得先把快照的存储策略理清楚。如果环境规模变大还可以考虑用virsh migrate做在线迁移或者引入oVirt/OpenStack这种更重的管理平台。但那是另一个层级的故事了单机实战阶段把本文的这些技能点练扎实应对日常开发测试已经完全足够。最后聊一点个人体会。我在搭这套环境时栽过最大的跟头不是命令写错而是对性能配置的理解偏差。最开始图省事所有虚拟机都走默认参数结果跑数据库压测时I/O和网络双双成为瓶颈数据惨不忍睹。后来老老实实把virtio、cache策略、CPU pinning、大页一个个调过来性能才真正接近物理机水平。所有配置改动之前我都习惯先用virsh dumpxml导出当前XML留一份备份改坏了能立刻还原。这个习惯帮我挽回了无数次误操作也让我胆敢在生产预演环境里尝试各种参数组合。如果你想在一台机器上快速验证配置我最推荐的做法是先建两台最小化Linux虚拟机一台默认配置一台按性能调优配置逐项调整跑相同的负载对比。看到差距之后你对QEMUKVM的理解会上升一个台阶。虚拟机搭建只是起点真正的乐趣在于把它调成一个又快又稳又可控的小型基础设施。
阅读完成 · 觉得有帮助?