存储管理与快照迁移

8002 字
40 分钟
存储管理与快照迁移
存储管理与快照迁移

存储管理 && 快照迁移 && 高级运维#

[TOC]


🧱 知识点总结#

Note

在上篇笔记📚中,我们用 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-aslibvirt 代为创建,一步到位libvirt 全可见 ✅️
Warning

核心事实:无论哪种方式创建,只要文件在池目录里且做过 refresh,libvirt 都能完整管理(vol-list / vol-info / vol-delete 均可用)


📦 存储池 (Storage Pool)#

==存储池== 就是 libvirt 管理的”仓库”,虚拟机磁盘、ISO 镜像都放在池子里

存储池类型#

类型说明适用场景
dir目录型,最常用本地磁盘镜像存放
lvm基于 LVM 卷组需要快照、高性能的场景
nfs基于 NFS 共享共享存储、迁移场景

💡 这里我们以 ==dir 目录型== 为重点,生产环境中 LVM 和 NFS 同样重要

创建目录型存储池#

Terminal window
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.iso
jiuzhao@Ubuntu ~$ virsh pool-refresh img
Pool default refreshed
# 手动同步和更新存储池信息
Note

📌 关于 default 池(默认存储池)

  • pool-refresh 后,目录里的 .qcow2 / .iso 等镜像文件会被识别为存储卷
  • pool-start(启动池)会隐式执行一次 refresh
  • ⚠️ libvirtd 不会自动实时同步,新增/删除文件后需手动 refresh
Terminal window
2)定义存储池
'我们先手动创建目标路径'
jiuzhao@Ubuntu ~$ mkdir ~/test
jiuzhao@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_test
Pool pool_test started
4)设置开机自启
jiuzhao@Ubuntu ~$ virsh pool-autostart pool_test
Pool pool_test marked as autostarted
jiuzhao@Ubuntu qemu$ virsh pool-list --all | grep test
pool_test active✅ yes✅
5)查看存储池详细信息
jiuzhao@Ubuntu ~$ virsh pool-info pool_test
Name: pool_test
UUID: 780fc28c-419c-41fe-adc2-650697d9161b
State: running '✅️ 活跃状态'
Persistent: yes '✅️ 持久化'
Autostart: yes '✅️ 开机自启'
Capacity: 732.48 GiB '总容量'
Allocation: 88.91 GiB '已分配'
Available: 643.57 GiB '可用空间'

💡 类比对照:存储池的操作和上篇笔记 ==虚拟网络== 的操作几乎一模一样

操作虚拟网络存储池
定义virsh net-definevirsh pool-define-as
启动virsh net-startvirsh pool-start
自启virsh net-autostartvirsh pool-autostart
查看virsh net-list --allvirsh pool-list --all
停止virsh net-destroyvirsh pool-destroy
删除virsh net-undefinevirsh pool-undefine

📦 存储卷 (Storage Volume)#

==存储卷== 是存储池中的具体”货物” —— 一个个虚拟磁盘文件

通俗解释:池子是仓库,卷就是仓库里的每个箱子 (磁盘镜像)

🧱 与 day02 的联动:

Note

两种方式的本质区别不在”受管/不受管”,而在:

  • qemu-img create:直接操作文件系统 → 需 pool-refresh 同步到 libvirt
  • virsh vol-create-as:libvirt 代劳创建 → 自动完成”创建 + 登记”
  • refresh 之后,两者在 libvirt 中完全等价(vol-list、vol-info、vol-delete 均可用)

📌 完整命令对照:

Terminal window
# ===== 创建独立磁盘 =====
# day02 方式:qemu-img + pool-refresh
qemu-img create -f qcow2 /home/jiuzhao/test/node.img 15G
virsh 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-refresh
qemu-img create -f qcow2 \
-b /home/jiuzhao/test/node.img \
-F qcow2 \
/home/jiuzhao/test/node-front.qcow2 \
20G
virsh 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-refresh
rm /home/jiuzhao/test/node.img
virsh pool-refresh pool_test # 必须手动同步!
# day03 方式:一步到位
virsh vol-delete vm-disk1 --pool pool_test

创建存储卷#

