存储管理与快照迁移

5966 字
30 分钟
存储管理与快照迁移

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

[TOC]


🧱 知识点总结#

从”手动挡”到”自动挡”#

Note

上篇笔记📚中,我们用 qemu-img create + virsh define 完成批量创建

  • 但这些操作 ==libvirt 并不完全”知情”==
    • 你用 qemu-img 创建的磁盘、用 rm 删除的镜像,libvirt 的存储池并不会自动感知

🧱 为什么 libvirt “不知情”?

libvirt 内部维护了一份元数据”登记表”,它并不直接盯着文件系统看:

Terminal window
qemu-img create 创建磁盘:
/guest_images/vm-disk1.qcow2 文件确实在磁盘上 ✅️
libvirt 的"登记表"里没有它 libvirt 不知道 ❌️
virsh vol-create-as 创建磁盘:
/guest_images/vm-disk1.qcow2 文件在磁盘上 ✅️
libvirt 同时在登记表里记了一笔 libvirt 知道 ✅️

通俗类比

  • qemu-img create = 你偷偷把一箱货搬进仓库,仓库管理员没收到入库单,==账本上没有==

  • virsh vol-create-as = 你走正规流程入库,管理员登记了货物名称、大小、位置,==账本上记了==

    📌 ==virsh pool-refresh== 就是管理员去仓库实地盘点一圈,把账本和实物对齐 —> 发现多了一箱货,补登进去

本质区别是 qemu-img ==绕过了 libvirt 的”记账”环节==,libvirt 的元数据没有被更新

✅️ 把”管理权”交还给 libvirt —— 用受管的存储池 和 存储卷替代 qemu-img 的裸操作,让一切变得可控、可追踪

📌 管理工具演进路线

阶段创建磁盘方式管理方式可见性
原始版qemu-img create手动 rmlibvirt 不可见 ❌️
受管版virsh vol-create-asvirsh vol-deletelibvirt 全可见 ✅️

📦 存储池 (Storage Pool)#

概念#

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

通俗解释:qemu-img 创建的磁盘就像你自己在仓库里堆货物 —— 仓库管理员 (libvirt) 不知道你放了什么

  • 存储池就是让管理员接手管理,他知道仓库里有什么、还剩多少空间、哪个货物归谁
  • 就是 libvirt 对磁盘的”正式管理方式”

存储池类型#

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

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

创建目录型存储池#

Terminal window
1)定义存储池
[root@vhost1 ~]# virsh pool-define-as guest_images_dir dir --target "/guest_images"
Pool guest_images_dir defined
# pool-define-as: 定义存储池
# guest_images_dir: 存储池名称
# dir: 类型为目录型
# --target: 存储池对应的物理目录路径
2)构建存储池(自动创建目标目录)
[root@vhost1 ~]# ls /guest_images
ls: cannot access '/guest_images': No such file or directory '目录还不存在'
[root@vhost1 ~]# virsh pool-build guest_images_dir
Pool guest_images_dir built
[root@vhost1 ~]# ls -ld /guest_images
drwx--x--x. 2 root root 6 Jun 1 08:44 /guest_images '目录已自动创建'
3)启动存储池
[root@vhost1 ~]# virsh pool-start guest_images_dir
Pool guest_images_dir started
4)设置开机自启
[root@vhost1 ~]# virsh pool-autostart guest_images_dir
Pool guest_images_dir marked as autostarted
5)查看存储池详细信息
[root@vhost1 ~]# virsh pool-info guest_images_dir
Name: guest_images_dir
UUID: 0f07bb21-b726-4da1-86fd-34ed7fcdb884
State: running '✅️ 活跃状态'
Persistent: yes '✅️ 持久化'
Autostart: yes '✅️ 开机自启'
Capacity: 69.94 GiB '总容量'
Allocation: 21.94 GiB '已分配'
Available: 48.00 GiB '可用空间'

📌 四步走pool-define-aspool-buildpool-startpool-autostart

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

操作虚拟网络存储池
定义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

默认存储池#

Terminal window
[root@vhost1 ~]# virsh pool-list --all
Name State Autostart
----------------------------------------
default active yes
guest_images_dir active yes
images active yes
[root@vhost1 ~]# virsh pool-dumpxml default | grep path
<path>/var/lib/libvirt/images</path>
# 这就是 day02 中我们一直放磁盘的地方!
Note

