libvirt安装实战:从环境检查到创建第一台虚拟机与排错
1. 先搞清楚要装的是什么:libvirt 这一层到底管什么
很多人在安装 libvirt 之前,其实对它的定位是模糊的。你可能会问:我直接用 qemu-system-x86_64 命令不也能启动虚拟机吗?为什么还要额外装一个 libvirt 和一堆配套的包?
这里要先理清一个概念:KVM/QEMU 负责的是底层的 CPU 虚拟化和设备模拟,也就是"真正在跑虚拟机"的那部分。而 libvirt 是站在它们上面的一层统一管理接口,它本身不模拟任何硬件,也不执行任何虚拟 CPU 指令,它做的是三件事:定义虚拟机、调度虚拟机生命周期、统一配置资源。
换句话说,libvirt 是一个"虚拟化管理中间层"。它给你提供了一套稳定的 API 和命令行工具,让你不用去记不同 hypervisor 各自的命令格式。今天你用 KVM,明天想用 LXC 容器,甚至想管理远端宿主机上的虚拟机,对于上层工具来说,调用 libvirt 的方式几乎不变。
libvirt 装好之后,你的系统里会多出几个关键组件:
- libvirtd:后台守护进程,所有管理请求都经由它转达给具体的 hypervisor;
- virsh:命令行管理工具,日常操作虚拟机主要靠它;
- virt-install / virt-clone:从镜像创建虚拟机、克隆已有虚拟机的专用工具;
- virt-manager:图形化管理界面,前端图形化封装,底层还是调用 libvirt 的 API;
- libvirt-client 相关库文件:给其他语言(如 Python 的 libvirt-python)提供开发接口。
这个内容适合谁?如果你是运维、虚拟化平台开发者,或者只是想在个人电脑上搭一套 KVM 实验环境,那 libvirt 都是绕不开的一环。这篇内容会覆盖从环境检查、安装方式选型、装完后的权限与网络配置,到第一台虚拟机的创建和常见报错排查,尽量让你一次走通。
2. 装之前的评估:环境不达标,装完也跑不起来
2.1 CPU 虚拟化支持是第一道门槛
libvirt 本身是纯软件,但它要管理的 KVM 需要硬件辅助虚拟化。所以在安装之前,先确认你的 CPU 支持并已开启虚拟化扩展:
grep -E -o '(vmx|svm)' /proc/cpuinfo | sort -uIntel 平台显示 vmx,AMD 平台显示 svm。如果这里没有输出,大概率是 BIOS/UEFI 里没开虚拟化开关,或者你的 CPU 型号本身不支持。还有一种情况是:你现在这台机器本身就是一个虚拟机,那就要看宿主是否开启了嵌套虚拟化,这一层没打开,guest 里面再装 KVM 会非常痛苦。
更直接的检查方式是看设备节点是否存在:
ls -l /dev/kvm有这个节点,说明内核 KVM 模块已经加载成功。如果文件不存在,试着加载模块:
modprobe kvm_intel # Intel 平台 modprobe kvm_amd # AMD 平台模块加载失败时可以看内核日志定位原因,常见的包括 BIOS 未开启虚拟化、已有其他管理程序占用 VT-x 等。
2.2 架构、内核版本与内存规划
接着确认系统架构:
uname -m绝大多数场景是 x86_64,但 ARM64(aarch64)平台也能跑 KVM 加 libvirt,只是发行版包名略有差异。内核版本建议在较新的长期支持版本以上,太老的内核可能存在 cgroup、网络过滤方面的兼容问题,连带着 libvirtd 在启动时也会出现各种莫名其妙的告警。
内存方面,libvirtd 本身很轻,占的内存可以忽略不计,真正吃内存的是你要跑的虚拟机。但有个容易被忽略的点:libvirt 默认给每个虚拟机的 QEMU 进程创建一个 cgroup,所有 guest 的总内存和 CPU 配额受宿主机整体资源约束。如果你打算跑多个虚拟机,先估算一下总量,免得后面物理内存不够导致 guest 被 OOM 杀掉。
2.3 先决定装到哪种程度
libvirt 的安装不是单一一个包,而是一组互相依赖的包。根据使用场景,装的东西可以差很多:
- 只要命令行管理:libvirt-daemon、libvirt-client 就够;
- 还要能从镜像创建虚拟机:加 virt-install;
- 桌面环境想要图形界面:加 virt-manager;
- 要在浏览器里管理:考虑 cockpit 的虚拟机插件。
我接触过不少新手,上来就只装一个 libvirt0,结果发现根本没有 virsh 命令,这就是没搞清楚包之间的分工。下面一节会给出完整的安装命令。
3. 三条安装路线:软件源安装、容器部署、源码编译
3.1 发行版软件源安装是最稳妥的选择
对绝大多数人来说,不要自己折腾源码编译,直接用发行版仓库里的包是最省心的方案。软件的打包、依赖、服务文件、默认配置都已经帮你处理好了。
Debian 系发行版下的完整安装:
sudo apt update sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager这几个包的分工大概是:
- qemu-kvm:底层 hypervisor,提供 /usr/bin/qemu-system-x86_64 ;
- libvirt-daemon-system:包含 libvirtd 守护进程、socket 激活单元和默认的 AppArmor 策略;
- libvirt-clients:包含 virsh 等命令行工具;
- bridge-utils:提供 brctl 工具,建网桥的时候会用到;
- virtinst:提供 virt-install、virt-clone;
- virt-manager:图形化管理界面。
RHEL/CentOS 系发行版下,可以直接用虚拟化组包:
sudo dnf install @virtualization这条命令会一次拉进来 KVM、libvirt、virt-install,以及相关的管理前端组件。手动拆开装的话就是:
sudo dnf install -y qemu-kvm libvirt virt-install装完注意:不同发行版的服务名可能略有差异,Debian 系通常直接用libvirtd,有的新版本用libvirtd.socket做 socket 激活。务必用 systemctl 确认之后再往下走。
3.2 容器里跑 libvirtd:适合测试,不适合生产
如果你想把 libvirtd 跑在容器里,比如在一个统一的基础镜像里做虚拟化控制平面的开发,可以做,但要把几个关键设备透传进去:
docker run -d \ --name libvirt-test \ --device /dev/kvm \ -v /sys/fs/cgroup:/sys/fs/cgroup:rw \ -v /var/lib/libvirt:/var/lib/libvirt:rw \ --network host \ libvirt/libvirtd容器化部署的坑很多,最典型的是 /dev/kvm 访问权限和 cgroup 挂载的读写权限。容器里的模块加载、udev 规则、AppArmor/SELinux 策略都和宿主机不完全一致,经常会出现 libvirtd 启动正常但一创建虚拟机就报 monitor 连接失败的问题。我自己的实践体会是:容器方式适合做 API 层面的功能测试和 CI 环境,生产环境老老实实用宿主机安装的方式,省下的那点隔离性远远补不上踩坑的时间成本。
还有一点要特别注意:如果你本身的"宿主机"就是一台虚拟机,那容器里的 libvirtd 同样需要嵌套虚拟化支持,没有 /dev/kvm 的话,QEMU 会退化成纯软件模拟(TCG 模式),性能完全没法看。
3.3 源码编译安装:什么人才需要这么做
源码编译的情况越来越少,一般只有这三种诉求:定制编译选项、目标发行版的仓库里软件版本太旧、或者你在做嵌入式和交叉编译相关的定制。
libvirt 的编译依赖较长一串开发库,包括 gcc、make、pkg-config、glib2-devel、libxml2-devel、yajl-devel、libnl3-devel 等。基本步骤如下:
tar -xzf libvirt-*.tar.gz cd libvirt-* meson setup build --prefix=/usr -Ddriver_qemu=enabled meson compile -C build meson install -C build新版 libvirt 已经迁移到 meson 构建系统,configure 脚本时代已经过去了。编译本身通常几分钟能跑完,但真正麻烦的是后置工作:你要自己处理 systemd 服务单元、/etc/libvirt 下的默认配置、SELinux/AppArmor 策略。这些在发行版打包时都替你做好了,源码装完这些全得自己补,漏掉任何一个,后面用起来都别扭。
所以我给的建议很直接:没有特殊理由,直接用发行版仓库。编译这条路知道流程即可,不值得为了"尝鲜"付出配置成本。
4. 装完后的配置:权限、网络、存储一个都不能漏
4.1 让当前用户能管理虚拟机
安装完成后第一件事是确认服务状态:
sudo systemctl status libvirtd如果没启动,先启动并设置开机自启:
sudo systemctl enable --now libvirtd然后把你的当前用户加入到 libvirt 和 kvm 用户组,否则每次执行 virsh 都得加 sudo:
sudo usermod -aG libvirt,kvm 你的用户名这里解释一下为什么:libvirtd 默认以 root 身份运行,但它的访问策略通过 polkit 控制。系统里会有 libvirt 和 libvirt-qemu 这样的系统用户组,把普通用户加进 libvirt 组之后,这个用户才能通过 polkit 的默认规则获得 qemu:///system 域的管理权限。改完用户组之后需要重新登录一次才能生效,这是很多人第一次执行 virsh list 报 Permission denied 的原因。
另外要分清两个连接 URI:
virsh -c qemu:///system list --all virsh -c qemu:///session list --allsystem 是需要系统级权限的持久域,虚拟机以 libvirt-qemu 系统用户的身份运行,适合放到后台长期跑;session 是用户级域,仅对当前登录用户可见,虚拟机进程以你自己的用户身份运行。刚入门的朋友如果发现 virsh list 和图形界面看到的虚拟机列表对不上,多半就是搞混了这两个域。
4.2 默认网络:你一定用得上
装完 libvirt,系统里默认会定义一个名为 default 的 NAT 网络,但很多发行版默认不会自动启动它。检查一下:
virsh net-list --all如果看到 default 显示为 inactive,启动并设置自动启动:
virsh net-start default virsh net-autostart default这个网络做了什么事?它会在宿主机上创建一个名为 virbr0 的网桥,IP 段通常是 192.168.122.0/24,libvirtd 还会拉起一个内嵌的 dnsmasq 负责给虚拟机分配 IP 和做 DNS 解析。虚拟机只要在配置里绑定了 network=default,就能拿到一个 192.168.122.x 的地址,由宿主做 NAT 转发出去上网。
平时常见的一个坑是:宿主机上你自己跑了一个 dnsmasq 或别的 DHCP 服务,占了 53 或 67 端口,导致 default 网络启动时报 dnsmasq 起不来。解决方法是换掉默认网络的 IP 段,或者干脆停掉你自建的那个服务。还有一个更隐蔽的问题:部分云厂商的 VPC 内部已经使用了 192.168.122.0/24 网段,网络冲突会让虚拟机网络时通时断,这种情况建议直接编辑 default 网络换一个网段。
4.3 默认存储池确认与磁盘规划
libvirt 管理存储的逻辑是存储池加卷。安装后默认有一个 pool 指向 /var/lib/libvirt/images,所有虚拟机的磁盘镜像默认放这里:
virsh pool-list查看默认池的信息:
virsh pool-info default这个目录默认属于 root 用户和 libvirt 组,权限倒是没问题,但要留意磁盘空间。很多人虚拟机建到一半才发现根分区不够,而 /var/lib 恰恰在根分区的场景很常见,尤其是一些默认分区方案里 /var 没有单独划分的系统。建议先看看磁盘剩余空间:
df -h /var/lib/libvirt/images空间紧张的话可以建一个新的存储池指向别的分区或数据盘:
virsh pool-define-as data dir - - - - /data/libvirt-images virsh pool-build data virsh pool-start data virsh pool-autostart data之后创建虚拟机的时候直接用 --disk pool=data 指定就行了。
4.4 socket 激活与 polkit 的互动
现代发行版里 libvirtd 默认走 socket 激活:libvirtd.socket 在监听到连接时才拉起主服务,这样能减少闲置占用的系统资源。如果你发现 systemctl status libvirtd 显示 active(running),但进程列表里找不到 libvirtd,别慌,多半是 socket 激活模式在起作用。
这个机制的副作用是:日志里偶尔会出现连接中断或重启服务后才生效配置的现象。改完 /etc/libvirt/libvirtd.conf 之后,习惯性重启这两个单元:
sudo systemctl restart libvirtd.socket libvirtd.service很多人在改配置之后只 restart 了 libvirtd.service,结果发现配置没生效,其实就是忘了把 libvirtd.socket 一起重启,因为连接还复用在旧的 socket 描述符上。
5. 验证安装成果:从版本检查到创建第一台虚拟机
5.1 基础自检
服务和权限都搞定后,先看版本信息:
virsh version这里能看到三部分数据:virsh 本身版本、libvirt 库版本、底层的 QEMU 版本。三者的版本号不必完全一致,但不要相差太远,否则会出现 API 兼容性告警。
接下来用工具直接检测宿主机虚拟化能力:
virt-host-validate这个命令会逐项检查 CPU 虚拟化、硬件支持、安全模块、cgroup 等,任何 FAIL 项都会在这里暴露出来。
确认能连上守护进程并列出虚拟机:
virsh list --all刚装完应该没有任何虚拟机,但命令能正常执行且不报错就说明链路已经是通的了。
5.2 用 virt-install 创建第一台虚拟机
安装一部 Linux 虚拟机的典型命令如下:
sudo virt-install \ --name demo \ --memory 2048 \ --vcpus 2 \ --disk path=/var/lib/libvirt/images/demo.qcow2,size=20,format=qcow2 \ --os-variant ubuntu22.04 \ --network network=default \ --graphics none \ --location /path/to/iso或http://安装源几个参数选型上多解释一句:
- disk 的 format 建议选 qcow2,qcow2 是稀疏文件,创建时只占很小空间,按需增长,还支持快照和压缩。raw 格式性能稍好一点,但没有这些衍生特性;
- os-variant 可以用
osinfo-query os查可用列表,它决定了虚拟机的默认设备模型和驱动选择,选对了能避免很多启动失败的问题; - 如果你想在纯命令行环境安装,--graphics none 会启用文本模式安装,安装完成之后可以用 virsh console demo 进入虚拟机终端。如果是在桌面环境,把 --graphics none 去掉,virt-manager 或 virt-viewer 会弹出图形窗口。
安装完的虚拟机查看和管理:
virsh list --all virsh start demo virsh console demo virsh reboot demo virsh shutdown demo virsh destroy demo # 强制断电,类比拔电源这里要提醒一下:destroy 在英文里名字看起来很重,但它对 KVM 虚拟机来说就是强制关机的意思,不会误删磁盘文件。真正常见的误操作反而是virsh undefine,这个命令会删除虚拟机定义,配合 --remove-all-storage 参数还会连带删除磁盘镜像,执行前一定确认这不是你要保留的数据。
5.3 快照与克隆:装完之后值得马上试的两个功能
虚拟机跑起来之后,第一时间建议试试快照:
virsh snapshot-create-as demo first-snapshot "安装完成后" virsh snapshot-list demo virsh snapshot-revert demo first-snapshotqcow2 格式下快照几乎是瞬时完成的,因为写时复制机制只记录差异数据。这个功能在折腾系统、测试软件时是救命稻草:遇到搞坏的配置直接回滚,不用重装。
克隆也是一行命令的事:
virt-clone --original demo --name demo-clone --auto-clone它会自动为新虚拟机生成新的磁盘镜像和新的 MAC 地址,省去手动改配置的麻烦。但注意克隆出来的虚拟机如果两台上电同时运行在同一个网络里,主机名和 SSH 密钥都是同一套,需要进系统重新配置,这是很多人克隆完发现"两台机器像双胞胎"的原因。
6. 高频报错排查:我把踩过的坑整理成了一张速查表
| 報错现象 | 根本原因 | 处理方式 |
|---|---|---|
| virsh 提示无法连接 socket,Connection refused | libvirtd 服务未启动或 socket 被禁用 | systemctl start libvirtd;检查 libvirtd-tcp.socket / libvirtd.socket 状态 |
| 连接成功但 virsh list 报 Permission denied | 当前用户不在 libvirt 组;polkit 策略未生效 | 加入 libvirt 组并重新登录;确认没有自定义 polkit 规则挡掉普通用户 |
| 启动 default 网络失败,日志显示 dnsmasq 出错 | 宿主机 53/67 端口被占用,或网段冲突 | 编辑 default 网络换 IP 段;排查自建 DNS/DHCP 服务 |
| 创建虚拟机时 kvm_intel 模块加载失败 | BIOS 未开 VT-x,或宿主未开启嵌套虚拟化 | 进 BIOS 开启虚拟化;在宿主机验证 /dev/kvm 存在 |
| 虚拟机启动后卡住,日志出现 monitor connection reset | QEMU 进程被安全模块拦截,或 cgroup 资源不足 | 检查 AppArmor/SELinux 日志,调整相应策略;查看 dmesg 里 OOM 记录 |
| virsh console 连不上,黑屏无输出 | 虚拟机没有配置串口控制台 | 创建时加 --graphics none --extra-args "console=ttyS0";或安装完在 guest 里配置 getty |
| destroy 之后虚拟机定义还在,启动提示重复 | 忘了 undefine 或定义里磁盘路径占用 | 先用 undefine 清除定义;确认磁盘文件没有被其他虚拟机占用 |
| 两个虚拟机网络冲突,IP 时有时无 | 克隆后 MAC 地址重复 | 用 virt-clone --auto-clone 重新生成 MAC;手动改 ifcfg 里的 HWADDR |
再补充一个很隐蔽的坑:日志里出现 AppArmor 拒绝访问某个 qcow2 文件的记录,但你的磁盘路径明明在 /var/lib/libvirt/images 下。这种通常是之前手动移动过镜像文件,文件的 security label 没有跟着走。解决方法是把镜像放回默认池路径,或者用chcon/ AppArmor 的 profile 调整,不要图省事直接把 AppArmor 整个禁用,那等于拆了安全层。
另外,如果你在虚拟机里又装了 KVM 做嵌套实验,记得确认宿主和 guest 两边都把嵌套虚拟化打开。Intel 平台宿主机上通常这样做:
echo 'options kvm_intel nested=1' > /etc/modprobe.d/kvm_intel.conf重启后 guest 里能看到 vmx 标志位,才说明这条链路是通的。
7. 最后分享一点我的实操体会
装 libvirt 这件事,表面上看是几条安装命令,实际上真正的复杂度都在装完之后。我个人的建议是:装完先别急着上图形界面和自动化的管理工具,先用 virsh 命令行把网络、存储、快照这些概念亲手操作一遍。命令行工具的输出自带完整信息,报错也直接,能帮你把 libvirt 的内部机制摸清楚,之后再接触那些封装好的面板或 API,遇到问题你也不会慌。
另外一个小技巧:改任何 /etc/libvirt 下的配置之前,先备份一份。libvirt 的配置解析非常严格,一个 XML 标签写错可能导致整个服务起不来,有备份兜底能省下大把排查时间。虚拟机这块也一样,/var/lib/libvirt/images 里的磁盘镜像就是你的全部身家,定期做快照或者定期备份,等真正用到的时候你会回来感谢这个习惯。
如果你能把第一台虚拟机从创建、克隆到快照回滚完整走一遍,libvirt 这层的基本功就算扎实了。后面再接触 OpenStack、Ovirt 这些上层平台,你会发现它们底层全是这一套东西,到时候再回头看这个安装文档,感触会完全不同。