Terminal window
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 qcow2
Vol vm-disk1 created
# 由 libvirtd (root) 代理创建
3)验证卷已创建
jiuzhao@Ubuntu ~$ virsh vol-list pool_test
Name Path
-----------------------------------------
vm-disk1 /home/jiuzhao/test/vm-disk1
jiuzhao@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-disk1
image: /home/jiuzhao/test/vm-disk1
file format: qcow2
virtual 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-disk1
Name: vm-disk1
Type: file
Capacity: 18.63 GiB
Allocation: 196.00 KiB

📌 virsh vol-create-as 参数速查:

参数含义
--pool目标存储池名称
--name卷名称
--capacity容量 (支持 G/M/K 单位)
--format格式 (qcow2 / raw)
--backing-vol后端盘名称 (创建前端盘时用)
--backing-vol-format后端盘格式
Note

📌 关于文件后缀:磁盘文件的格式由**文件头部魔数(magic bytes)**决定,与后缀名无关

写成 .qcow2、.img、.disk 甚至不加后缀,qemu-img info 都能正确识别

Terminal window
jiuzhao@Ubuntu ~$ sudo qemu-img info ~/test/vm-disk1 | grep 'file format'
file format: qcow2

不过约定俗成地用 .qcow2 / .raw 后缀,方便人眼辨认

使用 virsh 创建前端盘#

回顾 day02 COW 概念:前端盘依赖后端盘,只存差异数据

Terminal window
jiuzhao@Ubuntu ~$ cd test/
jiuzhao@Ubuntu test$ ls
test.qcow2 vm-disk1
jiuzhao@Ubuntu test$ virsh vol-create-as \
--pool pool_test \
--name vm-disk1-front \
--capacity 30G \
--format qcow2 \
--backing-vol vm-disk1 \
--backing-vol-format qcow2
Vol vm-disk1-front created
jiuzhao@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-front
image: /home/jiuzhao/test/vm-disk1-front
file format: qcow2
virtual size: 30 GiB (32212254720 bytes)
disk size: 196 KiB
backing 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
Note

✅️ 与 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 的”登记表”和磁盘文件系统对齐而已

Terminal window
'环境清理'
jiuzhao@Ubuntu ~$ virsh vol-delete test.qcow2 pool_test
Vol test.qcow2 deleted
# 我这里省略了 --pool 和 --vol 同时推荐你去指定一下
jiuzhao@Ubuntu ~$ virsh vol-delete --pool pool_test --vol vm-disk1-front
Vol vm-disk1-front deleted
jiuzhao@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 10G
Formatting '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_test
Pool 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.qcow2
jiuzhao@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_test
Pool 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_test
Vol 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 池)
Note
  • libvirtd 不启用 inotify 实时监控 pool 目录
  • 以下行为均不会自动触发 vol-list 更新:qemu-img create、touch、rm、cp
  • 唯一触发元数据同步的操作:pool-refresh 或 pool-start

⚙️ XML 配置管理 (高级)#

三种修改 XML 的方式#

在 day02 中我们用 vim 直接编辑 XML 文件,day03 介绍更优雅的方式

方式命令优点缺点
直接 vimvim /etc/libvirt/qemu/node1.xml最灵活语法错误风险高、需手动生效
virsh editvirsh edit node1
和 vim 是同一个底层配置文件
自动语法校验还是手写 XML
virt-xmlvirt-xml node1 --edit --memory 4096==不用写 XML==,一行命令搞定部分复杂场景仍需手写
Terminal window
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 edit
jiuzhao@Ubuntu ~$ virsh edit node1
Domain 'node1' XML configuration not changed.

内存管理#

Terminal window
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 node1
shut off
jiuzhao@Ubuntu ~$ virsh setmaxmem node1 --size 1G
jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i memory
<memory unit='KiB'>1048576</memory> '最大内存 → 1G'
<currentMemory unit='KiB'>1048576</currentMemory>
3)开机后动态调整当前内存(不能超过 maxmem)
jiuzhao@Ubuntu ~$ virsh start node1
Domain 'node1' started
jiuzhao@Ubuntu ~$ virsh domstate node1
running
iuzhao@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 管理#

Terminal window
1)查看当前 vCPU 配置
jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu
<vcpu placement='static'>2</vcpu>
# 2核cpu,既是最大的也是当前的核数

📌 参数区分:记一个简单规则就够了——就看 VM 当前是开机还是关机