📌 default 池会自动管理目录中的镜像:重启后或 pool-refresh 后,目录里的 .qcow2 文件会被自动识别为存储卷

--------------格式有要求吗??? 上篇笔记并没有要求的!! .qcow2 ???

这就是为什么 day02 中我们用 qemu-img create 创建的磁盘,在 virsh vol-list default 里也能看到


📦 存储卷 (Storage Volume)#

概念#

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

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

🧱 与 day02 的联动

day02 方式 (手动)day03 方式 (受管)
qemu-img create -f qcow2 node.img 15Gvirsh vol-create-as --pool guest_images_dir --name vm-disk1 --capacity 20GB --format qcow2
qemu-img create -f qcow2 -b node.img node1.qcow2 20Gvirsh vol-create-as --pool ... --backing-vol vm-disk1 --backing-vol-format qcow2
rm -rf /var/lib/libvirt/images/node1.qcow2virsh vol-delete vm-disk1 guest_images_dir

创建存储卷#

Terminal window
1)在存储池中创建卷
[root@vhost1 ~]# virsh vol-create-as \
--pool guest_images_dir \
--name vm-disk1 \
--capacity 20GB \
--format qcow2
Vol vm-disk1 created
2)验证卷已创建
[root@vhost1 ~]# ls /guest_images/
vm-disk1
[root@vhost1 ~]# qemu-img info /guest_images/vm-disk1
image: "api"
file format: qcow2
virtual size: 18.6 GiB (20000000000 bytes)
disk size: 196 KiB '精简置备,实际只占 196K'
3)查看存储池中的卷列表
[root@vhost1 ~]# virsh vol-list guest_images_dir
Name Path
------------------------------------
vm-disk1 /guest_images/vm-disk1
4)查看卷详细信息
[root@vhost1 ~]# virsh vol-info vm-disk1 guest_images_dir
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后端盘格式

使用 virsh 创建前端盘#

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

Terminal window
[root@vhost1 ~]# virsh vol-create-as \
--pool guest_images_dir \
--name vm-disk1-front \
--capacity 30G \
--format qcow2 \
--backing-vol vm-disk1 \
--backing-vol-format qcow2
Vol vm-disk1-front created
[root@vhost1 ~]# qemu-img info /guest_images/vm-disk1-front
image: "api"
file format: qcow2
virtual size: 30 GiB (32212254720 bytes)
disk size: 196 KiB
backing file: /guest_images/vm-disk1 '后端盘已关联 ✅️'
backing file format: qcow2

✅️ 对比 day02 的 qemu-img create -b:效果完全一样,但 libvirt 现在”知道”这个前端盘的存在

使 qemu-img 创建的磁盘受管#

Terminal window
1)用 qemu-img 手动创建磁盘
[root@vhost1 ~]# cd /guest_images/
[root@vhost1 /guest_images]# qemu-img create -f qcow2 test.qcow2 10G
[root@vhost1 /guest_images]# ls
test.qcow2 vm-disk1 vm-disk1-front
2)当前 libvirt 看不到这个磁盘
[root@vhost1 /guest_images]# virsh vol-list guest_images_dir
Name Path
------------------------------------------------
vm-disk1 /guest_images/vm-disk1
vm-disk1-front /guest_images/vm-disk1-front
# test.qcow2 不在列表中 ❌️
3)刷新存储池
[root@vhost1 /guest_images]# virsh pool-refresh guest_images_dir
Pool guest_images_dir refreshed
4)再次查看——已经受管了
[root@vhost1 /guest_images]# virsh vol-list guest_images_dir
Name Path
------------------------------------------------
test.qcow2 /guest_images/test.qcow2 '✅️ 出现了!'
vm-disk1 /guest_images/vm-disk1
vm-disk1-front /guest_images/vm-disk1-front
5)删除卷
[root@vhost1 /guest_images]# virsh vol-delete test.qcow2 guest_images_dir
Vol test.qcow2 deleted

📌 pool-refresh:让 libvirt 重新扫描存储池目录,把不认识的文件”登记入册”

  • default 池在重启后会自动 refresh,自定义池需要手动执行

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

三种修改 XML 的方式#

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

