我第一次在旧服务器上搭 QEMU + KVM 环境,心里其实挺没底的。之前一直用桌面虚拟化工具,觉得虚拟机就是“开个窗口装系统”那么简单。直到某天需要在没有显示器的 Linux 主机上批量起虚拟机,才发现传统方案那套“图形界面点一点”的思路根本走不通。真正把 QEMU 和 KVM 组合起来跑通之后,我才意识到两者的分工、内核模块、网络拓扑、存储后端里藏着大量平时看不到的细节。
这篇内容就是围绕“QEMU + KVM 环境搭建”整理的完整记录,覆盖从选型判断、硬件检查、软件安装、创建虚拟机,到网络桥接、性能调优和常见故障排查。适合想在 Linux 服务器上做隔离环境、私有测试平台、开发模拟,或者想从桌面虚拟化迁移到轻量级虚拟化的朋友。我会尽量说清楚每个步骤背后的原因,而不是丢一堆命令让你照抄。
1. 选型判断:什么时候该用 QEMU+KVM,而不是 VirtualBox 或纯 QEMU
1.1 三种虚拟化方案的底层逻辑
很多人一开始就搞混这几样东西。QEMU 是一个模拟器,它能模拟 CPU、内存、磁盘、网卡、显卡等整套硬件,甚至可以模拟出和宿主 CPU 完全不同的架构,比如在 x86 机器上模拟 ARM 开发板。但纯 QEMU 模拟每条指令都要经过翻译层,性能损失非常大,跑个系统勉强能用,跑重负载基本没法看。
KVM 则是 Linux 内核里的虚拟化模块,它利用 CPU 自带的硬件虚拟化扩展(Intel VT-x 或 AMD-V),让虚拟机里的特权指令可以直接交给物理 CPU 执行。KVM 本身只负责 CPU 和内存的虚拟化,它不管你怎么模拟磁盘、网卡这些设备。所以实际落地时,通常用 QEMU 充当设备模拟层,KVM 充当加速引擎,两者组合起来才是我们常说的“QEMU + KVM”方案。
VirtualBox 走的也是硬件加速路线,它的优势是跨平台、图形界面友好、开箱即用。但它撇开了 Linux 内核的 virtio 半虚拟化体系,内核模块和生态相对封闭,在纯服务器场景下性能和自动化能力都差一些。而且 VirtualBox 对无头服务器(headless server)的支持很别扭,远程管理主要靠独立协议,和 libvirt 这套标准 API 对接起来不顺畅。
1.2 根据应用场景做取舍
我给不同场景的建议是:
| 使用场景 | 推荐方案 | 原因 |
|---|---|---|
| 纯桌面学习、临时跑个系统 | VirtualBox 或 QEMU 图形模式 | 上手简单,不折腾内核 |
| Linux 服务器上跑隔离环境、测试集群 | QEMU + KVM + libvirt | 性能接近原生,支持命令行和自动化 |
| 跨架构模拟,比如在 x86 上模拟 ARM/路由器 | 纯 QEMU(TCG 模式) | KVM 只能支持宿主相同架构,跨架构必须靠软件模拟 |
| 生产环境、私有云底层 | QEMU + KVM + libvirt + 存储/网络方案 | 这是很多开源云平台的标准虚拟化栈 |
我实际项目里最常用的是第三种组合。比如在一台只有一个 IP 的服务器上,用 QEMU + KVM 同时跑几个相互隔离的测试环境,靠桥接网络让每台虚拟机获得独立局域网地址,再用 virt-manager 远程管理图形界面。整套东西跑在 Linux 上,稳定性和性能都远比单纯模拟器要好。
如果你只是想在本机开个 Linux 虚拟机学习命令,那 QEMU + KVM 的安装成本会显得有点高。但如果你需要在一台长期开机的服务器上建虚拟化平台,那这套环境就是绕不开的底座。下面这些内容才是真正有复现价值的硬件准备和实操步骤。
2. 硬件与内核的“入场券”:CPU 虚拟化扩展检查清单
2.1 CPU 虚拟化扩展检定与 BIOS 设置
KVM 能否正常工作,第一步就看物理 CPU 支不支持硬件虚拟化扩展。这一步如果不过关,后面装再多的软件包都白搭。检查方法很简单:
grep -E -c '(vmx|svm)' /proc/cpuinfo如果输出大于 0,说明 CPU 支持对应的虚拟化扩展。Intel 处理器对应的旗标是vmx,AMD 处理器对应的是svm。也可以用更易读的方式查看:
lscpu | grep -i virtualization我遇到过不少情况,命令输出明明是 0,但 CPU 型号明明支持虚拟化。这时候十有八九是 BIOS 里的 “Virtualization Technology” 或 “SVM Mode” 选项被关了。不同主板叫法不一样,有的叫 “Intel Virtualization Technology”,有的叫 “Virtualization Extensions”,需要进 BIOS 找到并把 Enabled。如果你打算做 PCI 设备直通,还需要确认 “VT-d” 或 “IOMMU” 相关选项也处于开启状态。
这个检查必须放在装机前。我在第一次搭建时忽略了 BIOS 设置,结果系统装好、软件包全部装完,才发现/dev/kvm一直不存在。排查了一圈,最终的修复只是进 BIOS 打开一个开关并重启。这种低级错误完全可以在开始前用两分钟避免。
2.2 内核模块、宿主系统版本与 IOMMU 确认
进入系统后,要确认 KVM 内核模块是否已经加载:
lsmod | grep kvm如果没有任何输出,可以手动加载。Intel 平台加载kvm_intel,AMD 平台加载kvm_amd:
sudo modprobe kvm_intel # 或者 sudo modprobe kvm_amd加载成功后再确认设备节点:
ls -l /dev/kvm如果能看到/dev/kvm,说明底层加速已经可用。没有这个节点时,libvirt 会直接报 “KVM is not available” 或 “unsupported configuration”,虚拟机要么无法创建,要么退化成纯软件模拟,速度慢到怀疑人生。
宿主系统的 Linux 内核版本也很关键。旧内核虽然也能跑 KVM,但很多新特性比如嵌套虚拟化、更多 vCPU 支持、VirtIO 1.1 以上设备都依赖新内核。我建议优先使用 5.4 以上的长期支持版内核,发行版只要不是太老,基本都能满足。对 Ubuntu/Debian 系,可用sudo apt install linux-generic更新内核;对 RHEL/CentOS 系,可用dnf update kernel。
另外,如果你有 PCI 直通需求,需要在内核启动参数里开启 IOMMU。编辑/etc/default/grub,在GRUB_CMDLINE_LINUX里加上:
intel_iommu=on iommu=ptIntel 平台用intel_iommu=on,AMD 平台用amd_iommu=on。改完后执行sudo update-grub或grub2-mkconfig并重启。这一步不做的话,后面直通网卡或显卡时,设备会一直卡在 “not managed” 状态。
硬件检查这一关,本质上是“预检”而不是“排错”。它能帮你把环境搭建分成两层:第一层是 CPU 与内核之间的虚拟化通道,第二层才是 QEMU 和 libvirt 的用户态工具。通道没打通,上层工具再花哨也没用。
3. 从安装到跑通 libvirt:QEMU/KVM 的底座配置
3.1 Ubuntu/Debian 系安装流程
Ubuntu 上安装 QEMU + KVM 全家桶,一个命令就能搞定:
sudo apt update sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager这些包各有分工:
qemu-kvm:QEMU 的 KVM 加速版本,负责虚拟机设备模拟。libvirt-daemon-system:libvirt 后端守护进程,统一管理虚拟机生命周期。libvirt-clients:提供virsh、virt-install等命令行工具。bridge-utils:提供桥接网络所需的brctl工具。virt-manager:图形化管理工具,虽然服务器可能没桌面,但远程连过来管理非常方便。virtinst:提供virt-install这条创建虚拟机的核心命令。
Debian 上有些包名略有差异,比如可能会用qemu-system-x86而不是qemu-kvm。安装完直接执行:
systemctl status libvirtd如果服务没有自动起来,就手动启动并设置开机自启:
sudo systemctl enable --now libvirtd3.2 RHEL/CentOS 系安装流程
基于红帽的发行版可以用 dnf 安装对应组件:
sudo dnf install -y qemu-kvm libvirt virt-install bridge-utils sudo systemctl enable --now libvirtd红帽系的包名和 Ubuntu 略有区别,但 libvirt 的体系结构是一样的。安装完成后可用下面命令确认:
virsh list --all virsh versionvirsh version输出里能看到两部分信息:一部分是 libvirt 的版本,一部分是 QEMU 的版本。只要这里能正常输出而不会报 socket 权限错误,说明 libvirt 核心服务已经开始工作。
3.3 让当前用户拥有管理权限
安装只是第一步。大多数情况下,普通用户执行virsh list --all会报:
failed to connect to the hypervisor原因很简单:libvirt 的/var/run/libvirt/libvirt-sock只允许 root 和libvirt用户组访问。解决方法是把你的登录用户加入libvirt组:
sudo usermod -aG libvirt $USER改完组之后一定要重新登录,或者重启一下会话,否则组权限不会立即生效。我第一次安装时就是加完组没重新登录,反复折腾了十几分钟才意识到是会话缓存的问题。
更进阶的情况下,如果你需要允许远程调用 libvirt 接口,还需要改/etc/libvirt/libvirtd.conf,把unix_sock_group和unix_sock_rw_perms这两项的注释取消并改成合适权限。但对于单机使用,加入libvirt组已经是唯一正确的做法。
还有一点容易被忽略:qemu:///system和qemu:///session是两套完全独立的实例。普通用户默认走 session,权限隔离更强但看不到 root 创建的虚拟机。实际管理大多数使用qemu:///system,这也是 virt-manager 默认连接的地址。如果你发现某台虚拟机“明明存在但看不到”,先检查是不是连接到了错误的 URI。
4. 创建第一台虚拟机:ISO 安装与云镜像两种路径
4.1 使用 virt-install 安装 ISO
libvirt 体系里创建虚拟机最主流的命令行工具是virt-install。我一般在有图形环境的主机上用 virt-manager 点点点,但到了纯服务器环境,命令行反而更顺手、更可复制。
安装 ISO 镜像的基本命令长这样:
sudo virt-install \ --name vm1 \ --memory 2048 \ --vcpus 2 \ --disk path=/var/lib/libvirt/images/vm1.qcow2,size=20,format=qcow2,bus=virtio \ --cdrom /opt/iso/ubuntu-server.iso \ --os-variant ubuntu22.04 \ --network network=default \ --graphics vnc \ --boot cdrom,hd逐项解释一下:
--name:虚拟机名字,后续 virsh 操作都要靠它。--memory:内存大小,单位是 MiB。--vcpus:虚拟 CPU核数,可以是整数,也可以用maxvcpus等参数做热插拔预留。--disk:定义磁盘。这里的path指定磁盘镜像文件路径,size=20表示创建 20G 大小的镜像文件,format=qcow2指定镜像格式,bus=virtio让磁盘总线走半虚拟化。--cdrom:指定安装介质。--os-variant:让 libvirt 根据系统类型自动匹配最优的设备配置和 ACPI 特性。你可以先执行osinfo-query os查看支持的列表。--network network=default:使用 libvirt 默认的 NAT 网络。--graphics vnc:为无桌面服务器开启 VNC 显示输出。--boot cdrom,hd:设置启动顺序,优先从光盘启动。如果不加这项,有些镜像会默认从硬盘启动,导致屏幕一直停在黑屏或引导系统里。
跑完这条命令后,多数情况下还会提示是否自动连接到控制台。在无桌面环境下,我建议加--noautoconsole让它创建完就回到命令行,然后用virsh vncdisplay vm1查询 VNC 端口,再通过 virt-manager 或 VNC 客户端远程连过去完成系统安装。
4.2 使用云镜像快速启动 VM
如果你不想从头装系统,云镜像是更快捷的方式。各大 Linux 发行版都提供精简过的 Cloud Image,这些镜像通常已经开启了 cloud-init 自动化配置。用这种镜像创建虚拟机时,不用走安装流程,启动后就能直接登录。
先创建一块基于云镜像的差异磁盘:
sudo qemu-img create -f qcow2 -b /var/lib/libvirt/images/ubuntu-cloud.qcow2 /var/lib/libvirt/images/vm2.qcow2这个命令的意思是,vm2.qcow2共享底层云镜像的数据,只有写入的数据才单独占空间,非常省磁盘。然后导入虚拟机:
sudo virt-install \ --name vm2 \ --memory 2048 \ --vcpus 2 \ --import \ --disk path=/var/lib/libvirt/images/vm2.qcow2,bus=virtio,format=qcow2 \ --network network=default \ --os-variant ubuntu22.04 \ --graphics none \ --console pty,target_type=serial这里有个关键的坑:云镜像默认的登录用户、密码和网络初始化都由 cloud-init 负责,但默认客户端可能没有预装 cloud-init,导致启动后你根本不知道用户名和密码。我一般会在创建前提前定制云镜像,或者在virt-install时通过--cloud-init user-data参数传入自定义配置。
另一个容易踩的坑是云镜像的串口控制台。很多云镜像默认只输出到串口,--graphics none --console pty,target_type=serial就是让虚拟机把系统日志输出到串口,使你能在宿主机终端直接看到启动过程。如果不加这项,虚拟机可能已经正常启动了,但你的终端上什么都看不到,以为它卡死了。
4.3 默认网络、VNC 与常用维护操作
libvirt 默认的default网络会创建一个 NAT 模式的虚拟网桥virbr0,虚拟机通过它访问外网,宿主机通过iptablesMASQUERADE 规则做地址转换。这是最简单的开箱即用网络,但不适合需要对外提供服务的场景。这个后面会专门展开。
日常管理操作基本都集中在几条 virsh 命令上。查看所有虚拟机:
virsh list --all启动、关闭、强制关闭:
virsh start vm1 virsh shutdown vm1 virsh destroy vm1销毁虚拟机并删除磁盘:
virsh undefine vm1 --remove-all-storage我最常用的一个操作是virsh edit vm1,它能直接打开虚拟机的 XML 定义文件,手动修改内存大小、CPU 模型、网卡模型等。每次改完记得让虚拟机重启才能生效。XML 文件是 libvirt 的核心,学会看它,比点一万次图形界面都管用。
5. 网络升级:NAT 默认网络、桥接与 VLAN 隔离
5.1 NAT 默认网络的工作原理
libvirt 默认网络对刚入门的人来说非常友好,创建虚拟机时只要填--network network=default,它就会帮你分配一个192.168.122.x的地址,虚拟机可以正常上网,宿主机也能通过 SSH 连进虚拟机。
但 NAT 模式有先天限制:外部机器无法主动访问虚拟机,除非你手动设置端口转发。因为我经常需要在局域网里直接访问虚拟机上的服务,比如跑一个 API 演示环境、部署一套带独立 IP 的集群,NAT 模式就非常别扭。每次都要加一条映射规则,管理起来跟维护路由表一样繁琐。
所以我后面的项目几乎都会切到桥接模式。桥接模式把虚拟机的虚拟网卡直接挂到物理网卡上,让虚拟机像一台独立主机一样出现在局域网里,可以自己获取 DHCP 地址,也可以配静态 IP。外部设备访问虚拟机和访问宿主机没有区别。
5.2 桥接配置及典型失败点
不同发行版的网络配置方式差异很大。Ubuntu 从 17.10 开始用 netplan,配置文件在/etc/netplan/。典型的桥接配置长这样:
network: version: 2 renderer: networkd ethernets: ens33: dhcp4: no bridges: br0: interfaces: [ens33] dhcp4: yes dhcp6: no parameters: forward-delay: 0 stp: false这段配置把物理网卡ens33绑定进桥接网卡br0,IP 地址由br0通过 DHCP 获取。修改完执行sudo netplan apply生效。
这里最典型的失败点有两个。第一,编辑的时候千万不要手滑把物理网卡的 IP 配置也保留着,否则会出现 DHCP 同时给物理网卡和桥接网卡分配地址的情况,整个网络立刻乱掉。第二,很多数据中心服务器没有 DHCP,一旦断开物理网卡的 IP,桥接又没配好静态地址,你就等于把自己锁在门外了。所以在远程服务器上做桥接,建议先把一个带静态 IP 的备用网络配置写好,或者确保有服务器控制台可以随时介入。
CentOS/RHEL 系如果使用 NetworkManager,桥接可以用 nmcli:
nmcli con add type bridge ifname br0 con-name br0 nmcli con add type bridge-slave ifname ens33 master br0 nmcli con up br0桥接创建完成后,创建虚拟机时指定桥接网络:
sudo virt-install \ --name vm3 \ --network bridge=br0 \ ...也可以后期给虚拟机动态添加网卡:
virsh attach-interface vm3 bridge br0 --model virtio --config --live5.3 可选:OVS 与 VLAN
如果你构建的网络规模稍大一点,同一张物理网卡上要跑多个 VLAN,或者需要做流量镜像、虚拟交换机动态策略,那么传统的 Linux bridge 就有点不够用了。这种情况下 Open vSwitch 是常见的选择。
简单的 OVS 桥接配置流程如下。安装 openvswitch 组件后创建 OVS 网桥:
sudo ovs-vsctl add-br ovsbr0 sudo ovs-vsctl add-port ovsbr0 ens33然后让 libvirt 使用这个 OVS 网桥。在虚拟机 XML 里,网络接口类型不再是network,而是direct或自定义的<interface type='bridge'>,并且加一个<virtualport type='openvswitch'/>标签。如果你需要 VLAN 隔离,就在 OVS 端口上打 tag:
sudo ovs-vsctl set port vnet0 tag=100这样每个虚拟机就可以划分到不同 VLAN,虚拟交换机层面做隔离,不必为每个网络都配独立的物理网卡。
6. 性能调优:CPU、内存、I/O 的隐藏开关
6.1 vCPU 超分与 CPU pinning 的取舍
KVM 的 vCPU 本质上对应宿主上的线程,所以允许“超分”:4 核的宿主机完全可以创建多台 2 vCPU 的虚拟机,让它们共享物理核。日常测试没有太大问题,但在负载持续很高的情况下,超分会导致性能波动,甚至出现 CPU 亲和性差、缓存命中率下降。
如果对延迟敏感,或者跑的是数据库、高频交易这类负载,可以考虑 CPU pinning,也就是把每个 vCPU 线程固定到特定物理核上:
virsh vcpupin vm1 0 0 virsh vcpupin vm1 1 1这里第一条命令的意思是,把 vm1 的第 0 个 vCPU 固定到宿主物理核 0 上;第二条把第 1 个 vCPU 固定到物理核 1 上。做完 pinning 之后,虚拟机里的线程不会在核间迁移,缓存局部性更好,性能更稳定。
QEMU/KVM 的 CPU 型号也要重点考虑。默认情况下,libvirt 会根据--os-variant生成一个经过裁剪的 CPU 模型,保证虚拟机可以在不同物理机上迁移。但如果你想尽可能发挥宿主 CPU 的性能,可以在 XML 里把 CPU mode 改成host-passthrough:
<cpu mode='host-passthrough'/>这样虚拟机会直接暴露宿主 CPU 的全部特性。代价是虚拟机对硬件更“敏感”,一旦你想把这台虚拟机迁移到另一块 CPU 型号不同的宿主机上,就可能起不来。所以我的习惯是单机测试环境用 host-passthrough,生产集群按需用 host-model 或指定 CPU model,以便迁移。
6.2 内存大页与 NUMA 亲和
内存虚拟化最常见的性能损耗来源是页表翻译。大页(HugePages)能有效减少 TLB miss,对内存密集型虚拟机提升很明显。Linux 默认开启的透明大页(THP)虽然方便,但它对 KVM 的适配不如手动配置的静态大页稳定。
配置静态大页的第一步是预留大页内存:
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages然后编辑虚拟机 XML,在<memoryBacking>标签里开启:
<memoryBacking> <hugepages/> </memoryBacking>重启虚拟机后,virsh dominfo vm1的输出里内存部分会显示为 “Huge pages”。需要提醒的是,这个配置要求宿主机有足够大页内存可用,如果预留不足,虚拟机可能启动失败。
NUMA 亲和是另一个容易被忽视的点。在多路服务器上,每个 CPU 物理封装都配有自己的一组内存节点。如果虚拟机 CPU 被调度到 node 0,内存却分配在 node 1,跨节点访问会让延迟大幅上升。用numactl --hardware可以看到宿主节点的分布。然后在虚拟机 XML 里设置:
<cpu mode='host-passthrough' check='none'> <numa> <cell id='0' cpus='0-1' memory='2097152'/> </numa> </cpu>并在<numatune>标签里指定内存节点:
<numatune> <memory mode='strict' nodeset='0'/> </numatune>配合 CPU pinning 使用,效果最理想。
6.3 VirtIO 驱动与多队列
VirtIO 是 KVM 性能调优里最重要的关键词。磁盘和网络设备只要走 VirtIO 半虚拟化,就能绕过设备模拟的开销,直接在宿主和客户机之间共享 virtqueue 队列。Linux 客户机自带 VirtIO 驱动,Windows 客户机需要单独安装 virtio-win 驱动。
磁盘模型在 XML 里对应<disk>标签内的<target bus='...'>。把bus从ide改成virtio,磁盘吞吐往往能提升数倍。网络接口则要开启多队列功能,让每个虚拟队列绑定不同的 vCPU,避免单个软中断 CPU 成为瓶颈:
<interface type='network'> <source network='default'/> <model type='virtio'/> <driver name='vhost' queues='4'/> </interface>客户机内部还要手动打开多队列网卡:
ethtool -L eth0 combined 4把队列数调成和 vCPU 数一致比较合理。队列数设多了反而因为上下文切换增加开销。
6.4 常见性能对比参考
我会用一个简单表格记录默认配置和调优后的实际差距:
| 项目 | 默认配置 | 调优配置 | 典型提升 |
|---|---|---|---|
| 磁盘总线 | SATA/IDE | virtio-scsi | 吞吐 3 ~ 8 倍以上 |
| 网卡模型 | e1000 | virtio-net 多队列 | 转发吞吐 2 ~ 4 倍以上 |
| CPU 模式 | 兼容性模型 | host-passthrough | 减少指令集差异损耗 |
| 内存分配 | THP | 静态 HugePages | 减少 TLB miss |
| 中断处理 | emulated | vhost-net 后端 | 减少用户态切换 |
这套调优组合拳打完之后,虚拟机性能已经非常接近物理机。对于绝大多数业务负载,唯一的瓶颈基本只剩宿主机的物理资源了。
7. 踩坑排查:从 /dev/kvm 缺失到虚拟机黑屏
7.1 模块加载与内核参数导致 /dev/kvm 不存在
我见过最频繁的报错是虚拟机创建失败,提示 “KVM is not available”。遇到这种情况,第一步永远不是重装软件包,而是按顺序检查三条链路:
# 1. CPU 是否支持虚拟化 grep -E -c '(vmx|svm)' /proc/cpuinfo # 2. 内核模块是否加载 lsmod | grep kvm # 3. 设备节点是否存在 ls -l /dev/kvm模块没加载时,加载模块以后看设备节点有没有出现。如果模块加载失败,大概率是 CPU 型号太老,或者 BIOS 里的虚拟化开关没打开。还有一种是“嵌套虚拟化”场景,也就是你本身跑在一台虚拟机里,虚拟机再开 KVM。这需要宿主的 KVM 模块开启嵌套参数:
echo 'options kvm_intel nested=1' >> /etc/modprobe.d/kvm.conf modprobe -r kvm_intel modprobe kvm_intel开启后,虚拟机里才能再次看到 vmx flag。很多人在云主机上搭 QEMU + KVM,发现怎么弄都无法硬件加速,基本都是这个原因。云主机如果没有嵌套虚拟化支持,那 KVM 这条路就比较尴尬,只能退到纯 QEMU 模拟。
7.2 libvirt 权限和 AppArmor/SELinux 拦截
普通用户访问 libvirt 报 “Permission denied”,通常就是libvirt组的问题。重新登录后问题一般就会解决。另一个常见报错是与安全模块相关,虚拟机启动时提示无法访问磁盘文件。
Ubuntu 上 AppArmor 默认给 libvirt 的磁盘访问做了一层限制,如果你的磁盘镜像放在/var/lib/libvirt/images/以外,比如放在/home/或挂载盘里,就很可能被拦截。解决方法是把镜像文件放到标准目录,或者为对应路径添加 AppArmor 规则。CentOS 上对应的是 SELinux,需要开启virt_use_nfs等布尔值:
setsebool -P virt_use_nfs true如果是普通目录访问受限,也可以临时用chcon -t virt_image_t /path/to/image调整标签。但最省事的做法还是把镜像统一放到/var/lib/libvirt/images/下,这个目录是 libvirt 的默认存储池所在位置,各安全模块对这个路径都有默认放行规则。
7.3 虚拟机黑屏或启动卡住
新建虚拟机后屏幕一直停在左上角光标,这个现象有几种可能。
一是启动顺序不对。如果 ISO 没被加入到优先启动项,虚拟机可能直接从空硬盘启动,自然就是黑屏。用--boot cdrom,hd强制指定启动顺序即可。
二是输出设备问题。无桌面服务器上默认走 VNC 显示,如果你根本没连 VNC 窗口,屏幕上当然只有一片黑。这时候用virsh vncdisplay vm-name查看显示端口,然后连上看看,往往系统已经正常启动了。
三是云镜像只有串口控制台。如果创建时用了--graphics none,但又没加--console pty,target_type=serial,虚拟机有没有启动你完全看不到。加串口参数后,宿主机终端就能看到虚拟机的启动日志,排查起来直观得多。
四是内存不足。如果宿主内存已经被其他虚拟机占满了,新虚拟机可能反复重启或者卡在早期引导阶段。可以先virsh freecell查看可用内存,确认资源充足。
我给自己的排查顺序是:先看/dev/kvm有没有,再确认 CPU 和内存资源,最后才怀疑系统镜像。这套顺序能覆盖大多数“看似玄学”的启动问题。
7.4 磁盘镜像与快照管理的惯例
快照是 QEMU/KVM 环境里非常实用但经常没人提的功能。qcow2 格式天然支持内部快照,用命令就能快速备份。
virsh snapshot-create-as vm1 snap1 "description" virsh snapshot-list vm1 virsh snapshot-revert vm1 snap1这个功能在升级系统或改配置之前尤其有用。我在给某台虚拟机安装新软件包或者改内核参数之前,都会先打一个快照。操作失误了就直接回滚,而不是重新装一遍系统。但要注意,qcow2 内部快照对性能有一定影响,生产环境追求极致性能时,更推荐在存储层做外部快照或者用 LVM。
磁盘镜像管理上还有一个通用经验:尽量使用 qcow2 而不是 raw。qcow2 支持稀疏分配、压缩、快照、后备镜像,功能全面。raw 虽然性能略好、格式简单,但没有快照功能,管理起来容易吃满磁盘。纯性能测试场景可以用 raw,日常使用优先 qcow2。
写在最后的一段实操体会
在整个 QEMU + KVM 环境的搭建和维护过程中,我个人最深的体会是:虚拟化这个领域,收益往往藏在“组合拳”里。单看一个参数,你可能觉得没什么变化,但当你把 CPU 型号、VirtIO 队列、内存大页、磁盘控制器全部调整到位之后,虚拟机跑大型应用的手感跟默认配置完全是两台机器。而且越往底层走,越要习惯“先查硬件,再查内核,最后查配置”的思维。绝大多数看起来诡异的问题,原因都很朴素:要么 CPU 虚拟化没开,要么权限不够,要么安全模块拦截了磁盘路径。
如果你是从桌面虚拟化转过来的,不要一上来就追求最复杂的桥接和直通方案。先把默认 NAT 网络跑通,把第一个 Linux 虚拟机顺利装好,再一步步换成桥接、调优 VirtIO、尝试快照。每一步都验证通过,再进入下一步,这样遇到问题时,你能很清楚知道问题出在你改动的哪一层。QEMU + KVM 这套技术栈足够厚实,值得你花时间慢慢啃。