Terminal window
VM 运行时 VM 关机时
--live 立即生效,重启丢失 ✅ ❌ 报错(没在运行)
--config 下次启动才生效 ✅ ✅ 修改配置文件
--current 等于 --live 等于 --config
--maximum 改的是上限,不是当前值
Tip

💡 推荐 --live --config 一起用:立刻生效 + 持久化,重启不丢

⚠️ --current 不能和 --live / --config 同时使用,互斥

Terminal window
2)关机状态下修改最大 vCPU
jiuzhao@Ubuntu ~$ virsh shutdown node1
Domain 'node1' is being shutdown
jiuzhao@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 --current
jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu
<vcpu placement='static' current='3'>4</vcpu>
# 当前 3 核,最大 4 核
⚠️ 现在是关机状态, --curent == --config
jiuzhao@Ubuntu ~$ virsh setvcpus node1 1 --config
# 直接用 --config 也是修改的当前状态
jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu
<vcpu placement='static' current='1'>4</vcpu>

Warning

关于热修改 vCPU:能否热增加/减少,看的是 hotpluggable 属性——由机型决定

Terminal window
'VM处于运行状态'
# 当前状态:Q35 机型,max=4, current=1
jiuzhao@Ubuntu ~$ virsh domstate node1
running
jiuzhao@Ubuntu ~$ virsh dumpxml node1 | grep -i vcpu
<vcpu placement='static' current='1'>4</vcpu>
jiuzhao@Ubuntu ~$ virsh setvcpus node1 3 --live
# 当前的改为3
jiuzhao@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 node1
maximum config 4
maximum live 4
current config 1
current live 2
# 没有写入配置 --live 重启后失效
jiuzhao@Ubuntu ~$ virsh setvcpus node1 3 --live --config
jiuzhao@Ubuntu ~$ virsh vcpucount node1
maximum config 4
maximum live 4
current config 3
current live 3
# 现在成功写入配置
jiuzhao@Ubuntu ~$ virsh shutdown node1
Domain 'node1' is being shutdown
# 关机状态下,只有config
jiuzhao@Ubuntu ~$ virsh vcpucount node1
maximum config 4
current config 3
Note

🧱 热修改 vCPU 一句话:用 --live 可以在开机时调整 vCPU,但能否成功取决于机型和 XML 中 vCPU 的 hotpluggable 标记

入门阶段不用深究,关机改最省心

virt-xml 一行搞定#

==virt-xml== 让你不用手写 XML 就能修改虚拟机配置

Terminal window
# 基本语法: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)修改 vCPU
jiuzhao@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 yes
jiuzhao@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.qcow2
jiuzhao@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.qcow2
jiuzhao@Ubuntu ~$ virsh vol-list pool_test
Name Path
---------------------------------------------------
newdisk.qcow2 /home/jiuzhao/test/newdisk.qcow2
jiuzhao@Ubuntu ~$ virsh vol-delete --pool pool_test --vol newdisk.qcow2
Vol newdisk.qcow2 deleted
jiuzhao@Ubuntu ~$ virsh vol-list pool_test
Name Path
--------------
🈳
jiuzhao@Ubuntu ~$ ls -lh ~/test/
总计 0
7) --build-xml 只输出不修改
# 使用 --build-xml 时,不需要指定一个已存在的虚拟机名称作为参数
# 它就是一个独立的模板生成器,用它来查看默认的 XML 模板
⚠️ 这里给的是添加网络设备 & 添加磁盘的模板~
# 主要用于 磁盘--disk 网络--network
jiuzhao@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 不修改预览用
Note

🧱 virt-xml 改动的是持久化配置,关机状态直接生效,开机状态需加 --update 触发热插拔。


📸 快照与克隆#

快照 (Snapshot)#

==快照== 就是给虚拟机当前状态拍一张”照片”,以后随时可以回到这个状态

通俗解释:打游戏时存个档 —— 后面玩崩了就读档重来