方式命令优点缺点
直接 vimvim /etc/libvirt/qemu/test.xml最灵活语法错误风险高、需手动生效
virsh editvirsh edit test自动语法校验还是手写 XML
virt-xmlvirt-xml test --edit --memory 4096==不用写 XML==,一行命令搞定部分复杂场景仍需手写
Terminal window
1)语法校验
[root@vhost1 ~]# virt-xml-validate /etc/libvirt/qemu/test.xml
/etc/libvirt/qemu/test.xml validates '✅️ 语法正确'

内存管理#

Terminal window
1)查看当前内存配置
[root@vhost1 ~]# virsh dumpxml test | grep -i mem
<memory unit='KiB'>2097152</memory> '最大内存 2G'
<currentMemory unit='KiB'>2097152</currentMemory> '当前内存 2G'
2)关机状态下修改最大内存
[root@vhost1 ~]# virsh setmaxmem --size 1G test
[root@vhost1 ~]# virsh dumpxml test | grep -i mem
<memory unit='KiB'>1048576</memory> '最大内存 → 1G'
<currentMemory unit='KiB'>1048576</currentMemory>
3)开机后动态调整当前内存(不能超过 maxmem)
[root@vhost1 ~]# virsh start test
[root@vhost1 ~]# virsh setmem --size 512M test '运行时缩小到 512M ✅️'
[root@vhost1 ~]# virsh dumpxml test | grep -i mem
<memory unit='KiB'>1048576</memory> '最大 还是 1G'
<currentMemory unit='KiB'>524288</currentMemory> '当前 512M'
[root@vhost1 ~]# virsh setmem --size 2G test '❌️ 超过 maxmem'
error: invalid argument: cannot set memory higher than max memory

⚠️ 关键区别

命令作用运行时可用?
virsh setmaxmem修改最大内存上限❌️ 关机才能改
virsh setmem修改当前使用内存✅️ 可以热调整 (但不能超 maxmem)

CPU 管理#

Terminal window
1)查看当前 vCPU 配置
[root@vhost1 ~]# virsh dumpxml test | grep -i vcpu
<vcpu placement='static' current='1'>1</vcpu>
2)关机状态下修改最大 vCPU
[root@vhost1 ~]# virsh setvcpus test 3 --config --maximum
[root@vhost1 ~]# virsh dumpxml test | grep -i vcpu
<vcpu placement='static' current='1'>3</vcpu>
# 最大 3 核,当前使用 1 核
3)修改当前使用的 vCPU 数量
[root@vhost1 ~]# virsh setvcpus test 2 --current
[root@vhost1 ~]# virsh dumpxml test | grep -i vcpu
<vcpu placement='static' current='2'>3</vcpu>
# 当前 2 核,最大 3 核
4)查看 vCPU 数量
[root@vhost1 ~]# virsh vcpucount test
maximum config 3
current config 2

📌 参数区分

参数含义
--config修改持久化配置 (关机生效)
--current修改当前状态
--maximum修改的是最大上限而非当前值
--live热修改 (需 VM 运行中)

virt-xml 一行搞定#

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

Terminal window
# 基本语法
virt-xml DOMAIN XML-ACTION XML-OPTION [选项]
1)修改内存
[root@vhost1 ~]# virt-xml test --edit --memory 4096
Domain 'test' defined successfully.
[root@vhost1 ~]# virsh dumpxml test | grep -i mem
<memory unit='KiB'>4194304</memory>
# 一行命令,内存变成 4G ✅️
2)添加磁盘(磁盘自动创建)
[root@vhost1 ~]# virt-xml test --add-device --disk /var/lib/libvirt/images/newdisk.qcow2,format=qcow2,size=20 --update
Allocating 'newdisk.qcow2' | 20 GB 00:00:00
Domain 'test' defined successfully.
# 磁盘自动创建 + 附加,一步到位
3)修改网卡
[root@vhost1 ~]# virt-xml test --edit --network network=default
Domain 'test' defined successfully.
# 把 VM 从 vbr 网络切到 default 网络
4)移除磁盘设备
[root@vhost1 ~]# virsh shutdown test
[root@vhost1 ~]# virsh detach-disk test --config --target sda
Disk detached successfully
# --config: 持久化删除
# --target: 指定磁盘设备名
5)热添加磁盘(VM 运行中)
[root@vhost1 ~]# virsh vol-create-as --pool default --name vm-disk-test --capacity 20GB --format qcow2
[root@vhost1 ~]# virsh attach-disk test /var/lib/libvirt/images/vm-disk-test vdb
Disk attached successfully
# 在 VM 里 lsblk 就能看到新磁盘 vdb ✅️

