存储管理与快照迁移
存储管理 && 快照迁移 && 高级运维
[TOC]
🧱 知识点总结
在上篇笔记📚中,我们用 qemu-img create + virsh define 完成批量创建
- 不管用
qemu-img还是virsh vol-create-as创建磁盘,最终 libvirt 都能管理 - ==关键在于:libvirtd 不实时监控文件系统变化,需要
pool-refresh同步状态==
🧱 为什么需要 pool-refresh?
libvirt 内部维护了一份元数据”登记表”,但它并不使用 inotify 实时监听目录:
qemu-img create和rm等文件系统操作,libvirtd 不会自动感知- 需要通过
pool-refresh让 libvirtd 重新扫描池目录,同步元数据 pool-start(启动池)会隐式执行一次 refresh
✅️ 正确的理解 —— 不是 “qemu-img 创建的磁盘不受管”,而是 “文件系统变更需要 refresh 才能被 libvirt 感知”
📌 两种创建方式对比:
| 创建方式 | 特点 | refresh 后状态 |
|---|---|---|
qemu-img create | 直接操作文件系统,灵活 | 与 virsh 创建无异,libvirt 全可见 ✅️ |
virsh vol-create-as | libvirt 代为创建,一步到位 | libvirt 全可见 ✅️ |
核心事实:无论哪种方式创建,只要文件在池目录里且做过 refresh,libvirt 都能完整管理(vol-list / vol-info / vol-delete 均可用)
📦 存储池 (Storage Pool)
==存储池== 就是 libvirt 管理的”仓库”,虚拟机磁盘、ISO 镜像都放在池子里
存储池类型
| 类型 | 说明 | 适用场景 |
|---|---|---|
dir | 目录型,最常用 | 本地磁盘镜像存放 |
lvm | 基于 LVM 卷组 | 需要快照、高性能的场景 |
nfs | 基于 NFS 共享 | 共享存储、迁移场景 |
💡 这里我们以 ==dir 目录型== 为重点,生产环境中 LVM 和 NFS 同样重要
创建目录型存储池
1)查看存储池jiuzhao@Ubuntu ~$ virsh pool-list --all Name State Autostart------------------------------- default🌰 active yes img active yes 数据盘 active yes# img是我专门用来存放下载的镜像~# 数据盘就是创建出来的磁盘'上面这俩都是我在kvm UI界面进行创建的'=================================================🌰 默认存储池jiuzhao@Ubuntu ~$ virsh pool-dumpxml default | grep path <path>/var/lib/libvirt/images</path># 这就是 day02 中我们一直放磁盘的地方!jiuzhao@Ubuntu ~$ virsh vol-list img# 查看指定存储池里的卷(虚拟磁盘/ISO镜像)列表和信息 Name Path---------------------------------------------------------------- Rocky-10.2-x86_64-dvd1.iso /home/jiuzhao/img/Rocky-10.2-x86_64-dvd1.iso Rocky-10.2-x86_64-minimal.iso /home/jiuzhao/img/Rocky-10.2-x86_64-minimal.iso ubuntu-24.04.4-live-server-amd64.iso /home/jiuzhao/img/ubuntu-24.04.4-live-server-amd64.isojiuzhao@Ubuntu ~$ virsh pool-refresh imgPool default refreshed# 手动同步和更新存储池信息📌 关于 default 池(默认存储池)
pool-refresh后,目录里的.qcow2/.iso等镜像文件会被识别为存储卷pool-start(启动池)会隐式执行一次 refresh- ⚠️ libvirtd 不会自动实时同步,新增/删除文件后需手动 refresh
2)定义存储池'我们先手动创建目标路径'jiuzhao@Ubuntu ~$ mkdir ~/testjiuzhao@Ubuntu ~$ virsh pool-define-as pool_test dir --target "/home/jiuzhao/test/"# pool-define-as: 定义存储池# pool_test: 存储池名称# dir: 类型为目录型# --target: 存储池对应的物理目录路径jiuzhao@Ubuntu ~$ virsh pool-list --all Name State Autostart----------------------------------- default active yes img active yes pool_test inactive no'现在是未激活状态也没有开机自启动'
3)启动存储池jiuzhao@Ubuntu ~$ virsh pool-start pool_testPool pool_test started
4)设置开机自启jiuzhao@Ubuntu ~$ virsh pool-autostart pool_testPool pool_test marked as autostartedjiuzhao@Ubuntu qemu$ virsh pool-list --all | grep test pool_test active✅ yes✅
5)查看存储池详细信息jiuzhao@Ubuntu ~$ virsh pool-info pool_testName: pool_testUUID: 780fc28c-419c-41fe-adc2-650697d9161bState: running '✅️ 活跃状态'Persistent: yes '✅️ 持久化'Autostart: yes '✅️ 开机自启'Capacity: 732.48 GiB '总容量'Allocation: 88.91 GiB '已分配'Available: 643.57 GiB '可用空间'💡 类比对照:存储池的操作和上篇笔记 ==虚拟网络== 的操作几乎一模一样
| 操作 | 虚拟网络 | 存储池 |
|---|---|---|
| 定义 | virsh net-define | virsh pool-define-as |
| 启动 | virsh net-start | virsh pool-start |
| 自启 | virsh net-autostart | virsh pool-autostart |
| 查看 | virsh net-list --all | virsh pool-list --all |
| 停止 | virsh net-destroy | virsh pool-destroy |
| 删除 | virsh net-undefine | virsh pool-undefine |
📦 存储卷 (Storage Volume)
==存储卷== 是存储池中的具体”货物” —— 一个个虚拟磁盘文件
通俗解释:池子是仓库,卷就是仓库里的每个箱子 (磁盘镜像)
🧱 与 day02 的联动:
两种方式的本质区别不在”受管/不受管”,而在:
qemu-img create:直接操作文件系统 → 需pool-refresh同步到 libvirtvirsh vol-create-as:libvirt 代劳创建 → 自动完成”创建 + 登记”- refresh 之后,两者在 libvirt 中完全等价(vol-list、vol-info、vol-delete 均可用)
📌 完整命令对照:
# ===== 创建独立磁盘 =====# day02 方式:qemu-img + pool-refreshqemu-img create -f qcow2 /home/jiuzhao/test/node.img 15Gvirsh pool-refresh pool_test # 必须手动同步!
# day03 方式:一步到位virsh vol-create-as \ --pool pool_test \ --name vm-disk1 \ --capacity 20GB \ --format qcow2
# ===== 创建前端盘(COW) =====# day02 方式:qemu-img -b + pool-refreshqemu-img create -f qcow2 \ -b /home/jiuzhao/test/node.img \ -F qcow2 \ /home/jiuzhao/test/node-front.qcow2 \ 20Gvirsh pool-refresh pool_test # 必须手动同步!
# day03 方式:一步到位virsh vol-create-as \ --pool pool_test \ --name vm-disk1-front \ --capacity 20GB \ --format qcow2 \ --backing-vol vm-disk1 \ --backing-vol-format qcow2
# ===== 删除磁盘 =====# day02 方式:rm + pool-refreshrm /home/jiuzhao/test/node.imgvirsh pool-refresh pool_test # 必须手动同步!
# day03 方式:一步到位virsh vol-delete vm-disk1 --pool pool_test创建存储卷
1)查看存储池中的卷列表jiuzhao@Ubuntu ~$ virsh vol-list pool_test Name Path--------------🈳
2)在存储池中创建卷jiuzhao@Ubuntu ~$ virsh vol-create-as \ --pool pool_test \ --name vm-disk1 \ --capacity 20GB \ --format qcow2Vol vm-disk1 created# 由 libvirtd (root) 代理创建
3)验证卷已创建jiuzhao@Ubuntu ~$ virsh vol-list pool_test Name Path----------------------------------------- vm-disk1 /home/jiuzhao/test/vm-disk1jiuzhao@Ubuntu qemu$ ls -lh /home/jiuzhao/test/总计 196K# vm-disk1 属主 root —— virsh vol-create-as 由 libvirtd (root) 代理创建-rw------- 1 root root 193K 7月 16 20:37 vm-disk1-rw-r--r-- 1 jiuzhao jiuzhao 193K 7月 16 20:59 test.qcow2# test.qcow2 属主 jiuzhao —— qemu-img create 以当前用户直接创建# root身份必须sudo一下jiuzhao@Ubuntu ~$ sudo qemu-img info /home/jiuzhao/test/vm-disk1image: /home/jiuzhao/test/vm-disk1file format: qcow2virtual size: 18.6 GiB (20000000000 bytes)disk size: 196 KiB '精简置备,实际只占 196K'cluster_size: 65536......
4)查看卷详细信息jiuzhao@Ubuntu ~$ virsh vol-info --pool pool_test --vol vm-disk1Name: vm-disk1Type: fileCapacity: 18.63 GiBAllocation: 196.00 KiB📌 virsh vol-create-as 参数速查:
| 参数 | 含义 |
|---|---|
--pool | 目标存储池名称 |
--name | 卷名称 |
--capacity | 容量 (支持 G/M/K 单位) |
--format | 格式 (qcow2 / raw) |
--backing-vol | 后端盘名称 (创建前端盘时用) |
--backing-vol-format | 后端盘格式 |
📌 关于文件后缀:磁盘文件的格式由**文件头部魔数(magic bytes)**决定,与后缀名无关
写成 .qcow2、.img、.disk 甚至不加后缀,qemu-img info 都能正确识别
jiuzhao@Ubuntu ~$ sudo qemu-img info ~/test/vm-disk1 | grep 'file format'file format: qcow2不过约定俗成地用 .qcow2 / .raw 后缀,方便人眼辨认
使用 virsh 创建前端盘
回顾 day02 COW 概念:前端盘依赖后端盘,只存差异数据
jiuzhao@Ubuntu ~$ cd test/jiuzhao@Ubuntu test$ lstest.qcow2 vm-disk1jiuzhao@Ubuntu test$ virsh vol-create-as \ --pool pool_test \ --name vm-disk1-front \ --capacity 30G \ --format qcow2 \ --backing-vol vm-disk1 \ --backing-vol-format qcow2Vol vm-disk1-front createdjiuzhao@Ubuntu test$ ls -lh总计 588K-rw-r--r-- 1 jiuzhao jiuzhao 193K 7月 16 20:59 test.qcow2-rw------- 1 root root 193K 7月 16 20:37 vm-disk1-rw------- 1 root root 193K 7月 17 08:49 vm-disk1-front# root身份创建的需要sudo
jiuzhao@Ubuntu test$ sudo qemu-img info /home/jiuzhao/test/vm-disk1-frontimage: /home/jiuzhao/test/vm-disk1-frontfile format: qcow2virtual size: 30 GiB (32212254720 bytes)disk size: 196 KiBbacking file: /home/jiuzhao/test/vm-disk1 '后端盘已关联 ✅️'backing file format: qcow2
jiuzhao@Ubuntu test$ virsh vol-list pool_test Name Path----------------------------------------------------- test.qcow2 /home/jiuzhao/test/test.qcow2 vm-disk1 /home/jiuzhao/test/vm-disk1 vm-disk1-front /home/jiuzhao/test/vm-disk1-front✅️ 与 qemu-img create -b 对比:效果一样,区别仅在于 virsh vol-create-as 一步完成”创建 + 登记”,而 qemu-img 需要手动 refresh 后才被 libvirt 感知
qemu-img 创建磁盘 —— pool-refresh 同步
用
qemu-img直接创建的磁盘文件不会被 libvirtd 实时感知
qemu-img 创建的文件只要在池目录中,经过 refresh 就能被 libvirt 管理
执行
pool-refresh即可让 libvirt 重新扫描池目录、更新元数据,磁盘便纳入管理💡 refresh 只是让 libvirt 的”登记表”和磁盘文件系统对齐而已
'环境清理'jiuzhao@Ubuntu ~$ virsh vol-delete test.qcow2 pool_testVol test.qcow2 deleted# 我这里省略了 --pool 和 --vol 同时推荐你去指定一下jiuzhao@Ubuntu ~$ virsh vol-delete --pool pool_test --vol vm-disk1-frontVol vm-disk1-front deletedjiuzhao@Ubuntu ~$ virsh vol-list pool_test Name Path----------------------------------------------------- vm-disk1 /home/jiuzhao/test/vm-disk1'只剩下一个磁盘文件,它是由libvirtd(root)创建的'
1)用 qemu-img 手动创建磁盘jiuzhao@Ubuntu ~$ cd /home/jiuzhao/test/jiuzhao@Ubuntu test$ qemu-img create -f qcow2 jiu.qcow2 10GFormatting 'jiu.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=10737418240 lazy_refcounts=off refcount_bits=16✅ qemu-img create 以当前用户直接创建jiuzhao@Ubuntu test$ ls -lh ./总计 392K-rw-r--r-- 1 jiuzhao jiuzhao 193K 7月 17 09:29 jiu.qcow2-rw------- 1 root root 193K 7月 16 20:37 vm-disk1# 这个virsh手动创建的磁盘属主为 jiuzhao
2)当前 libvirt 看不到这个磁盘jiuzhao@Ubuntu test$ virsh vol-list pool_test Name Path----------------------------------------- vm-disk1 /home/jiuzhao/test/vm-disk1# jiu.qcow2 不在列表中 ❌️ —— libvirtd 不会实时感知文件系统变化
3)刷新存储池jiuzhao@Ubuntu test$ virsh pool-refresh pool_testPool pool_test refreshed
4)再次查看 —— 已经在列表中了jiuzhao@Ubuntu test$ virsh vol-list pool_test Name Path--------------------------------------------- jiu.qcow2 /home/jiuzhao/test/test.qcow2 '✅️ 出现了!' vm-disk1 /home/jiuzhao/test/vm-disk1
5)删除卷(两种方式对比)# 方式A:rm + pool-refresh —— 等同于方式B,但多一步jiuzhao@Ubuntu test$ rm jiu.qcow2jiuzhao@Ubuntu test$ ls -lh ./总计 196K-rw------- 1 root root 193K 7月 16 20:37 vm-disk1# 只剩下一个磁盘文件jiuzhao@Ubuntu test$ virsh vol-list pool_test Name Path------------------------------------------- jiu.qcow2 /home/jiuzhao/test/jiu.qcow2 vm-disk1 /home/jiuzhao/test/vm-disk1'手动 rm 删除后,vol-list 条目信息(元数据)并不会立马更新'jiuzhao@Ubuntu test$ virsh pool-refresh pool_testPool pool_test refreshed# 手动刷新后,才更新过来jiuzhao@Ubuntu test$ virsh vol-list pool_test Name Path----------------------------------------- vm-disk1 /home/jiuzhao/test/vm-disk1
# 方式B:virsh vol-delete —— 删除文件 + 更新元数据,一步到位jiuzhao@Ubuntu test$ virsh vol-delete vm-disk1 pool_testVol vm-disk1 deleted'立马更新元数据'jiuzhao@Ubuntu test$ virsh vol-list pool_test Name Path--------------🈳📌 pool-refresh 总结:
| 场景 | pool-refresh 是否必要 |
|---|---|
pool-start 启动池 | ❌ 不需要(启动时自动 refresh) |
池运行期间 qemu-img create 创建新文件 | ✅ 需要 |
池运行期间 rm 删除文件 | ✅ 需要 |
virsh vol-create-as / virsh vol-delete | ❌ 不需要(自动更新元数据) |
| libvirtd 重启后 | ❌ 不需要(启动时自动 refresh 所有 active 池) |
- libvirtd 不启用 inotify 实时监控 pool 目录
- 以下行为均不会自动触发 vol-list 更新:
qemu-img create、touch、rm、cp - 唯一触发元数据同步的操作:
pool-refresh或pool-start
⚙️ XML 配置管理 (高级)
三种修改 XML 的方式
在 day02 中我们用
vim直接编辑 XML 文件,day03 介绍更优雅的方式
| 方式 | 命令 | 优点 | 缺点 |
|---|---|---|---|
| 直接 vim | vim /etc/libvirt/qemu/node1.xml | 最灵活 | 语法错误风险高、需手动生效 |
| virsh edit | virsh edit node1和 vim 是同一个底层配置文件 | 自动语法校验 | 还是手写 XML |
| virt-xml | virt-xml node1 --edit --memory 4096 | ==不用写 XML==,一行命令搞定 | 部分复杂场景仍需手写 |
1)vim编辑jiuzhao@Ubuntu ~$ sudo vim /etc/libvirt/qemu/node1.xml# 语法校验jiuzhao@Ubuntu ~$ sudo virt-xml-validate /etc/libvirt/qemu/node1.xml/etc/libvirt/qemu/node1.xml validates '✅️ 语法正确'
2)virsh editjiuzhao@Ubuntu ~$ virsh edit node1Domain 'node1' XML configuration not changed.内存管理
1)查看当前内存配置jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i memory <memory unit='KiB'>2097152</memory> '最大内存 2G' <currentMemory unit='KiB'>2097152</currentMemory> '当前内存 2G'
2)关机状态下修改最大内存jiuzhao@Ubuntu ~$ virsh domstate node1shut offjiuzhao@Ubuntu ~$ virsh setmaxmem node1 --size 1Gjiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i memory <memory unit='KiB'>1048576</memory> '最大内存 → 1G' <currentMemory unit='KiB'>1048576</currentMemory>
3)开机后动态调整当前内存(不能超过 maxmem)jiuzhao@Ubuntu ~$ virsh start node1Domain 'node1' startedjiuzhao@Ubuntu ~$ virsh domstate node1runningiuzhao@Ubuntu ~$ virsh setmem node1 --size 512M'运行时缩小到 512M ✅️'jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i memory <memory unit='KiB'>1048576</memory> '最大 还是 1G' <currentMemory unit='KiB'>524288</currentMemory> '当前 512M'# 错误示例jiuzhao@Ubuntu ~$ virsh setmem --size 2G node1'❌️ 超过 maxmem'error: invalid argument: cannot set memory higher than max memory⚠️ 关键区别:
| 命令 | 作用 | 运行时可用? |
|---|---|---|
virsh setmaxmem | 修改最大内存上限 | ❌️ 关机才能改 |
virsh setmem | 修改当前使用内存 | ✅️ 可以热调整 (但不能超 maxmem) |
CPU 管理
1)查看当前 vCPU 配置jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu <vcpu placement='static'>2</vcpu># 2核cpu,既是最大的也是当前的核数📌 参数区分:记一个简单规则就够了——就看 VM 当前是开机还是关机
VM 运行时 VM 关机时--live 立即生效,重启丢失 ✅ ❌ 报错(没在运行)--config 下次启动才生效 ✅ ✅ 修改配置文件--current 等于 --live 等于 --config--maximum 改的是上限,不是当前值💡 推荐 --live --config 一起用:立刻生效 + 持久化,重启不丢
⚠️ --current 不能和 --live / --config 同时使用,互斥
2)关机状态下修改最大 vCPUjiuzhao@Ubuntu ~$ virsh shutdown node1Domain 'node1' is being shutdownjiuzhao@Ubuntu ~$ virsh setvcpus node1 4 --config --maximum# 只修改最大限制jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu <vcpu placement='static' current='2'>4</vcpu># 最大 4 核,当前使用 2 核
3)修改当前使用的 vCPU 数量jiuzhao@Ubuntu ~$ virsh setvcpus node1 3 --currentjiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu <vcpu placement='static' current='3'>4</vcpu># 当前 3 核,最大 4 核
⚠️ 现在是关机状态, --curent == --configjiuzhao@Ubuntu ~$ virsh setvcpus node1 1 --config# 直接用 --config 也是修改的当前状态jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu <vcpu placement='static' current='1'>4</vcpu>关于热修改 vCPU:能否热增加/减少,看的是 hotpluggable 属性——由机型决定
'VM处于运行状态'# 当前状态:Q35 机型,max=4, current=1jiuzhao@Ubuntu ~$ virsh domstate node1runningjiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu <vcpu placement='static' current='1'>4</vcpu>jiuzhao@Ubuntu ~$ virsh setvcpus node1 3 --live# 当前的改为3jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu <vcpu placement='static' current='3'>4</vcpu> <vcpus> <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/> ← 0号永远不可热拔 <vcpu id='1' enabled='yes' hotpluggable='yes' order='2'/> ← 可热拔 <vcpu id='2' enabled='yes' hotpluggable='yes' order='3'/> ← 可热拔 <vcpu id='3' enabled='no' hotpluggable='yes'/> ← 可热增 </vcpus>
jiuzhao@Ubuntu ~$ virsh setvcpus node1 4 --live# ✅ 热增加 3→4:激活 id=3 (enabled='no' → 'yes')jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu <vcpu placement='static'>4</vcpu> <vcpus> <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/> <vcpu id='1' enabled='yes' hotpluggable='yes' order='2'/> <vcpu id='2' enabled='yes' hotpluggable='yes' order='3'/> <vcpu id='3' enabled='yes' hotpluggable='yes' order='4'/> </vcpus>
jiuzhao@Ubuntu ~$ virsh setvcpus node1 2 --live# ✅ 热减少 4→2:卸载 id=2 和 id=3 (hotpluggable='yes' → enabled='no')jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu <vcpu placement='static' current='2'>4</vcpu> <vcpus> <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/> <vcpu id='1' enabled='yes' hotpluggable='yes' order='2'/> <vcpu id='2' enabled='no' hotpluggable='yes'/> <vcpu id='3' enabled='no' hotpluggable='yes'/> </vcpus>
💡 规则很简单:hotpluggable='yes' + enabled='no' → 可以热增加 ✅hotpluggable='yes' + enabled='yes' → 可以热减少 ✅hotpluggable='no' + enabled='yes' → 不可热减少 ❌
4)查看 vCPU 数量jiuzhao@Ubuntu ~$ virsh vcpucount node1maximum config 4maximum live 4current config 1current live 2# 没有写入配置 --live 重启后失效
jiuzhao@Ubuntu ~$ virsh setvcpus node1 3 --live --config
jiuzhao@Ubuntu ~$ virsh vcpucount node1maximum config 4maximum live 4current config 3current live 3# 现在成功写入配置
jiuzhao@Ubuntu ~$ virsh shutdown node1Domain 'node1' is being shutdown# 关机状态下,只有configjiuzhao@Ubuntu ~$ virsh vcpucount node1maximum config 4current config 3🧱 热修改 vCPU 一句话:用 --live 可以在开机时调整 vCPU,但能否成功取决于机型和 XML 中 vCPU 的 hotpluggable 标记
入门阶段不用深究,关机改最省心
virt-xml 一行搞定
==virt-xml== 让你不用手写 XML 就能修改虚拟机配置
# 基本语法:virt-xml 虚拟机名称 动作 选项# 动作选一:--edit | --add-device | --remove-device | --build-xml(预览)
1)修改内存jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i memory <memory unit='KiB'>2097152</memory> <currentMemory unit='KiB'>1048576</currentMemory>jiuzhao@Ubuntu ~$ virt-xml node1 --edit --memory 4096域 'node1' 成功定义。jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i memory <memory unit='KiB'>4194304</memory> <currentMemory unit='KiB'>4194304</currentMemory># 一行命令,内存变成 4G ✅
2)修改 vCPUjiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu <vcpu placement='static'>2</vcpu>jiuzhao@Ubuntu ~$ virt-xml node1 --edit --vcpus 3域 'node1' 成功定义。jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu <vcpu placement='static'>3</vcpu>
3)修改网卡所属网络(default → my_net)jiuzhao@Ubuntu ~$ virsh domiflist node1 Interface Type Source Model MAC------------------------------------------------------------- - network default virtio 52:54:00:56:51:ba jiuzhao@Ubuntu ~$ virsh net-list Name State Autostart Persistent-------------------------------------------- default active yes yes my_net active yes yesjiuzhao@Ubuntu ~$ virt-xml node1 --edit 1 --network network=my_net,model=virtio# --edit 1 指第1块网卡# model=virtio 指定网络设备型号,我们用virtio性能更高一点# virtio 是专为虚拟化设计的“高速专线”,而 e1000e 是模拟真实硬件的“兼容模式”域 'node1' 成功定义。jiuzhao@Ubuntu ~$ virsh domiflist node1 Interface Type Source Model MAC------------------------------------------------------------ - network my_net virtio 52:54:00:56:51:ba# 从 default 切到了 my_net ✅
4)添加磁盘(自动创建磁盘文件 + 附加,一步到位)jiuzhao@Ubuntu ~$ virsh vol-list pool_test Name Path--------------🈳# 这个测试的存储池什么都没有jiuzhao@Ubuntu ~$ virsh domblklist node1 Target Source---------------------------------------------------- vda /home/jiuzhao/storage/images/node1.qcow2# 现在VM只有一块磁盘jiuzhao@Ubuntu ~$ virt-xml node1 --add-device \ --disk /home/jiuzhao/test/newdisk.qcow2,format=qcow2,size=10# 中间用逗号分隔正在分配 'newdisk.qcow2' | 0 B 00:00 ...域 'node1' 成功定义。jiuzhao@Ubuntu ~$ virsh domblklist node1 Target Source---------------------------------------------------- vda /home/jiuzhao/storage/images/node1.qcow2 sda /home/jiuzhao/test/newdisk.qcow2 '✅ 新增的磁盘'jiuzhao@Ubuntu ~$ virsh vol-list pool_test Name Path--------------------------------------------------- newdisk.qcow2 /home/jiuzhao/test/newdisk.qcow2# 存储池中也有对应的磁盘文件了
5)添加网卡jiuzhao@Ubuntu ~$ virt-xml node1 --add-device --network network=default,model=virtio域 'node1' 成功定义。jiuzhao@Ubuntu ~$ virsh domiflist node1 Interface Type Source Model MAC------------------------------------------------------------- - network my_net virtio 52:54:00:56:51:ba - network default virtio 52:54:00:48:d6:ad '✅ 新增的网卡'
6)移除设备(按编号或路径)jiuzhao@Ubuntu ~$ virt-xml node1 --remove-device --network 2# 按索引(第2块网卡) 或 路径(磁盘) 删除jiuzhao@Ubuntu ~$ virsh domiflist node1 Interface Type Source Model MAC------------------------------------------------------------ - network my_net virtio 52:54:00:56:51:ba# 只剩下一个jiuzhao@Ubuntu ~$ virt-xml node1 --remove-device --disk /home/jiuzhao/test/newdisk.qcow2jiuzhao@Ubuntu ~$ virsh domblklist node1 Target Source---------------------------------------------------- vda /home/jiuzhao/storage/images/node1.qcow2# ⚠️ 它虽然不再node1中,但是这个磁盘依旧在 pool_tes t这个池中jiuzhao@Ubuntu ~$ ls -lh ~/test/总计 1.8M-rw------- 1 root root 11G 7月 17 15:16 newdisk.qcow2jiuzhao@Ubuntu ~$ virsh vol-list pool_test Name Path--------------------------------------------------- newdisk.qcow2 /home/jiuzhao/test/newdisk.qcow2jiuzhao@Ubuntu ~$ virsh vol-delete --pool pool_test --vol newdisk.qcow2Vol newdisk.qcow2 deletedjiuzhao@Ubuntu ~$ virsh vol-list pool_test Name Path--------------🈳jiuzhao@Ubuntu ~$ ls -lh ~/test/总计 0
7) --build-xml 只输出不修改# 使用 --build-xml 时,不需要指定一个已存在的虚拟机名称作为参数# 它就是一个独立的模板生成器,用它来查看默认的 XML 模板⚠️ 这里给的是添加网络设备 & 添加磁盘的模板~# 主要用于 磁盘--disk 网络--networkjiuzhao@Ubuntu ~$ virt-xml --build-xml --disk size=10,format=qcow2<disk type="file" device="disk"> <driver name="qemu" type="qcow2" discard="unmap"/> <source file="/var/lib/libvirt/images/disk.qcow2"/> <target dev="hda" bus="ide"/></disk>jiuzhao@Ubuntu ~$ virt-xml --build-xml --network network=my_net,model=virtio<interface type="network"> <source network="my_net"/> <mac address="52:54:00:09:4e:69"/> <model type="virtio"/></interface>📌 四大动作速查:
| 动作 | 作用 | 示例 |
|---|---|---|
--edit | 修改配置 | --edit --memory 2048 |
--add-device | 添加设备 | --add-device --disk ... |
--remove-device | 移除设备 | --remove-device --disk /path |
--build-xml | 只输出 XML 不修改 | 预览用 |
🧱 virt-xml 改动的是持久化配置,关机状态直接生效,开机状态需加 --update 触发热插拔。
📸 快照与克隆
快照 (Snapshot)
==快照== 就是给虚拟机当前状态拍一张”照片”,以后随时可以回到这个状态
通俗解释:打游戏时存个档 —— 后面玩崩了就读档重来
1)创建快照# 运行中创建:捕获内存+磁盘完整状态jiuzhao@Ubuntu ~$ virsh snapshot-create-as node1 snap1Domain snapshot snap1 created# 关机状态也能创建:仅捕获磁盘状态 ✅ 推荐关机拍快照jiuzhao@Ubuntu ~$ virsh snapshot-create-as node1 snap2Domain snapshot snap2 created# snap1 → snap2 链式关系:snap2 基于 snap1
2)查看快照jiuzhao@Ubuntu ~$ virsh snapshot-list node1 Name Creation Time State---------------------------------------------- snap1 2026-07-17 16:50:38 +0800 running snap2 2026-07-17 16:50:39 +0800 shutoff # 最右边是VM的状态jiuzhao@Ubuntu ~$ virsh snapshot-list --tree node1snap1 | +- snap2 'snap2 基于 snap1'
3)恢复到 snap1jiuzhao@Ubuntu ~$ virsh snapshot-current node1 --namesnap2# 获取当前正在运行的快照名称jiuzhao@Ubuntu ~$ virsh snapshot-revert node1 snap1# 回滚(恢复)到名为 snap1 的快照状态jiuzhao@Ubuntu ~$ virsh snapshot-current node1 --namesnap1 '当前回到 snap1,snap2 仍在链中'jiuzhao@Ubuntu ~$ virsh snapshot-list --tree node1snap1 | +- snap2
4)删除快照jiuzhao@Ubuntu ~$ virsh snapshot-delete node1 snap2Domain snapshot snap2 deletedjiuzhao@Ubuntu ~$ virsh snapshot-list node1 Name Creation Time State---------------------------------------------- snap1 2026-07-17 16:50:38 +0800 runningjiuzhao@Ubuntu ~$ virsh snapshot-list --tree node1snap1# 只剩下一个快照了📌 快照命令速查:
| 操作 | 命令 |
|---|---|
| 创建 | virsh snapshot-create-as <VM> <快照名> |
| 查看列表 | virsh snapshot-list <VM> |
| 查看树 | virsh snapshot-list --tree <VM> |
| 恢复 | virsh snapshot-revert <VM> <快照名> |
| 当前快照 | virsh snapshot-current <VM> --name |
| 删除 | virsh snapshot-delete <VM> <快照名> |
🧱 **快照 **底层依赖 qcow2 的 COW 机制,每个快照增量存储,不复制全量数据
克隆 (Clone)
==克隆== 就是基于一台现有虚拟机制作一份完整副本,克隆体和原 VM 完全独立
1)自动克隆(libvirt 自动命名、自动分配磁盘)jiuzhao@Ubuntu ~$ virt-clone --original node1 --auto-clone正在分配 'node1-clone.qcow2'成功克隆 'node1-clone'--original node1:指定源虚拟机(被克隆的对象)- 必须是已关机或暂停状态的虚拟机
--auto-clone:自动克隆模式。它会自动完成以下三件事:- 自动生成新虚拟机名称(如
node1-clone) - 自动复制磁盘镜像文件并重命名
- 自动生成新的 UUID 和 MAC 地址(避免与原机冲突)
- 自动生成新虚拟机名称(如
💡 适用场景:快速创建一个和原机完全一样的测试环境,不需要自定义任何配置
2)查看克隆体jiuzhao@Ubuntu ~$ virsh list --all | grep node - node1 shut off - node1-clone shut offjiuzhao@Ubuntu ~$ virsh domblklist node1-clone Target Source---------------------------------------------------------- vda /home/jiuzhao/storage/images/node1-clone.qcow2jiuzhao@Ubuntu ~$ ls /etc/libvirt/qemu/ | grep nodenode1-clone.xmlnode1.xm
jiuzhao@Ubuntu ~$ du -sh /home/jiuzhao/storage/images/node1*95M node1.qcow2 '原 VM(精简置备)'1.9G node1-clone.qcow2 '克隆体(完整复制)'# 克隆是全量复制,不是 COW 前端盘,体积明显更大它把 Rocky.img 的全部基础数据 + node1.qcow2 的差异数据合并成一个独立的、不再依赖任何 backing file 的 qcow2
这就是”全量复制”的真正含义——复制的不是 95M,而是整个 chain 的全部有效数据
# 自定义名称 + 指定存储路径(最常用)virt-clone --original node1 \ --name node2 \ --file /home/jiuzhao/storage/images/node2.qcow2# 使用 virt-clone 命令(无论是 --auto-clone 还是手动指定 --name + --file)克隆出来的虚拟机,其 UUID 和 MAC 地址 都会由工具自动重新生成,绝不会与原虚拟机 node1 相同⚠️ 克隆 vs 前端盘:
| virt-clone | day02 前端盘 (COW) | |
|---|---|---|
| 磁盘占用 | 完整复制 ❌ | 增量共享 ✅ |
| 独立性 | 完全独立 ✅ | 依赖后端盘 |
| 场景 | 少量、完全隔离 | 批量快速部署 |
🐟 guestfish 和 guestmount
==libguestfs 工具集== 让你 ==不启动虚拟机就能操作磁盘内部==
在 day02 知识补充 中我们提到了这个工具集,day03 深入实战
📌 一句话:VM 死机了?照样能看日志、改配置、捞数据
| 工具 | 模式 | 适合场景 |
|---|---|---|
guestmount | 挂载模式 (类似 mount) | 浏览文件、结合宿主机命令操作 |
guestfish | 交互式 Shell | 文件操作、复杂定制 |
virt-customize | 一键定制 | 设置密码、安装软件包 |
🧱 为什么三个工具都需要 sudo?
guestmount / guestfish / virt-customize 底层都是启动一台微型辅助虚拟机来读写磁盘镜像,需要访问 KVM 硬件加速(/dev/kvm)→ 必须 root 或 kvm 组成员权限
⚠️ 操作期间原 VM 必须关机,否则磁盘被 QEMU 锁定,辅助虚拟机启动会崩溃
guestmount —— 把镜像挂载成目录
1)安装工具jiuzhao@Ubuntu ~$ sudo apt install -y libguestfs-tools
2)挂载镜像到目录(-a 指定磁盘, -i 自动检测并挂载分区)jiuzhao@Ubuntu ~$ mkdir /tmp/vmdirjiuzhao@Ubuntu ~$ sudo guestmount -a /home/jiuzhao/storage/images/node1.qcow2 -i /tmp/vmdir
jiuzhao@Ubuntu ~$ sudo ls /tmp/vmdir/afs boot etc lib media opt root sbin srv tmp varbin dev home lib64 mnt proc run server sys usr# 镜像内部的文件系统像普通目录一样展现在眼前
3)像操作普通目录一样操作镜像内部jiuzhao@Ubuntu ~$ echo "hello from guestmount" | sudo tee /tmp/vmdir/root/guestmount-test.txt > /dev/nulljiuzhao@Ubuntu ~$ sudo cat /tmp/vmdir/root/guestmount-test.txthello from guestmount💡 注意:guestmount 底层会创建临时 VM 来访问磁盘,卸载后自动删除
- 操作期间原 VM 不能运行(磁盘锁冲突)
jiuzhao@Ubuntu ~$ virsh start node1error: Failed to start domain 'node1' ❌
4)卸载jiuzhao@Ubuntu ~$ sudo guestunmount /tmp/vmdirjiuzhao@Ubuntu ~$ rm -rf /tmp/vmdirjiuzhao@Ubuntu ~$ virsh start node1Domain 'node1' startedjiuzhao@Ubuntu ~$ virsh console node1Connected to domain 'node1'Escape character is ^] (Ctrl + ])
Rocky login: rootPassword:[root@Rocky ~]# cat /root/guestmount-test.txthello from guestmountguestfish —— 交互式操作磁盘
1)进入 guestfishjiuzhao@Ubuntu ~$ sudo guestfish -i -a /home/jiuzhao/storage/images/node1.qcow2jiuzhao@Ubuntu ~$ sudo guestfish -i -d node1# -d 直接指定 VM 名,等价于 -a 写路径Welcome to guestfish📌 常用启动选项:
| 选项 | 作用 |
|---|---|
-i | 自动检测 OS + 自动挂载(进去就能操作目录) |
-a /path/to/disk | 指定磁盘镜像 |
--ro | 只读模式,安全操作首选 |
-d domain | 直接指定 libvirt 域名(替代 -a) |
📌 交互模式常用内置命令:
| 命令 | 作用 |
|---|---|
cat / ls / touch / rm | 和 shell 一样,操作镜像内文件 |
is-file /path | 是普通文件?返回 true/false |
is-dir /path | 是目录?返回 true/false |
write /path "内容" | 直接写入文本文件 |
upload 宿主机文件 镜像路径 | 从宿主机拷入 |
download 镜像路径 宿主机文件 | 从镜像拷出 |
command "dnf install ..." | 在镜像 OS 里执行系统命令 |
💡 记忆:Fish 内置命令直接操作磁盘文件(不需要 VM OS 配合),command 前缀的才是调用 VM 内部的 OS 命令
- Fish 内置命令:
cat、touch、upload(直接操作文件,不需要网络) - 系统命令:
command "dnf install ..."(需要command前缀,依赖网络环境)
2)Fish 内置命令——直接读写文件><fs> cat /etc/hostnameRocky><fs> is-file /root/demo.txtfalse><fs> upload /home/jiuzhao/桌面/Text.txt /root/demo.txt# 上传宿主机的文件至VM中><fs> is-file /root/demo.txttrue><fs> cat /root/demo.txt[!warning]关于......
3)退出><fs> exitcommand "vim ..."不能用 —— guestfish 不会给 vim 分配终端,会卡住- 要编辑文件请用 Fish 内置的
vi命令
- 要编辑文件请用 Fish 内置的
><fs> command "vim /root/demo.txt"^C^C ❌ # 会卡住><fs> vim /root/demo.txtvim: unknown command ❌><fs> vi /root/demo.txt ✅📌 guestfish 下载安装包在不同发行版上表现不一
- RHEL 系通常 OK(libguestfs 主力测试平台),Debian/Ubuntu 新内核容易崩
- 装包统一用
virt-customize --install更稳定
- 装包统一用
🔧 virt-customize —— 批量定制云镜像
virt-customize底层基于libguestfs,比guestfish更高层,专为”定制镜像”设计核心优势:不启动 VM 就能修改磁盘内部——设密码、装软件、写文件、执行脚本都行
📌 virt-customize 常用选项
| 选项 | 作用 |
|---|---|
-d node1 | 直接指定 VM 名(推荐,替代 -a,需关机) |
-a /path/to/disk | 指定磁盘镜像路径 |
--root-password password:xxx | 设置 root 密码 |
--install nginx | 安装软件包(需 --network) |
--network | 启用辅助虚拟机网络,装包必备 |
--upload local:remote | 上传文件到镜像 |
--run script.sh | 在镜像内执行脚本 |
--run-command '...' | 执行任意命令 |
--delete /path | 删除镜像内文件 |
--hostname node1 | 设置主机名 |
--firstboot-command '...' | 首次启动时执行命令 |
--selinux-relabel | 重新标记 SELinux 上下文(必记) |
⚠️ 执行 virt-customize 后如果无法登录,90% 是 SELinux 上下文问题
- 使用
--selinux-relabel参数即可解决 - 如果当时忘了,事后可以补跑:
virt-customize -a image.qcow2 --selinux-relabel
# 设置 root 密码 + 安装 nginx,一步到位jiuzhao@Ubuntu ~$ sudo virt-customize -d node1 \ --network \ --root-password password:passwd \ --install nginx \ --run-command 'systemctl enable nginx'# 可以多试几次,不太稳定的样子# 辅助虚拟机的网络本身就是间歇性的——第一次可能起不来,后面 OK 了就行[ 0.0] Examining the guest ...[ 13.9] Setting a random seed[ 13.9] Installing packages: nginx[ 14.9] Running: systemctl enable nginx[ 15.0] SELinux relabelling[ 15.1] Finishing off
# 启动 VM 验证jiuzhao@Ubuntu ~$ virsh start node1jiuzhao@Ubuntu ~$ ssh root@10.0.0.2 'systemctl status nginx | head -4'● nginx.service - The nginx HTTP and reverse proxy server Active: active (running)常用操作
1)设置 root 密码(最常用)virt-customize -a /home/jiuzhao/storage/base/rocky10.qcow2 \ --root-password password:你的密码
2)安装软件包virt-customize -a /home/jiuzhao/storage/base/rocky10.qcow2 \ --install qemu-guest-agent,cloud-init,vim-enhanced
3)上传文件到镜像内virt-customize -a rocky10.qcow2 \ --upload /home/my.repo:/etc/yum.repos.d/
4)在镜像内执行脚本virt-customize -a rocky10.qcow2 \ --run /home/base_config.sh# --run 的参数是宿主机路径,不是客户机内部路径# 它会自动把宿主机上这个脚本拷进客户机并执行,不需要先 --upload
5)删除镜像内文件virt-customize -a rocky10.qcow2 \ --delete /root/anaconda-ks.cfg
6)综合定制virt-customize -a /home/jiuzhao/storage/base/rocky10.qcow2 \ --root-password password:1 \ --install qemu-guest-agent,cloud-init,vim-enhanced,net-tools \ --run /server/sh/Rocky_base_config.sh \ --selinux-relabel# --selinux-relabel: 修改文件后重新标记 SELinux 上下文# 不加这个,重启后可能因 SELinux 无法登录📌 三件套对比:
| 对比维度 | guestmount | guestfish | virt-customize |
|---|---|---|---|
| 使用方式 | 挂载成目录 | 交互式 Shell | 一行命令 |
| 适合场景 | 浏览/拷贝文件 | 文件级操作 | 密码/软件包/批量定制 |
| 需要网络 | ❌ | 仅 command 时需要 | --install 时需要 |
| VM 必须关机 | ✅ | ✅ | ✅ |
🌐 网络实战
回顾 day01:
default网络使用 NAT 模式,虚拟机通过virbr0上网,外面无法直接访问libvirt 的网络分两层:virsh 管理的虚拟网络 和 VM 直接绑物理网卡,它们不是一回事
📌 关键区分:
type='network'→ 连接到 virsh 管理的虚拟网络,有 DHCP/DNS/防火墙type='direct'→ 绕过 virsh 网络层,直接绑物理网卡 ——底层就是 Macvtaptype='bridge'→ 连接到 nmcli 手工创建的 Linux 网桥,不走 virsh 网络
1. Macvtap —— 直连物理网卡
Macvtap 让 VM 网卡直接”插”在物理网卡上,获取同网段 IP,性能极高
# ① 当前网卡是 default (NAT)jiuzhao@Ubuntu ~$ virsh domiflist node1 Interface Type Source Model MAC------------------------------------------------------------- - network default virtio 52:54:00:e5:64:a7
# ② 添加一张新 Macvtap 网卡(不改原网卡,原网卡是 manual 静态 IP)jiuzhao@Ubuntu ~$ virt-xml node1 --add-device \ --network type=direct,source=wlp4s0,source.mode=bridge,model=virtioDomain 'node1' defined successfully.
jiuzhao@Ubuntu ~$ virsh domiflist node1 Interface Type Source Model MAC------------------------------------------------------------- - network default virtio 52:54:00:e5:64:a7 - direct wlp4s0 virtio 52:54:00:0d:fa:ca ← '新增 Macvtap'jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -A 3 "direct" <interface type='direct'> # type='direct' = Macvtap <mac address='52:54:00:65:ba:fe'/> <source dev='wlp4s0' mode='bridge'/> <model type='virtio'/>🧱 Macvtap 两大坑:
- WiFi 不通:多 MAC 被 AP 拒绝,DHCP/手动 IP/VM 间互访全失败
- Windows 上 VMware 桥接在 WiFi 下能用,是因为 VMware 驱动做了 MAC 伪装,KVM Macvtap 没有这一层 hack
- 有线网卡 OK:eth/ens/eno 等有线接口上 Macvtap 完全正常,DHCP 拿 IP、同网段互通都没问题
- 宿主机互访不通:Macvtap 流量不经过内核协议栈,⚠️ VM ↔ 宿主机 ping 不通 ⚠️
# ③ 启动 → eth1 只有 IPv6,没有 IPv4jiuzhao@Ubuntu ~$ ssh root@10.0.0.2 'ip -br a show eth1'eth1 UP fe80::ddf9:5d28:1841:e1ee/64 '无 IPv4 ← DHCP 失败'# 这里如果是要获取具体的IP的话还得是有线连接才行~
# ④ 清理jiuzhao@Ubuntu ~$ virsh destroy node1jiuzhao@Ubuntu ~$ virt-xml node1 --remove-device --network 22. NAT 网络 —— XML 创建
libvirt 支持的网络模式通过
<forward>元素控制
| 模式 | XML | 说明 |
|---|---|---|
| NAT | <forward mode='nat'/> | 默认,VM 通过宿主机 NAT 上网 |
| Bridge | <forward mode='bridge'/> | 桥接,VM 直接暴露到物理网络(几乎不用) |
| Isolated | 不写 <forward> | 完全隔离,VM 之间能通,不能出外网 |
| Route | <forward mode='route'/> | 路由转发(不做 NAT) |
| Open | <forward mode='open'/> | 类似 Isolated 但无防火墙限制 |
# === 定义 NAT 网络 XML ===jiuzhao@Ubuntu ~$ cat > nat-demo.xml <<'XML'<network> <name>nat-demo</name> <forward mode='nat'/> <bridge name='virbr9' stp='on' delay='0'/> <ip address='10.9.9.1' netmask='255.255.255.0'> <dhcp> <range start='10.9.9.2' end='10.9.9.254'/> </dhcp> </ip></network>XML
# === net-define:持久化(推荐) ===# 之前 Day02 里 virsh net-define /path/to/net.xml 就是这条路jiuzhao@Ubuntu ~$ virsh net-define nat-demo.xmljiuzhao@Ubuntu ~$ virsh net-start nat-demojiuzhao@Ubuntu ~$ virsh net-autostart nat-demojiuzhao@Ubuntu ~$ virsh net-list --all | grep nat-demo nat-demo active yes yes ← Persistent ✅📌 net-define vs net-create:
net-define 写配置到 /etc/libvirt/qemu/networks/ 持久化,net-create 只在内存中、stop 即消失
# === net-create:临时(stop 就消失,无持久化文件) ===jiuzhao@Ubuntu ~$ virsh net-create nat-demo.xmljiuzhao@Ubuntu ~$ virsh net-list --all | grep nat-demo nat-demo active no no ← Persistent ❌
# === 清理 ===jiuzhao@Ubuntu ~$ virsh net-destroy nat-demojiuzhao@Ubuntu ~$ virsh net-undefine nat-demo3. 传统桥接 (nmcli)
有线网卡上可用 nmcli 创建 Linux Bridge,VM 和宿主机同网段互通
# === nmcli 三步建桥(⚠️ 仅支持有线网卡,WiFi 不行) ===[root@vhost1 ~]# ip a s ens16124: ens161: <BROADCAST,MULTICAST,UP,LOWER_UP> ... link/ether 00:0c:29:8d:09:0c ...# 这是我们的物理网卡,后面要作为 br0 的 port[root@vhost1 ~]# nmcli connection add type bridge ifname br0 con-name br0[root@vhost1 ~]# nmcli connection add type ethernet port-type bridge \ ifname ens161 con-name br0-ens161 controller br0[root@vhost1 ~]# nmcli connection up br0
# 验证 br0 获取到了教室网段的 IP[root@vhost1 ~]# ip a s br0 inet 10.2.35.218/22 ... '教室网段 IP ✅'
# === VM 切到桥接 ===[root@vhost1 ~]# virt-xml test --edit --network bridge=br0,model=virtio[root@vhost1 ~]# virsh domiflist test Interface Type Source Model MAC - bridge br0 virtio 52:54:00:48:da:9d# Type 从 network 变成 bridge ✅
# 开机验证[root@vhost1 ~]# virsh start test[root@vhost1 ~]# virsh console test[root@localhost ~]# ip a s2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> ... inet 10.2.33.2/22 ... '获取到了教室网段 IP ✅️'# 此时跨节点的虚拟机之间可以直接通信!🧱 真实环境实测 —— 宿主机只有 WiFi,没插网线时
我这边宿主机网卡现状:
jiuzhao@Ubuntu ~$ ip -br addreno1 DOWN '❌ 有线网卡没插线 (NO-CARRIER)'wlp4s0 UP 192.168.18.16/24 '✅ 唯一在线:WiFi'这种环境想给 VM 直连物理网卡,两个方案都做不了:
| 尝试方案 | 现象 | 结论 |
|---|---|---|
| nmcli 传统桥接 | eno1 Link detected: no,NM 状态 unavailable,建桥没物理口可绑 | ❌ 必须有线 |
| Macvtap 绑 wlp4s0 | 网卡 UP 有 carrier,但 tcpdump 0 packets,ping 网关 Destination Host Unreachable | ❌ AP 拒绝多 MAC |
4. 三种模式一图流速查
| 特性 | 传统桥接 (Bridge) | Macvtap | NAT (default) |
|---|---|---|---|
| IP 网段 | 宿主机同一网段 | 宿主机同一网段 | 私有子网(不同网段) |
| 外网访问虚拟机 | ✅ 可直接访问 | ✅ 可直接访问 | ❌ 需端口转发 |
| 虚拟机上网 | ✅ | ✅ | ✅ 通过宿主机转发 |
| 与宿主机互访 | ✅ 畅通无阻 | ❌ 天生隔离 | ✅ 畅通无阻 |
| 网络性能 | 较高(约物理 90%) | 极高(接近物理 99%) | 较高(有 NAT 转发损耗) |
| 配置难度 | 中等(需改网卡配置) | 简单(一行 virt-xml) | 最简单(默认即用) |
| 有线/WiFi | 仅有线 ❗ | 有线可靠,WiFi 看 AP | 均支持 ✅ |
💡 抉择一句话:开发测试用 NAT → 需要局域网互访用 Bridge → 追求性能用 Macvtap(有线)
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!