Terminal window
1)创建快照
# 运行中创建:捕获内存+磁盘完整状态
jiuzhao@Ubuntu ~$ virsh snapshot-create-as node1 snap1
Domain snapshot snap1 created
# 关机状态也能创建:仅捕获磁盘状态 ✅ 推荐关机拍快照
jiuzhao@Ubuntu ~$ virsh snapshot-create-as node1 snap2
Domain 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 node1
snap1
|
+- snap2 'snap2 基于 snap1'
3)恢复到 snap1
jiuzhao@Ubuntu ~$ virsh snapshot-current node1 --name
snap2
# 获取当前正在运行的快照名称
jiuzhao@Ubuntu ~$ virsh snapshot-revert node1 snap1
# 回滚(恢复)到名为 snap1 的快照状态
jiuzhao@Ubuntu ~$ virsh snapshot-current node1 --name
snap1 '当前回到 snap1,snap2 仍在链中'
jiuzhao@Ubuntu ~$ virsh snapshot-list --tree node1
snap1
|
+- snap2
4)删除快照
jiuzhao@Ubuntu ~$ virsh snapshot-delete node1 snap2
Domain snapshot snap2 deleted
jiuzhao@Ubuntu ~$ virsh snapshot-list node1
Name Creation Time State
----------------------------------------------
snap1 2026-07-17 16:50:38 +0800 running
jiuzhao@Ubuntu ~$ virsh snapshot-list --tree node1
snap1
# 只剩下一个快照了

📌 快照命令速查:

操作命令
创建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> <快照名>
Note

🧱 **快照 **底层依赖 qcow2 的 COW 机制,每个快照增量存储,不复制全量数据

克隆 (Clone)#

==克隆== 就是基于一台现有虚拟机制作一份完整副本,克隆体和原 VM 完全独立

Terminal window
1)自动克隆(libvirt 自动命名、自动分配磁盘)
jiuzhao@Ubuntu ~$ virt-clone --original node1 --auto-clone
正在分配 'node1-clone.qcow2'
成功克隆 'node1-clone'
  • --original node1:指定源虚拟机(被克隆的对象)
    • 必须是已关机或暂停状态的虚拟机
  • --auto-clone:自动克隆模式。它会自动完成以下三件事:
    • 自动生成新虚拟机名称(如 node1-clone)
    • 自动复制磁盘镜像文件并重命名
    • 自动生成新的 UUID 和 MAC 地址(避免与原机冲突)

💡 适用场景:快速创建一个和原机完全一样的测试环境,不需要自定义任何配置

Terminal window
2)查看克隆体
jiuzhao@Ubuntu ~$ virsh list --all | grep node
- node1 shut off
- node1-clone shut off
jiuzhao@Ubuntu ~$ virsh domblklist node1-clone
Target Source
----------------------------------------------------------
vda /home/jiuzhao/storage/images/node1-clone.qcow2
jiuzhao@Ubuntu ~$ ls /etc/libvirt/qemu/ | grep node
node1-clone.xml
node1.xm
jiuzhao@Ubuntu ~$ du -sh /home/jiuzhao/storage/images/node1*
95M node1.qcow2 '原 VM(精简置备)'
1.9G node1-clone.qcow2 '克隆体(完整复制)'
# 克隆是全量复制,不是 COW 前端盘,体积明显更大
Tip

它把 Rocky.img 的全部基础数据 + node1.qcow2 的差异数据合并成一个独立的、不再依赖任何 backing file 的 qcow2

这就是”全量复制”的真正含义——复制的不是 95M,而是整个 chain 的全部有效数据

Terminal window
# 自定义名称 + 指定存储路径(最常用)
virt-clone --original node1 \
--name node2 \
--file /home/jiuzhao/storage/images/node2.qcow2
# 使用 virt-clone 命令(无论是 --auto-clone 还是手动指定 --name + --file)克隆出来的虚拟机,其 UUID 和 MAC 地址 都会由工具自动重新生成,绝不会与原虚拟机 node1 相同

⚠️ 克隆 vs 前端盘:

virt-cloneday02 前端盘 (COW)
磁盘占用完整复制 ❌增量共享 ✅
独立性完全独立 ✅依赖后端盘
场景少量、完全隔离批量快速部署

🐟 guestfish 和 guestmount#

==libguestfs 工具集== 让你 ==不启动虚拟机就能操作磁盘内部==

在 day02 知识补充 中我们提到了这个工具集,day03 深入实战

📌 一句话:VM 死机了?照样能看日志、改配置、捞数据

工具模式适合场景
guestmount挂载模式 (类似 mount)浏览文件、结合宿主机命令操作
guestfish交互式 Shell文件操作、复杂定制
virt-customize一键定制设置密码、安装软件包
Note