📌 virt-xml 四大动作

动作作用示例
--edit修改已有配置--edit --memory 4096
--add-device添加新设备--add-device --disk ...
--remove-device移除设备--remove-device --disk target=vdb
--build-xml只输出 XML 不修改预览用

⚠️ 磁盘设备名与 bus 类型的关系

Terminal window
<disk type="file" device="disk">
<target dev="vda" bus="virtio"/> '设备名 vda ← bus=virtio'
</disk>
<disk type="file" device="disk">
<target dev="sda" bus="sata"/> '设备名 sda ← bus=sata'
</disk>
# 同样的磁盘,bus 不同 → 在 VM 里看到的设备名不同

📸 快照与克隆#

快照 (Snapshot)#

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

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

Terminal window
1)创建快照
[root@vhost1 ~]# virsh snapshot-create-as test snap1
Domain snapshot snap1 created
[root@vhost1 ~]# virsh snapshot-create-as test snap2
Domain snapshot snap2 created
# snap1 → snap2 形成链式关系
2)查看快照树
[root@vhost1 ~]# virsh snapshot-list --tree test
snap1
|
+- snap2
# snap2 是在 snap1 基础上创建的
3)查看当前快照
[root@vhost1 ~]# virsh snapshot-current test --name
snap2
4)恢复到 snap1
[root@vhost1 ~]# virsh snapshot-revert test snap1
Domain snapshot snap1 reverted
[root@vhost1 ~]# virsh snapshot-current test --name
snap1
# 回到 snap1 后,snap2 还在但不再是"当前"状态
5)删除快照
[root@vhost1 ~]# virsh snapshot-delete test snap2
Domain snapshot snap2 deleted
6)查看快照 XML 详情
[root@vhost1 ~]# virsh snapshot-dumpxml test 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> <快照名>
查看详情virsh snapshot-dumpxml <VM> <快照名>

💡 day02 virt-sysprep 的区别

  • virt-sysprep:永久清理个性化信息,用于制作模板
  • virsh snapshot:临时保存状态,用于实验/回滚

克隆 (Clone)#

==克隆== 就是基于一台现有虚拟机制作一份完整的副本

Terminal window
1)自动克隆(libvirt 自动命名、自动分配磁盘路径)
[root@vhost1 ~]# virt-clone --original test --auto-clone
Allocating 'test-clone.qcow2' | 20 GB 00:00:13
Clone 'test-clone' created successfully.
2)手动指定克隆参数
[root@vhost1 ~]# virt-clone \
--original example-VM-2 \
--name example-VM-3 \
--file /var/lib/libvirt/images/disk-1-example-VM-2.qcow2 \
--file /var/lib/libvirt/images/disk-2-example-VM-2.qcow2

📌 virt-clone 会自动处理:UUID 去重、MAC 地址去重、个性化信息清理 —— 不用再手动搞

⚠️ 克隆 vs 前端盘方案

virt-clone 克隆day02 前端盘 + COW
磁盘占用==完整复制== (每台独立一份数据)==增量== (共享后端盘)
空间效率低 ❌️高 ✅️
独立性完全独立 ✅️依赖后端盘
适合场景少量、完全隔离的场景批量快速部署
Terminal window
# 验证克隆的磁盘是完整复制
[root@vhost1 ~]# du -sh /var/lib/libvirt/images/test-clone.qcow2
1.5G /var/lib/libvirt/images/test-clone.qcow2
# 1.5G → 完整复制,不是前端盘(前端盘一般只有几百K)

🐟 guestfish 和 guestmount#

概念#

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

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

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

工具模式适合场景
guestmount挂载模式 (类似 mount)浏览文件、结合宿主机命令操作
guestfish交互式 Shell脚本化操作、复杂定制

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

