相信不少朋友都有过这样的经历:工作或学习里需要一个Linux环境,但手头只有一台Windows电脑,要么装双系统来回重启折腾,要么用VirtualBox或VMware开虚拟机,总觉得性能差点意思、操作也慢半拍。我个人的选择是QEMU+KVM这套组合,尤其是宿主系统本身就是Linux的时候,这套方案在性能和灵活性上几乎是碾压级的。
这篇内容我打算把QEMU+KVM从环境准备、虚拟机创建到性能调优的全过程完整梳理一遍,目标是让一个接触虚拟化不深的人,也能在5分钟内用命令行或图形化工具搭出一台Ubuntu 24.04虚拟机。你不需要是内核专家,只要按照步骤走,就能复现整个流程,并且通过优化让虚拟机的CPU、磁盘、网络性能都尽量贴近物理机。文章里还会专门整理我实际踩过的坑、排查过的常见问题,希望能帮你省下不少弯路。
1. 为什么我最终选了QEMU+KVM
1.1 QEMU和KVM到底是怎么配合的
刚开始接触这套方案的人,很容易被QEMU和KVM这两个词搞晕。简单说,KVM是Linux内核里自带的虚拟化模块,它把CPU的硬件虚拟化能力(Intel的VT-x或AMD的SVM)直接暴露给用户态程序,让虚拟机里的指令可以“近乎原生”地执行。但它本身只负责CPU虚拟化和内存虚拟化,对外设、磁盘、网络的模拟并不直接管。
QEMU则是一个功能强大的模拟器,它可以纯软件方式模拟出整台机器,包括CPU、内存、硬盘、网卡等。当QEMU和KVM配合时,QEMU就不再走纯软件模拟路线,而是调用KVM提供的加速接口,把CPU密集型任务交给硬件虚拟化处理,这样虚拟机跑起来的速度非常接近物理机。我用一个不太严谨但好理解的比喻:KVM像是给CPU开了一条高速专用车道,QEMU则负责把“整台电脑”的其他零件搭出来。
在实际部署中,我们通常还会引入libvirt这个管理中间层。libvirt是一套API和守护进程(libvirtd),它统一管理QEMU/KVM、LXC等虚拟化后端,提供了virsh命令行、virt-manager图形界面等工具。用libvirt的好处在于,创建、删除、迁移虚拟机都可以通过统一语法完成,后续写自动化脚本也方便得多。
1.2 和VirtualBox、VMware比,优势在哪里
如果宿主系统是Windows,VMware Workstation和VirtualBox确实是顺手的选择,它们安装简单、图形界面友好,新手也能很快上手。但当宿主系统是Linux、尤其是服务器或开发机时,QEMU+KVM的优势就很明显了。
第一个是性能。KVM是内核模块,虚拟化开销极低,CPU、内存、磁盘I/O、网络的性能损耗都远小于用户态模拟方案。对于数据库、编译等重负载场景,KVM的表现基本可以达到物理机的九成以上,而传统的纯软件虚拟化方案往往只有六七成。
第二个是资源管理能力。libvirt支持精细的CPU配额、内存热插拔、磁盘限速、网络带宽控制等能力,比如你想限制某台虚拟机最多使用4个vCPU、读写IOPS上限为10000,用virsh或者virt-manager就能直接配置,这在虚拟化场景里非常实用。
第三个是可扩展性和自动化能力。QEMU+KVM支持云镜像直接启动、快照、离线迁移、嵌套虚拟化,还能和OpenStack这类云平台无缝集成。就算你只是个人用,命令行创建虚拟机、批量克隆镜像这些操作也比点鼠标高效得多。
当然,QEMU+KVM的学习曲线确实比VMware要陡一些,很多概念(桥接网络、virtio驱动、NUMA绑定等)都需要理解。但只要把基本概念弄清楚,后面你会发现这套方案非常“稳”且“快”,值得投入时间。
1.3 这台虚拟机适合谁来用
QEMU+KVM不是只给服务器管理员用的。我身边有不少嵌入式开发、后端开发、测试同学都在用这套方案。如果你属于下面几类人,我觉得这篇文章完全适合你:
- 日常需要在Linux下开发、编译,又不想频繁切换物理机系统的开发者;
- 需要快速创建多台隔离环境跑测试、做实验的测试工程师;
- 对虚拟化性能有较高要求,不想用VirtualBox忍受磁盘I/O慢、网络吞吐低的人;
- 想在自己的Linux笔记本或服务器上搭建一套类似云主机体验的虚拟化环境的人。
这篇教程的操作环境是Ubuntu 24.04宿主系统,但我介绍的命令和思路在Debian、CentOS Stream等主流发行版上同样适用,只是包管理命令和软件源略有差异。
2. 环境准备与组件安装
2.1 确认CPU虚拟化特性已经开启
动手安装之前,先确认你的CPU支持并开启了硬件虚拟化。在Linux终端里执行:
grep -E '(vmx|svm)' /proc/cpuinfo如果你看到了vmx(Intel)或svm(AMD),说明CPU支持虚拟化。但支持并不代表已经启用,很多时候需要在BIOS/UEFI里打开VT-x或SVM功能。如果grep结果为空,大概率是BIOS里没开,你需要重启进固件设置界面找一下“Intel Virtualization Technology”或“SVM Mode”选项。
还有一个更直观的方法,直接看KVM模块是否加载:
lsmod | grep kvm正常情况下能看到kvm和kvm_intel(或kvm_amd)两个模块。如果没有输出,可以手动加载:
sudo modprobe kvm sudo modprobe kvm_intel这条命令演示用的是Intel平台,如果是AMD平台就把最后的模块名换成kvm_amd。加载之后再用lsmod确认一下即可。
2.2 安装QEMU/KVM相关组件
Ubuntu 24.04的软件源里已经准备好了所有需要的包,一条命令就能装齐:
sudo apt update sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager这里简单解释一下每个包的作用:
qemu-kvm:提供QEMU系统模拟器,是虚拟机的核心执行引擎;libvirt-daemon-system:libvirt守护进程及其系统启动配置;libvirt-clients:提供virsh等命令行管理工具;bridge-utils:用于创建和管理Linux网桥的工具;virtinst:提供virt-install命令行工具,用于快速创建虚拟机;virt-manager:图形化管理界面的包名,如果你更习惯命令行操作,不装它也行,但新手建议装上,后面创建虚拟机会方便很多。
安装完成后,启用并启动libvirtd服务:
sudo systemctl enable --now libvirtd检查服务是否正常运行:
sudo systemctl status libvirtd看到active (running)就说明libvirtd已经跑起来了。这时候再执行:
virsh list --all如果输出为空列表且没有报错,说明libvirt可以正常连接QEMU驱动。
2.3 用户组与权限配置
默认情况下,只有root和libvirt用户组的成员才能管理虚拟机。为了避免每次操作都用sudo,把自己的用户加入相关组:
sudo usermod -aG kvm,libvirt $USER注意,修改用户组后需要重新登录会话(或者注销再登录)才能生效。我经常看到有人执行完这条命令后直接运行virsh list还是提示权限不足,其实就是因为没重新登录,组权限还没刷新。
重新登录后执行id,如果输出里出现了kvm和libvirt,说明权限已经加上。
到这里,环境已经准备好了。可以下载Ubuntu 24.04的ISO镜像文件备用,我用的是desktop版镜像做演示,服务器版操作流程几乎一样。下载好镜像后,建议把它放到一个统一目录,比如~/iso/,方便后面指定路径。
3. 5分钟搭好Ubuntu 24.04虚拟机的实操路径
3.1 图形化方案:virt-manager一条龙
如果你对命令行还不熟悉,最直观的方式就是用virt-manager。启动方式很简单:
virt-manager如果是第一次运行,它会提示连接QEMU/KVM驱动,默认地址是qemu:///system,直接点“连接”即可。连接成功后,界面左侧会显示宿主机信息,右侧显示现有的虚拟机列表(目前是空的)。
创建虚拟机的步骤很简单:
- 点击工具栏上的“新建虚拟机”按钮(或者菜单栏选择File > New VM);
- 选择安装方式:这里选“本地安装介质(ISO映像或光盘)”,然后点击“前进”;
- 点击“浏览”,选择你下载好的Ubuntu 24.04 ISO文件,点击“前进”;
- 设置内存和CPU数量。个人建议至少分配4GB内存和4个vCPU,如果宿主机配置较紧张,2GB内存+2个vCPU也能跑基础操作,但编译类任务会很吃力;
- 启用存储:选择“创建磁盘映像”,给虚拟机创建一块虚拟磁盘,大小建议40GB起步,格式默认是qcow2(这个格式支持动态增长和快照);
- 点击“完成”之前,可以在最后一步勾选“安装前自定义配置”,这样能提前设置网卡模型、显示协议等参数。如果不想折腾,直接完成即可。
之后virt-manager会启动虚拟机并从ISO引导,后续的Ubuntu安装流程就跟物理机装系统一样了。图形化方案的优势是直观,每一步都有提示,新手照着点就能完成,整个过程确实用不了5分钟。
3.2 命令行方案:virt-install快速部署
如果你更倾向于命令行操作,或者需要在脚本里批量创建虚拟机,那就绕不开virt-install。首次创建虚拟机,我最常用的命令模板是这样的:
sudo virt-install \ --name ubuntu2404 \ --memory 4096 \ --vcpus 4 \ --disk path=/var/lib/libvirt/images/ubuntu2404.qcow2,size=40,format=qcow2 \ --cdrom /home/你的用户名/iso/ubuntu-24.04-desktop-amd64.iso \ --os-variant ubuntu24.04 \ --network network=default,model=virtio \ --graphics spice \ --video qxl逐个说明参数的含义:
--name:虚拟机名称,后续用virsh管理的时候会用到这个标识;--memory:内存大小,单位是MB,4096就是4GB;--vcpus:vCPU数量;--disk:指定磁盘路径、大小和格式,如果没有该文件,virt-install会自动创建;--cdrom:指定ISO安装镜像路径;--os-variant:告诉libvirt针对该操作系统做优化,可以用osinfo-query os查看支持的列表;--network:网络配置。network=default表示使用libvirt自带的NAT网络,model=virtio是网卡模型;--graphics spice:启用SPICE显示协议,配合--video qxl提供比较流畅的图形界面体验。
执行命令后,virt-install会创建虚拟机并打开图形窗口(如果通过SSH远程操作,需要配合VNC或SPICE客户端)。如果你使用的是无桌面版Ubuntu服务器,可以把--graphics改为--graphics none,改用串口控制台安装:
sudo virt-install \ --name ubuntu2404 \ --memory 2048 \ --vcpus 2 \ --disk path=/var/lib/libvirt/images/ubuntu2404.qcow2,size=20,format=qcow2 \ --cdrom /home/你的用户名/iso/ubuntu-24.04-server-amd64.iso \ --os-variant ubuntu24.04 \ --network network=default,model=virtio \ --graphics none \ --extra-args='console=ttyS0'--extra-args里的console=ttyS0会把内核日志和安装界面输出到串口,这样你可以在终端里通过virsh console直接控制安装过程。这种无界面安装模式,在远程服务器上特别实用。
安装系统本身的过程和物理机一样,选择语言、配置键盘、设置用户名密码、选择安装位置即可。安装完成后重启虚拟机,记得在virt-manager里把ISO光盘从“光驱”中移除,或者调整启动顺序为硬盘优先,否则每次启动都会重新进入安装界面。
3.3 安装Ubuntu 24.04的几个关键配置项
Ubuntu 24.04的安装器做得比较友好,大部分选项默认即可。但有三个地方值得特别注意:
磁盘分区建议选择“使用整个磁盘”装LVM。LVM的好处是后续扩容方便,不用重建分区。如果你打算做快照测试,qcow2格式本身就支持内置快照,LVM不是必须,但可以给内部文件系统层多加一层灵活性。
创建用户账户时,建议勾选“开机自动登录”或设置一个简单密码,开发测试机上图方便省去每次输密码的麻烦。生产环境还是老实设个强密码。
软件源配置建议换成国内镜像源。虽然安装阶段系统自带的源也能用,但下载软件包速度可能不理想。装完系统后可以手动修改/etc/apt/sources.list.d/ubuntu.sources,把其中archive.ubuntu.com和security.ubuntu.com替换成你所在地区访问速度较快的镜像地址,再执行:
sudo apt update && sudo apt upgrade -y这样能让系统更新和应用安装速度快很多。
4. 性能优化技巧:让虚拟机性能贴近物理机
4.1 CPU和内存优化:分配只是一部分,更关键的是调度策略
给虚拟机分配多核CPU并不难,难的是让这些vCPU真正跑在合适的物理核心上。默认情况下,libvirt会由内核调度器动态安排vCPU,这在负载不高时问题不大,但在高负载场景下,CPU缓存命中率下降、NUMA跨节点访问内存等问题会导致性能明显缩水。
如果服务器是单路或多路CPU,先查看宿主机的NUMA拓扑:
numactl --hardware输出里会显示每个NUMA节点的CPU核心范围和内存大小。创建虚拟机时,尽量把vCPU和内存都分配在同一个NUMA节点上,避免虚拟机跨节点访问内存。在XML配置里可以通过<numatune>和<vcpuplacement>来设置。
更进一步,可以使用CPU pinning(vCPU绑定)把虚拟机的每个vCPU固定到特定的物理核心上。编辑虚拟机配置:
virsh vcpupin ubuntu2404 0 2 virsh vcpupin ubuntu2404 1 3这条命令的含义是把虚拟机的第0个vCPU绑定到物理CPU的第2个核心,第1个vCPU绑定到第3个核心。具体绑到哪个核心,要依据宿主机的拓扑和空闲情况来定。绑定的好处是减少了vCPU在不同物理核心间迁移的开销,让CPU缓存和TLB命中率明显提升。不过要注意别把多个虚拟机绑到同一批核心上,否则会互相争抢。
内存方面,建议启用KSM(Kernel Samepage Merging)来合并相同的内存页,尤其当你同时运行多台相同操作系统的虚拟机时,共享内存页能显著降低内存占用。启用方式:
sudo sh -c 'echo 1 > /sys/kernel/mm/ksm/run'临时生效,重启后失效。如果想永久开启,可以写一个systemd服务或把配置写入/etc/sysctl.conf。不过KSM有CPU开销,如果宿主机内存足够,不一定非要开,可以用virsh setmem配合内存热插拔按需调整大小。
4.2 磁盘I/O优化:virtio驱动和缓存策略是重点
磁盘I/O是虚拟机性能最直观的短板,优化前后差别非常大。首先要确保虚拟机的磁盘总线类型是virtio,而不是默认的IDE或SATA。virtio是一种半虚拟化I/O方案,虚拟机里的驱动会绕过设备模拟层,直接与宿主机通信,吞吐量和延迟都远好于模拟设备。
在创建虚拟机时,--disk参数里可以指定bus=virtio:
--disk path=/var/lib/libvirt/images/ubuntu2404.qcow2,size=40,format=qcow2,bus=virtio如果已经创建好了,在virt-manager里打开虚拟机配置,把磁盘总线改为VirtIO后重启即可。Ubuntu 24.04的内核自带virtio驱动,改完一般直接就能启动。
其次是缓存策略。在virt-manager的磁盘配置里,有一个“缓存模式”选项,默认是None(直写)。对于大多数应用场景,我推荐writeback(回写),它先把写入数据缓存到宿主机内存里,再异步刷入磁盘,写入性能提升非常明显。代价是宿主机突然断电时,缓存中的数据可能会丢失,所以生产环境要权衡一下。如果追求更高的数据安全性,可以使用directsync或None。命令行方式修改:
virsh edit ubuntu2404找到对应的<disk>段,修改<driver>标签里的cache属性为writeback。
如果你的虚拟磁盘是qcow2格式,还可以看看discard(TRIM)是否开启,它能让虚拟机删除文件时及时释放宿主机的磁盘空间。在XML配置里给<driver>加上discard='unmap'即可:
<driver name='qemu' type='qcow2' cache='writeback' discard='unmap'/>最后,QEMU 8.0以上版本默认支持io_uring作为磁盘I/O引擎,相比传统的threads和native模式,io_uring在并发I/O场景下延迟更低、吞吐更大。确认QEMU版本:
qemu-system-x86_64 --version如果版本较新,可以在<driver>标签里添加:
<driver name='qemu' type='qcow2' cache='writeback' io='io_uring'/>实测下来,调好virtio总线、writeback缓存、io_uring引擎之后,虚拟机里做编译、数据库读写,速度和物理机差距已经非常小。
4.3 网络优化:多用virtio-net和vhost-net
虚拟机网络性能的默认配置通常比较保守,但对于需要高吞吐的应用,优化空间很大。
首先是网卡模型。创建虚拟机时如果没指定,很多工具默认会用e1000这种模拟网卡,兼容性好但性能一般。改成virtio-net后,网络包处理效率会显著提升:
--network network=default,model=virtio如果你已经建好了虚拟机,用virt-manager打开网络配置,把设备模型改成virtio即可。Ubuntu 24.04系统安装时一般会自动加载virtio_net内核模块,所以不用担心驱动问题。
其次是启用vhost-net。vhost-net是内核态的网络后端,它允许虚拟机数据包处理直接通过内核模块完成,绕过了QEMU用户态进程的拷贝,延迟会低不少。大多数发行版默认已经加载了该模块,确认一下:
lsmod | grep vhost如果有vhost和vhost_net输出,说明已经启用。默认创建的虚拟机如果网卡模型是virtio,基本上自动走vhost_net路径。
另外,如果你需要大量的网络吞吐,可以考虑给virtio-net开启多队列。多队列允许虚拟机使用多个CPU核心并行处理网络包,在转发或高并发场景下提升明显。创建虚拟机前,可以用virsh edit给<interface>添加队列配置,或者在virt-install命令中加参数。但需要注意,多队列需要虚拟机内部正确配置多个队列的中断和RSS,对新手有一定门槛,普通开发场景默认单队列完全够用。
关于网络模式,libvirt默认的NAT模式能让虚拟机上网,但如果你需要让外部设备直接访问虚拟机(比如部署一个Web服务),建议使用桥接模式。宿主机上创建网桥:
sudo virsh net-list sudo virsh net-info default默认网络是一个NAT网络,虚拟机在NAT后面的私有网段里,外部无法直接访问。想桥接,可以在virt-manager的虚拟网络设置里新建一个桥接网卡,或者手动编辑网络XML。桥接模式下,虚拟机会直接出现在局域网的网段里,拥有独立IP,其他设备可以直接访问它。不过桥接需要物理网卡支持(一般没问题),还要注意公司或校园网可能限制同一物理口下多IP的情况。
4.4 容易被忽略的细节优化:设备精简、快照和资源限制
很多细节其实对体验影响很大,但官方文档很少专门强调。我把自己常年积累的几个“小动作”分享给大家。
第一个是移除不需要的模拟设备。默认创建的虚拟机会带上USB控制器、软驱、声卡等一堆设备,如果只是做服务器或开发环境,这些用不上还白白消耗资源。在virt-manager里打开硬件信息列表,把不需要的设备(比如SoundCard、USB Redirection)移除,XML配置文件也会相应变短。
第二个是合理设置内存气球。libvirt默认会启用内存气球(virtio-balloon),它允许宿主机动态回收虚拟机空闲内存。但如果你在虚拟机内跑数据库或Java应用,JVM或数据库引擎往往会占用大量内存,气球动态缩小时可能导致虚拟机内部性能波动。对于性能敏感的应用,建议在XML里把<memballoon>的model改为none,让虚拟机内存分配固定下来。
第三个是做好镜像快照。qcow2格式天生支持快照,可以在执行危险操作前快速打一个快照:
virsh snapshot-create-as ubuntu2404 baseline "clean system with docker installed"回滚时:
virsh snapshot-revert ubuntu2404 baseline快照操作很快,比复制整个镜像文件方便太多了。推荐每个重要节点都打一个快照,容错率会高很多。
第四个是给虚拟机设置资源上限。在XML里,<cputune>可以设置shares(CPU权重)、period和quota(CPU配额);<blkiotune>可以设置磁盘权重。比如限制虚拟机最多只能使用4个vCPU的80%时间片:
<cputune> <shares>2048</shares> <period>100000</period> <quota>80000</quota> </cputune>对于服务器上跑多台虚拟机、想公平分配资源的场景,这种配置特别有用。
5. 常见问题与排查实录
5.1 权限报错和模块加载问题
症状是执行virsh list --all时提示failed to connect to the hypervisor或Permission denied。
排查思路是先确认libvirtd服务在跑:
systemctl status libvirtd如果服务正常,再看自己的用户是否在libvirt组里:
groups不在组里就重新执行前面的usermod并重新登录。还有一种情况是SELinux或AppArmor拦了权限路径。Ubuntu默认用AppArmor,偶尔会遇到虚拟机无法访问某些目录下的ISO文件或磁盘镜像。简单处理方式是检查/var/log/audit/audit.log或dmesg输出,看有没有被AppArmor拒绝的记录。如果是,可以把对应的虚拟机配置文件加入AppArmor白名单,或者把镜像放到默认的/var/lib/libvirt/images/目录下。
5.2 性能达不到预期怎么排查
虚拟机内部跑分和物理机差距很大时,先别急着调参,按下面顺序排查:
第一,确认虚拟机里是否用了virtio驱动的设备。执行:
ls -l /sys/class/net/如果网卡名称是eth0之类的模拟设备名,而不是ens3这类virtio设备名,说明可能还在用模拟网卡。同理检查磁盘设备名是否是vd*。
第二,检查CPU是否被限制或超卖严重。在宿主机上查看:
virsh vcpuinfo ubuntu2404如果某个vCPU的“cpu time”一直很高但“affinity”绑定导致部分核空转,可以调整绑定策略。如果宿主机上还有其他重型虚拟机在跑,资源争抢也会导致性能下降。
第三,验证是不是磁盘I/O瓶颈。在虚拟机里跑一个简单的随机读写测试:
dd if=/dev/zero of=/tmp/test bs=1M count=1024 oflag=direct对比在宿主机上同样操作。如果宿主机瞬间完成而虚拟机卡很久,重点检查磁盘缓存模式是否设置成了None、是否用了IDE总线。
5.3 网络不通和SSH连接失败
新创建的虚拟机能正常上网,但宿主机SSH连不进去,这种问题我遇到太多次了。通常原因是虚拟机内部没有开启SSH服务,或者防火墙拦截了22端口。Ubuntu 24.04默认可能只安装了openssh-server,但没启动或没放行端口?其实新版Ubuntu安装时只要选过“Install OpenSSH server”,一般会自动启。没选过的话手动操作:
sudo apt install openssh-server sudo systemctl enable --now ssh如果是外部设备想访问虚拟机的服务,记得确认虚拟机IP是否是局域网可路由地址(桥接模式),而不是NAT模式下的私有地址。NAT模式下外部设备想访问虚拟机,需要在宿主机上设置端口转发,比较麻烦,建议直接用桥接。
还有一个常见情况是虚拟机内使用了和宿主相同网段的静态IP,导致ARP冲突。在虚拟网络配置中最好用DHCP,或者手动指定一个不冲突的IP地址。
5.4 图形界面卡顿和分辨率不对
用virt-manager打开虚拟机图形界面,感觉鼠标飘、画面不流畅,这通常是显示协议和驱动没配对造成的。推荐SPICE协议搭配QXL显示驱动,两者的组合在Linux guest里表现很好。在虚拟机的硬件信息里查看显示项,如果当前是VGA,建议改成QXL,并把显示协议从VNC改成SPICE。
如果分辨率最高只有800x600,多半是虚拟机内部没有安装增强驱动(SPICE guest tools)。Ubuntu 24.04安装包默认不一定带,需要手动安装:
sudo apt install spice-vdagent安装后重启虚拟机,分辨率就能自由调整,剪贴板共享、文件拖拽等功能也一并可用了。
写到最后的一点体会
从第一次在命令行里敲virt-install到现在,我用QEMU+KVM跑过嵌入式交叉编译环境、Java服务集群、数据库测试实例,也踩过不少性能坑、网络坑,但整体上这套组合确实“越用越顺手”。每次新建虚拟机,我最深的感受是:优化不是一步到位的事,而是根据你的实际负载慢慢调出来的。先把virtio驱动和缓存策略做好,就能覆盖大多数场景的性能需求;再根据频繁出现的瓶颈去调整CPU绑定、网络队列和资源配额,一步一步逼近物理机体验。
最后再分享一个小技巧:给虚拟机配好了常用环境之后,别急着关机,先打个快照。后面不管怎么折腾系统、装坏什么软件,一条命令就能恢复到干净状态。这个习惯帮我省下了大量重复“重装系统”的时间,也是我认为在虚拟化工作流里性价比最高的操作之一。