🧱 为什么三个工具都需要 sudo?

guestmount / guestfish / virt-customize 底层都是启动一台微型辅助虚拟机来读写磁盘镜像,需要访问 KVM 硬件加速(/dev/kvm)→ 必须 root 或 kvm 组成员权限

⚠️ 操作期间原 VM 必须关机,否则磁盘被 QEMU 锁定,辅助虚拟机启动会崩溃

guestmount —— 把镜像挂载成目录#

Terminal window
1)安装工具
jiuzhao@Ubuntu ~$ sudo apt install -y libguestfs-tools
2)挂载镜像到目录(-a 指定磁盘, -i 自动检测并挂载分区)
jiuzhao@Ubuntu ~$ mkdir /tmp/vmdir
jiuzhao@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 var
bin 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/null
jiuzhao@Ubuntu ~$ sudo cat /tmp/vmdir/root/guestmount-test.txt
hello from guestmount
Tip

💡 注意:guestmount 底层会创建临时 VM 来访问磁盘,卸载后自动删除

  • 操作期间原 VM 不能运行(磁盘锁冲突)
Terminal window
jiuzhao@Ubuntu ~$ virsh start node1
error: Failed to start domain 'node1' ❌
4)卸载
jiuzhao@Ubuntu ~$ sudo guestunmount /tmp/vmdir
jiuzhao@Ubuntu ~$ rm -rf /tmp/vmdir
jiuzhao@Ubuntu ~$ virsh start node1
Domain 'node1' started
jiuzhao@Ubuntu ~$ virsh console node1
Connected to domain 'node1'
Escape character is ^] (Ctrl + ])
Rocky login: root
Password:
[root@Rocky ~]# cat /root/guestmount-test.txt
hello from guestmount

guestfish —— 交互式操作磁盘#

Terminal window
1)进入 guestfish
jiuzhao@Ubuntu ~$ sudo guestfish -i -a /home/jiuzhao/storage/images/node1.qcow2
jiuzhao@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 里执行系统命令
Tip

💡 记忆:Fish 内置命令直接操作磁盘文件(不需要 VM OS 配合),command 前缀的才是调用 VM 内部的 OS 命令

  • Fish 内置命令:cat、touch、upload(直接操作文件,不需要网络)
  • 系统命令:command "dnf install ..."(需要 command 前缀,依赖网络环境)
Terminal window
2)Fish 内置命令——直接读写文件
><fs> cat /etc/hostname
Rocky
><fs> is-file /root/demo.txt
false
><fs> upload /home/jiuzhao/桌面/Text.txt /root/demo.txt
# 上传宿主机的文件至VM中
><fs> is-file /root/demo.txt
true
><fs> cat /root/demo.txt
[!warning]
关于......
3)退出
><fs> exit
Warning
  • command "vim ..." 不能用 —— guestfish 不会给 vim 分配终端,会卡住
    • 要编辑文件请用 Fish 内置的 vi 命令
Terminal window
><fs> command "vim /root/demo.txt"
^C^C ❌ # 会卡住
><fs> vim /root/demo.txt
vim: unknown command ❌
><fs> vi /root/demo.txt ✅
Note

📌 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 上下文(必记)
Warning

⚠️ 执行 virt-customize 后如果无法登录,90% 是 SELinux 上下文问题

  • 使用 --selinux-relabel 参数即可解决
  • 如果当时忘了,事后可以补跑:virt-customize -a image.qcow2 --selinux-relabel
Terminal window
# 设置 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 node1
jiuzhao@Ubuntu ~$ ssh root@10.0.0.2 'systemctl status nginx | head -4'
● nginx.service - The nginx HTTP and reverse proxy server
Active: active (running)

常用操作

Terminal window
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 无法登录

📌 三件套对比:

对比维度guestmountguestfishvirt-customize
使用方式挂载成目录交互式 Shell一行命令
适合场景浏览/拷贝文件文件级操作密码/软件包/批量定制
需要网络❌仅 command 时需要--install 时需要
VM 必须关机✅✅✅

🌐 网络实战#

回顾 day01:default 网络使用 NAT 模式,虚拟机通过 virbr0 上网,外面无法直接访问

libvirt 的网络分两层:virsh 管理的虚拟网络 和 VM 直接绑物理网卡,它们不是一回事