Terminal window
1)安装工具
[root@vhost1 ~]# yum -y install libguestfs
2)创建挂载目录
[root@vhost1 ~]# mkdir /vmdir
3)挂载镜像(-a 指定磁盘, -i 自动检测并挂载分区)
[root@vhost1 ~]# guestmount -a /var/lib/libvirt/images/test-clone.qcow2 -i /vmdir
[root@vhost1 ~]# df -h /vmdir
Filesystem Size Used Avail Use% Mounted on
/dev/fuse 19G 1.5G 17G 8% /vmdir
4)现在可以像操作普通目录一样操作镜像内部了
[root@vhost1 ~]# ls /vmdir/
afs boot etc lib media opt root sbin sys usr
bin dev home lib64 mnt proc run srv tmp var
[root@vhost1 ~]# mkdir /vmdir/testdir
[root@vhost1 ~]# echo 123 > /vmdir/testdir/123.txt
[root@vhost1 ~]# cp /vmdir/etc/hosts /mnt
5)卸载
[root@vhost1 ~]# guestunmount /vmdir

💡 注意:guestmount 底层会创建临时 VM 来访问磁盘 (在 virt-manager 中可见),卸载后自动删除

guestfish —— 交互式定制磁盘#

这次我们在镜像里安装 httpd、创建用户、定制网站首页

Terminal window
1)进入 guestfish 交互式 Shell
[root@vhost1 ~]# guestfish -i --network -a /var/lib/libvirt/images/test-clone.qcow2
# -i: 自动挂载
# --network: 启用网络 (安装软件需要)
# -a: 指定磁盘镜像
2)查看文件(Fish 自带命令)
><fs> wc-l /etc/hosts
7
3)调用操作系统的命令(需加 command 前缀)
><fs> command "wc -l /etc/hosts"
7 /etc/hosts
4)安装 httpd 服务(走网络, 较慢)
><fs> command "yum -y install httpd"
5)定制首页
><fs> touch /var/www/html/index.html
><fs> vi /var/www/html/index.html 'fish 内置 vi 编辑器'
><fs> cat /var/www/html/index.html
index
6)创建用户并设密码
><fs> command "useradd user1"
><fs> command "echo redhat | passwd --stdin user1"
7)设置 httpd 开机自启
><fs> command "systemctl enable httpd"
><fs> command "systemctl is-enabled httpd"
enabled '✅️ 已设为自启'
8)重新打 SELinux 标签(重要!)
><fs> selinux-relabel /etc/selinux/targeted/contexts/files/file_contexts /
9)退出
><fs> exit

⚠️ guestfish 的两套命令体系

类型来源示例特点
Fish 内置命令libguestfscat, vi, touch, wc-l直接操作文件
系统命令VM 磁盘内的 OScommand "yum install ..."需要 command 前缀
Warning

command "vim ..." 不能用 —— guestfish 不会给 vim 分配终端,会卡住

要编辑文件请用 Fish 内置的 vi 命令

📌 guestfish vs guestmount 对比

对比维度guestfishguestmount
核心特性交互式 Shell文件系统挂载点
底层原理基于 libguestfs 库基于 FUSE
使用方式单一工具内操作挂载后用任何宿主机命令
适合场景脚本化批量处理浏览文件、结合外部工具

验证定制结果#

Terminal window
# 基于定制好的磁盘创建 VM
[root@vhost1 ~]# ssh root@192.168.122.207 '登进去验证'
[root@localhost ~]# curl 127.0.0.1/index.html
index '首页 ✅️'
[root@localhost ~]# id user1
uid=1001(user1) gid=1001(user1) groups=1001(user1) '用户 ✅️'

🌐 桥接网络 (非 NAT)#

概念#

回顾 day01default 网络使用 NAT 模式,虚拟机通过 virbr0 (192.168.122.0/24) 上网,==外面无法直接访问虚拟机==

day02 我们创建了自定义 my_net (192.168.219.0/24),仍然基于 NAT

==桥接网络== 让虚拟机直接”插”在物理交换机上,获取和宿主机同网段的 IP

创建桥接网卡#

Terminal window
1)查看宿主机物理网卡
[root@vhost1 ~]# ip a s ens161
24: ens161: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
link/ether 00:0c:29:8d:09:0c ...
# 这是我们的物理网卡,后面要作为 br0 的 port
2)创建桥接(br0)
[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
3)验证 br0 获取到了教室网段的 IP
[root@vhost1 ~]# ip a s br0
25: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
inet 10.2.35.218/22 brd 10.2.35.255 ... '教室网段 IP ✅️'

📌 nmcli 三步建桥

步骤命令作用
nmcli connection add type bridge创建桥设备 br0
nmcli connection add type ethernet port-type bridge ... controller br0把物理网卡 ens161 设为 br0 的端口
nmcli connection up br0激活桥接

🧱 类比:桥接 br0 就像是把虚拟机和宿主机插在同一个交换机上 —— IP 同网段、互相可见

将 VM 网络改为桥接#

Terminal window
1)VM 当前网络是 NAT (vbr)
[root@vhost1 ~]# virsh domiflist test
Interface Type Source Model MAC
------------------------------------------------------------
- network vbr virtio 52:54:00:48:da:9d
2)使用 virt-xml 改为桥接
[root@vhost1 ~]# virt-xml test --edit --network bridge=br0,model=virtio
Domain 'test' defined successfully.
3)验证网卡已切换
[root@vhost1 ~]# virsh domiflist test
Interface Type Source Model MAC
-----------------------------------------------------------
- bridge br0 virtio 52:54:00:48:da:9d
# Type 从 network 变成 bridge ✅️
4)开机验证
[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 ✅️'
# 此时跨节点的虚拟机之间可以直接通信!
Note

💡 NAT vs 桥接 适用场景

NAT (virbr0 / my_net)桥接 (br0)
IP 来源libvirt DHCP (192.168.x.x)教室/公司 DHCP
外部访问 VM❌️ 需要端口转发✅️ 直接可达
VM 间跨节点通信❌️ 需要额外配置✅️ 同网段直接通
安全性高 (隔离在内网)低 (暴露在物理网络)

🚢 虚拟机迁移#

概念#

==迁移== 就是把一台物理机上的虚拟机”搬”到另一台物理机上

📌 迁移的本质

Terminal window
vhost1 (源主机) vhost2 (目标主机)
┌──────────────┐ ┌──────────────┐
test (运行) │ ───迁移───> │ test (运行) │
内存状态 内存状态
磁盘数据 磁盘数据
└──────────────┘ └──────────────┘

迁移类型对比#

类型存储VM 状态停机时间命令参数
非共享存储热迁移各自独立running极短--live --persistent
共享存储实时迁移NFS 共享running几乎为零--live --persistent
离线迁移共享存储shut off整个迁移过程--offline --persistent

非共享存储热迁移#

磁盘在源主机上,需要手动把镜像拷贝到目标主机

Terminal window
# ===== 准备工作 =====
1)源主机和目标主机都需要同样的后端盘
[root@vhost1 ~]# scp /var/lib/libvirt/images/.node.img vhost2:/var/lib/libvirt/images/
[root@vhost2 ~]# virsh pool-refresh default
2)两边要有同样的网络
[root@vhost1 ~]# scp /etc/libvirt/qemu/networks/vbr.xml vhost2:/etc/libvirt/qemu/networks/vbr.xml
[root@vhost2 ~]# virsh net-define /etc/libvirt/qemu/networks/vbr.xml
[root@vhost2 ~]# virsh net-start vbr
[root@vhost2 ~]# virsh net-autostart vbr
3)迁移以 qemu 用户身份执行(uid=107)
[root@vhost1 ~]# chown -R qemu:qemu /var/lib/libvirt/images/
[root@vhost2 ~]# chown -R qemu:qemu /var/lib/libvirt/images/
4)放行迁移端口
[root@vhost1 ~]# firewall-cmd --add-port=49152/tcp
[root@vhost1 ~]# firewall-cmd --add-port=49152/tcp --permanent
[root@vhost2 ~]# firewall-cmd --add-port=49152/tcp
[root@vhost2 ~]# firewall-cmd --add-port=49152/tcp --permanent
5)手动拷贝前端盘到目标主机
[root@vhost1 ~]# scp /var/lib/libvirt/images/test.qcow2 vhost2:/var/lib/libvirt/images/test.qcow2
# ===== 执行迁移 =====
# 在 virt-manager 中: 右键运行中的 VM → 迁移 → 选目标主机 → 勾选 "Allow unsafe" → 点击 Migrate
# ===== 验证 =====
[root@vhost1 ~]# virsh -c qemu+ssh://root@vhost2/system list
Id Name State
----------------------
1 test running '在 vhost2 上了 ✅️'
[root@vhost1 ~]# virsh list --all
Id Name State
--------------------
'vhost1 上已经没了 ✅️'

共享存储实时迁移 (NFS)#

磁盘放在 NFS 共享上,两边都能访问,不用手动拷贝