virsh 虚拟网络 (net-define/net-create)

VM 网卡 (interface)

连接到

连接到

直连物理网卡 Macvtap

连 nmcli 创建的 br0

NAT (default) - 躲在宿主机后上网

Isolated - 完全断外网

Bridge - 桥接到物理网络

物理网卡 (wlp4s0/eno1)

Linux Bridge (br0)

virsh 虚拟网络 (net-define/net-create)

VM 网卡 (interface)

连接到

连接到

直连物理网卡 Macvtap

连 nmcli 创建的 br0

NAT (default) - 躲在宿主机后上网

Isolated - 完全断外网

Bridge - 桥接到物理网络

物理网卡 (wlp4s0/eno1)

Linux Bridge (br0)

📌 关键区分:

  • type='network' → 连接到 virsh 管理的虚拟网络,有 DHCP/DNS/防火墙
  • type='direct' → 绕过 virsh 网络层,直接绑物理网卡 ——底层就是 Macvtap
  • type='bridge' → 连接到 nmcli 手工创建的 Linux 网桥,不走 virsh 网络

1. Macvtap —— 直连物理网卡#

Macvtap 让 VM 网卡直接”插”在物理网卡上,获取同网段 IP,性能极高

Terminal window
# ① 当前网卡是 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=virtio
Domain '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'/>
Warning

🧱 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 不通 ⚠️
Terminal window
# ③ 启动 → eth1 只有 IPv6,没有 IPv4
jiuzhao@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 node1
jiuzhao@Ubuntu ~$ virt-xml node1 --remove-device --network 2

2. 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 但无防火墙限制
Terminal window
# === 定义 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.xml
jiuzhao@Ubuntu ~$ virsh net-start nat-demo
jiuzhao@Ubuntu ~$ virsh net-autostart nat-demo
jiuzhao@Ubuntu ~$ virsh net-list --all | grep nat-demo
nat-demo active yes yes ← Persistent ✅
Note

📌 net-define vs net-create:

net-define 写配置到 /etc/libvirt/qemu/networks/ 持久化,net-create 只在内存中、stop 即消失

Terminal window
# === net-create:临时(stop 就消失,无持久化文件) ===
jiuzhao@Ubuntu ~$ virsh net-create nat-demo.xml
jiuzhao@Ubuntu ~$ virsh net-list --all | grep nat-demo
nat-demo active no no ← Persistent ❌
# === 清理 ===
jiuzhao@Ubuntu ~$ virsh net-destroy nat-demo
jiuzhao@Ubuntu ~$ virsh net-undefine nat-demo

3. 传统桥接 (nmcli)#

有线网卡上可用 nmcli 创建 Linux Bridge,VM 和宿主机同网段互通

Terminal window
# === nmcli 三步建桥(⚠️ 仅支持有线网卡,WiFi 不行) ===
[root@vhost1 ~]# ip a s ens161
24: 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 s
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
inet 10.2.33.2/22 ... '获取到了教室网段 IP ✅️'
# 此时跨节点的虚拟机之间可以直接通信!
Caution

🧱 真实环境实测 —— 宿主机只有 WiFi,没插网线时

我这边宿主机网卡现状:

Terminal window
jiuzhao@Ubuntu ~$ ip -br addr
eno1 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)MacvtapNAT (default)
IP 网段宿主机同一网段宿主机同一网段私有子网(不同网段)
外网访问虚拟机✅ 可直接访问✅ 可直接访问❌ 需端口转发
虚拟机上网✅✅✅ 通过宿主机转发
与宿主机互访✅ 畅通无阻❌ 天生隔离✅ 畅通无阻
网络性能较高(约物理 90%)极高(接近物理 99%)较高(有 NAT 转发损耗)
配置难度中等(需改网卡配置)简单(一行 virt-xml)最简单(默认即用)
有线/WiFi仅有线 ❗有线可靠,WiFi 看 AP均支持 ✅

💡 抉择一句话:开发测试用 NAT → 需要局域网互访用 Bridge → 追求性能用 Macvtap(有线)

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Profile Image of the Author
久棹
不是先学好了再干,而是先干起来再学习,干中学!
分类
站点统计
文章
115
分类
15
标签
272
总字数
306,562
运行时长
0 天
最后活动
0 天前
文章目录