Terminal window
# ===== NFS 服务端 (vhost1) =====
1)创建共享目录并放好后端盘
[root@vhost1 ~]# mkdir /shared_images
[root@vhost1 ~]# cp -p /var/lib/libvirt/images/.node.img /shared_images
[root@vhost1 ~]# cp -p /var/lib/libvirt/images/* /shared_images
2)配置 NFS 导出
[root@vhost1 ~]# cat /etc/exports
/shared_images 192.168.100.0/24(rw)
3)启动 NFS 并放行防火墙
[root@vhost1 ~]# systemctl restart nfs-server
[root@vhost1 ~]# firewall-cmd --add-service=nfs
[root@vhost1 ~]# firewall-cmd --add-service=nfs --permanent
[root@vhost1 ~]# firewall-cmd --add-service=rpc-bind
[root@vhost1 ~]# firewall-cmd --add-service=rpc-bind --permanent
[root@vhost1 ~]# firewall-cmd --add-service=mountd
[root@vhost1 ~]# firewall-cmd --add-service=mountd --permanent
[root@vhost1 ~]# exportfs -rv
# ===== 两边都挂载 NFS =====
[root@vhost1 ~]# mount vhost1:/shared_images /var/lib/libvirt/images/
[root@vhost2 ~]# mount vhost1:/shared_images /var/lib/libvirt/images/
# 两边看到的是同一份磁盘数据! ✅️
# ===== 迁移(在 virt-manager 中操作) =====
# 右键 VM → 迁移 → 选目标主机 → 点击 Migrate
# 不用再手动 scp 磁盘了 ✅️
# ===== 命令行热迁移 =====
[root@vhost1 ~]# virsh migrate --live test qemu+ssh://root@vhost2/system --persistent --undefinesource
# --live: 在线迁移
# --persistent: 目标端持久化
# --undefinesource: 迁移后从源端删掉定义
[root@vhost1 ~]# virsh list 'vhost1 空了 ✅️'
[root@vhost1 ~]# virsh -c qemu+ssh://root@vhost2/system list
Id Name State
----------------------
3 test running 'vhost2 上运行中 ✅️'
# ===== 命令行离线迁移 =====
[root@vhost2 ~]# virsh shutdown test
[root@vhost2 ~]# virsh migrate test qemu+ssh://root@vhost1/system --offline --persistent
# --offline: 离线迁移 (VM 必须关机)

📌 迁移命令参数速查

参数含义
--live在线/热迁移 (VM 保持运行)
--offline离线迁移 (VM 关机状态)
--persistent目标端持久化配置
--undefinesource迁移成功后在源端删除 VM 定义
--verbose显示迁移进度

☁️ 官方云镜像#

概念#

OS 官方提供预装好的云镜像 (Cloud Image),体积小、即下即用

day02 模板制作 的区别:不用自己 virt-install 安装了,直接拿官方做好的镜像就行

使用流程#

Terminal window
1)下载云镜像( Rocky Linux 10 为例)
[root@vhost1 ~]# ls /images/*qcow2
/images/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2
2)复制到存储池
[root@vhost1 ~]# cp /images/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2 /var/lib/libvirt/images/rocky10.qcow2
[root@vhost1 ~]# virsh pool-refresh default
3)在 virt-manager 中导入
# 选择 "Import existing disk image"
# 选择 rocky10.qcow2,系统类型选 Rocky Linux 10
# 其余配置自定义
4)此时不知道 root 密码 ❌️
[root@vhost1 ~]# virsh console testrocky
localhost login: root
Password: ??? '云镜像没有预设密码!'

virt-customize 设置密码#

Terminal window
1)先关机
[root@vhost1 ~]# virsh shutdown testrocky
2)使用 virt-customize 设置 root 密码
[root@vhost1 ~]# cd /var/lib/libvirt/images/
[root@vhost1 /var/lib/libvirt/images]# virt-customize -a rocky10.qcow2 --root-password password:redhat123
[ 0.0] Examining the guest ...
[ 33.0] Setting a random seed
[ 33.1] Setting passwords
[ 35.0] SELinux relabelling
[ 39.9] Finishing off
3)开机验证
[root@vhost1 ~]# virsh start testrocky
[root@vhost1 ~]# virsh console testrocky
localhost login: root
Password: redhat123
[root@localhost ~]# '✅️ 成功登录'

📌 云镜像三种破密码方案

方法工具特点
破密码进入单用户模式操作繁琐,不推荐
cloud-init注入 cloud-init 配置==云环境首选==,批量自动化
virt-customizevirt-customize --root-password==最直接==,一行命令搞定

💡 virt-customizeguestfish 的关系:底层都基于 libguestfsvirt-customize 是更高层的封装,专为”定制云镜像”这个场景设计


📝 作业练习#

基于 day03 所学内容,完成以下练习

练习清单#

  1. 使用 Rocky Linux 10 云镜像,将 root 密码设为 huawei123
  2. 添加一块硬盘,创建基于 LVM 的存储池 guest_images_lvm
  3. 将官方云镜像复制到存储池目录中,使其受管
  4. 在存储池中,基于官方云镜像创建前端盘 (使用 virsh 命令创建受管的前端盘)
  5. 在 virt-manager 中使用前端盘创建虚拟机 (导入已存在的镜像),采用默认配置
  6. 创建一个新的磁盘 testdisk.qcow2 (使用 virsh 命令创建受管的磁盘),将磁盘添加到虚拟机中,名为 /dev/vdc
  7. 添加一张仅主机的网卡,创建网桥 br1,桥接到此仅主机的网卡
  8. 为虚拟机添加一张网卡,使用 br1 网桥
  9. 使用 virsh 或 virt-xml 命令,调整 CPU 和内存
  10. 运行虚拟机验证,验证后关机删除

参考命令速查#

Terminal window
# 设置密码
virt-customize -a <镜像> --root-password password:<密码>
# 创建 LVM 存储池
virsh pool-define-as guest_images_lvm logical --source-dev=<PV> --source-name=<VG> --target /dev/<VG>
# 存储池四步走
virsh pool-define-as <池名> dir --target "<目录>"
virsh pool-build <池名>
virsh pool-start <池名>
virsh pool-autostart <池名>
# 创建受管磁盘
virsh vol-create-as --pool <池名> --name 磁盘名 --capacity 大小 --format qcow2
# 创建受管前端盘
virsh vol-create-as --pool <池名> --name 前端盘名 --capacity 大小 --format qcow2 \
--backing-vol 后端盘名 --backing-vol-format qcow2
# 附加磁盘(需指定正确的 target 设备名)
virsh attach-disk <VM> <磁盘路径> vdc
# 调整 CPU/内存
virt-xml <VM> --edit --memory <大小>
virsh setvcpus <VM> <数量> --config --maximum

🔁 Day01 → Day03 知识串联#

能力day01day02day03
安装 VMvirt-install 手动安装模板机装一次,后续克隆直接用官方云镜像
管理磁盘qemu-img createqemu-img create -b 前端盘virsh vol-create-as 受管
网络default NAT (virbr0)自定义 NAT (my_net)==桥接 (br0)==
管理方式本地 virsh远程 qemu+ssh://远程 + 迁移
批量❌️shell 脚本 for 循环脚本 + 受管存储池
备份恢复❌️❌️==快照 + 克隆==
高级运维❌️guestfish 初识==guestfish + guestmount 实战==

⚠️ 常见坑点#

问题原因解决
qemu-img 创建的磁盘 virsh vol-list 看不到存储池没有 refreshvirsh pool-refresh <池名>
virsh setmem 报 “cannot set memory higher than max memory”当前内存不能超过 maxmem先关机 → setmaxmem → 开机 → setmem
virt-clone 磁盘占用和原 VM 一样大克隆是完整复制,不是 COW用 day02 前端盘方式替代
guestfish 中 command "vim ..." 卡住fish 不给 vim 分配终端用 fish 内置的 vi 命令
桥接 VM 获取不到 IPbr0 未正确桥接到物理网卡检查 nmcli connection show br0
迁移报权限错误镜像文件 owner 不是 qemuchown -R qemu:qemu /var/lib/libvirt/images/
云镜像登录不了官方镜像没有预设密码virt-customize --root-password 设置

文章分享

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

存储管理与快照迁移
https://www.kpyun.fun/posts/services/virt/virt03/
作者
久棹
发布于
2026-04-21
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
久棹
只要胆子大,天天寒暑假!
公告
欢迎来到久棹的技术小站!本站专注 Linux 运维学习笔记分享,如有问题欢迎交流探讨 🎉
分类
标签
站点统计
文章
98
分类
11
标签
203
总字数
244,453
运行时长
0
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.5
文章许可
CC BY-NC-SA 4.0

文章目录