QEMU 虚拟化完全指南 / 04 - 磁盘管理
04 - 磁盘管理
深入掌握 QEMU 磁盘镜像格式、快照管理、备份恢复、压缩优化与在线扩容的完整流程。
4.1 磁盘镜像格式详解
qcow2 格式
qcow2(QEMU Copy-On-Write version 2)是 QEMU 的原生磁镜像格式,也是最常用的格式:
| 特性 | 说明 |
|---|---|
| 稀疏分配 | 按需分配磁盘空间,初始占用极小 |
| 快照支持 | 支持内部快照和外部快照 |
| 压缩 | 支持 zlib/zstd 压缩 |
| 加密 | 支持 AES/LUKS 加密 |
| 后备文件 | 支持 backing file(增量镜像) |
| 最大大小 | 理论上 2 EB(exabytes) |
# 创建 qcow2 镜像
qemu-img create -f qcow2 vm-disk.qcow2 40G
# 创建带预分配的 qcow2(推荐用于生产)
qemu-img create -f qcow2 -o preallocation=metadata vm-disk.qcow2 40G
# 创建带压缩的 qcow2
qemu-img create -f qcow2 -o compression_type=zstd vm-disk.qcow2 40G
# 查看详细信息
qemu-img info --output=json vm-disk.qcow2
qcow2 预分配模式
| 模式 | 说明 | 创建速度 | 磁盘占用 | 读性能 | 写性能 |
|---|---|---|---|---|---|
off (默认) | 不预分配 | 最快 | 最小 | 差 | 差 |
metadata | 仅预分配元数据 | 快 | 小 | 好 | 好 |
falloc | 预分配空间(fallocate) | 中等 | 大 | 最好 | 最好 |
full | 完全预分配(填零) | 慢 | 最大 | 最好 | 最好 |
# 对比预分配效果
qemu-img create -f qcow2 -o preallocation=off test-off.qcow2 10G
qemu-img create -f qcow2 -o preallocation=metadata test-meta.qcow2 10G
qemu-img create -f qcow2 -o preallocation=falloc test-falloc.qcow2 10G
# 查看实际磁盘占用
ls -lh test-*.qcow2
du -h test-*.qcow2
raw 格式
raw 格式是最简单的磁盘格式,直接映射扇区:
# 创建 raw 镜像
qemu-img create -f raw vm-disk.raw 40G
# 查看信息
qemu-img info vm-disk.raw
| 特性 | raw | qcow2 |
|---|---|---|
| 性能 | 最好 | 接近(virtio + cache=writeback) |
| 稀疏分配 | 依赖宿主文件系统 | 原生支持 |
| 快照 | ❌ 不支持 | ✅ 支持 |
| 压缩 | ❌ 不支持 | ✅ 支持 |
| 加密 | ❌ 不支持 | ✅ 支持 |
| 增量备份 | ❌ 不支持 | ✅ backing file |
| 跨平台 | 最好 | QEMU 专用 |
| 转换 | 直接 dd | 需要 qemu-img convert |
其他格式
QEMU 也支持读取和转换其他虚拟化平台的磁盘格式:
| 格式 | 来源 | 读 | 写 |
|---|---|---|---|
| vmdk | VMware | ✅ | ✅ |
| vdi | VirtualBox | ✅ | ✅ |
| vhdx | Hyper-V | ✅ | ✅ |
| vhd | Hyper-V (旧) | ✅ | ✅ |
| qed | QEMU (旧) | ✅ | ✅ |
| parallels | Parallels | ✅ | ❌ |
4.2 qemu-img 管理工具
查看镜像信息
# 基本信息
qemu-img info disk.qcow2
# JSON 格式输出(便于脚本解析)
qemu-img info --output=json disk.qcow2
# 查看镜像的 backing chain
qemu-img info --backing-chain disk.qcow2
示例输出:
{
"filename": "disk.qcow2",
"format": "qcow2",
"virtual-size": 42949672960,
"cluster-size": 65536,
"format-specific": {
"type": "qcow2",
"data": {
"compat": "1.1",
"compression-type": "zlib",
"lazy-refcounts": false,
"refcount-bits": 16
}
},
"dirty-flag": false
}
格式转换
# qcow2 → raw
qemu-img convert -f qcow2 -O raw disk.qcow2 disk.raw
# raw → qcow2
qemu-img convert -f raw -O qcow2 disk.raw disk.qcow2
# VMDK → qcow2(从 VMware 迁移)
qemu-img convert -f vmdk -O qcow2 vmware-disk.vmdk qemu-disk.qcow2
# VDI → qcow2(从 VirtualBox 迁移)
qemu-img convert -f vdi -O qcow2 vbox-disk.vdi qemu-disk.qcow2
# 并行转换(提高速度)
qemu-img convert -f qcow2 -O qcow2 -T 4 disk.qcow2 new-disk.qcow2
# -T 指定并行 I/O 线程数
镜像检查与修复
# 检查镜像一致性
qemu-img check disk.qcow2
# 检查并修复(仅限 qcow2)
qemu-img check -r all disk.qcow2
# JSON 输出检查结果
qemu-img check --output=json disk.qcow2
4.3 增量镜像与后备文件
使用 backing file 创建增量镜像
增量镜像可以基于一个基础镜像创建多个差异镜像,节省磁盘空间:
# 创建基础镜像
qemu-img create -f qcow2 base.qcow2 40G
# ... 安装操作系统 ...
# 基于基础镜像创建增量镜像
qemu-img create -f qcow2 -b base.qcow2 -F qcow2 snapshot1.qcow2
qemu-img create -f qcow2 -b base.qcow2 -F qcow2 snapshot2.qcow2
# 查看 backing chain
qemu-img info --backing-chain snapshot1.qcow2
增量镜像架构:
base.qcow2 (只读,基础系统)
├── snapshot1.qcow2 (差异 1,用户数据)
├── snapshot2.qcow2 (差异 2,测试环境)
└── snapshot3.qcow2 (差异 3,开发环境)
合并增量镜像
# 将增量镜像的差异合并回基础镜像(会修改基础镜像!)
qemu-img commit snapshot1.qcow2
# 或者将增量镜像"扁平化"为独立镜像
qemu-img convert -f qcow2 -O qcow2 snapshot1.qcow2 flat.qcow2
修改 backing file
# 重新设置 backing file
qemu-img rebase -b new-base.qcow2 -F qcow2 snapshot1.qcow2
# 安全地断开 backing file 链接(unsafe 模式,直接解除关联)
qemu-img rebase -u -b "" snapshot1.qcow2
4.4 快照管理
内部快照(qcow2)
内部快照将快照数据存储在同一个 qcow2 文件中:
# 离线创建快照(虚拟机关机状态)
qemu-img snapshot -l disk.qcow2 # 列出快照
qemu-img snapshot -c snap1 disk.qcow2 # 创建快照
qemu-img snapshot -a snap1 disk.qcow2 # 恢复快照
qemu-img snapshot -d snap1 disk.qcow2 # 删除快照
# 在 QEMU Monitor 中创建快照(虚拟机运行中)
# (qemu) savevm snap1
# (qemu) loadvm snap1
# (qemu) info snapshots
# (qemu) delvm snap1
外部快照
外部快照将快照存储在单独的文件中:
# 使用 qemu-img 创建外部快照
qemu-img snapshot -c snap1 -a snap1 disk.qcow2
# 使用 blockdev-add 在运行时创建外部快照
# 这通常通过 libvirt 或 QMP 来完成
详细的快照管理请参见 第 06 章 - 快照管理。
4.5 备份策略
方法 1:qemu-img convert(离线备份)
# 冷备份(虚拟机关机状态)
qemu-img convert -f qcow2 -O qcow2 -c disk.qcow2 backup.qcow2
# 带压缩的备份
qemu-img convert -f qcow2 -O qcow2 -c -p disk.qcow2 backup-compressed.qcow2
# -c: 压缩
# -p: 显示进度
方法 2:快照 + dd(快速备份)
# 创建快照
qemu-img snapshot -c backup-point disk.qcow2
# 使用 dd 备份(仅备份实际使用的数据)
dd if=disk.qcow2 of=backup.dd bs=4M status=progress
# 恢复
dd if=backup.dd of=disk.qcow2 bs=4M status=progress
方法 3:增量备份
# 使用 backing chain 进行增量备份
# 只备份增量文件即可
cp snapshot1.qcow2 /backup/
# 恢复时需要整个 backing chain
# base.qcow2 + snapshot1.qcow2
方法 4:virtio-blk 块设备备份(在线备份)
# 在 QEMU Monitor 中执行块设备操作
# (qemu) drive_backup -f disk.qcow2 /backup/backup.qcow2 qcow2
# 使用 blockdev-snapshot-sync 创建外部快照后备份
备份脚本示例
#!/bin/bash
# vm-backup.sh - QEMU 虚拟机备份脚本
VM_NAME="$1"
VM_DIR="/var/lib/qemu/${VM_NAME}"
BACKUP_DIR="/backup/qemu/${VM_NAME}"
DATE=$(date +%Y%m%d_%H%M%S)
DISK="${VM_DIR}/disk.qcow2"
BACKUP="${BACKUP_DIR}/${VM_NAME}_${DATE}.qcow2"
# 检查参数
if [ -z "$VM_NAME" ]; then
echo "用法: $0 <虚拟机名称>"
exit 1
fi
# 创建备份目录
mkdir -p "${BACKUP_DIR}"
# 检查虚拟机是否运行中
if pgrep -f "qemu.*${VM_NAME}" > /dev/null; then
echo "虚拟机运行中,执行在线快照备份..."
# 通过 QMP 创建快照
echo '{"execute":"qmp_capabilities"}' | \
socat - UNIX-CONNECT:/var/run/qemu/${VM_NAME}.monitor
echo "快照已创建,请通过 QMP 完成备份"
else
echo "虚拟机关机状态,执行离线备份..."
qemu-img convert -f qcow2 -O qcow2 -c -p \
"${DISK}" "${BACKUP}"
fi
# 清理 30 天前的备份
find "${BACKUP_DIR}" -name "*.qcow2" -mtime +30 -delete
echo "备份完成: ${BACKUP}"
4.6 磁盘压缩
在线压缩(运行中的虚拟机)
# 在 QEMU Monitor 中执行
# (qemu) block_stream disk0 # 流式合并
离线压缩
# 方法 1:qemu-img convert 重新转换(最常用)
qemu-img convert -f qcow2 -O qcow2 -c disk.qcow2 disk-compressed.qcow2
# 方法 2:使用 zstd 压缩(QEMU 7.0+)
qemu-img convert -f qcow2 -O qcow2 \
-o compression_type=zstd disk.qcow2 disk-zstd.qcow2
# 方法 3:fstrim(需要客户机安装 qemu-guest-agent)
# 在客户机内执行
sudo fstrim -av
压缩效果对比
# 原始 qcow2(无压缩)
qemu-img create -f qcow2 test-none.qcow2 10G
# zlib 压缩
qemu-img create -f qcow2 -o compression_type=zlib test-zlib.qcow2 10G
# zstd 压缩(更快,压缩比略低)
qemu-img create -f qcow2 -o compression_type=zstd test-zstd.qcow2 10G
| 压缩方式 | 压缩速度 | 解压速度 | 压缩比 | CPU 开销 |
|---|---|---|---|---|
| 无 | - | - | - | 无 |
| zlib | 慢 | 中等 | 高 | 高 |
| zstd | 快 | 快 | 中等 | 中等 |
注意: 压缩会增加 CPU 开销,对 I/O 密集型虚拟机不建议使用压缩。压缩适用于归档备份场景。
4.7 磁盘扩容
扩大磁盘容量
# 扩大 qcow2 镜像(只能扩大,不能缩小)
qemu-img resize disk.qcow2 +20G
# 查看新大小
qemu-img info disk.qcow2
# 也可以设置绝对大小
qemu-img resize disk.qcow2 60G
扩展客户机分区
扩容虚拟磁盘后,还需要在客户机中扩展分区和文件系统:
方法 1:使用 growpart(推荐)
# 在客户机中执行
# 安装 growpart 工具
sudo apt install cloud-guest-utils # Debian/Ubuntu
sudo dnf install cloud-utils-growpart # Fedora
# 查看当前分区
lsblk
# 扩展分区(/dev/sda 的第 2 个分区)
sudo growpart /dev/sda 2
# 扩展文件系统
# ext4
sudo resize2fs /dev/sda2
# xfs
sudo xfs_growfs /
# 验证
df -h
方法 2:使用 parted
# 在客户机中执行
sudo parted /dev/sda
# (parted) print
# (parted) resizepart 2 100%
# (parted) quit
# 扩展文件系统
sudo resize2fs /dev/sda2 # ext4
sudo xfs_growfs / # xfs
方法 3:使用 LVM
# 如果客户机使用 LVM
sudo pvresize /dev/sda2
sudo lvextend -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv
sudo resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv
缩小磁盘(危险操作)
# QEMU 不直接支持缩小,需要转换
# 1. 先在客户机中缩小分区和文件系统
# 2. 转换到新大小的镜像
# 创建新大小的镜像
qemu-img create -f qcow2 new-disk.qcow2 20G
# 使用 virt-resize 克隆(需要 libguestfs-tools)
sudo apt install libguestfs-tools
virt-resize --shrink /dev/sda disk.qcow2 new-disk.qcow2
警告: 缩小磁盘有数据丢失风险,操作前务必做好完整备份。
4.8 qemu-nbd 网络块设备
qemu-nbd 可以将 QEMU 磁盘镜像导出为块设备,像本地磁盘一样挂载:
# 加载 nbd 内核模块
sudo modprobe nbd max_part=8
# 将 qcow2 镜像导出为块设备
sudo qemu-nbd --connect=/dev/nbd0 disk.qcow2
# 查看设备
lsblk /dev/nbd0
# 挂载分区
sudo mount /dev/nbd0p1 /mnt
# 查看文件
ls /mnt/
# 使用完毕后卸载
sudo umount /mnt
sudo qemu-nbd --disconnect /dev/nbd0
sudo modprobe -r nbd
使用 nbd 挂载 LVM 分区
# 导出镜像
sudo qemu-nbd --connect=/dev/nbd0 disk.qcow2
# 扫描 LVM 卷
sudo vgscan
sudo vgchange -ay
# 挂载 LVM 逻辑卷
sudo mount /dev/ubuntu-vg/ubuntu-lv /mnt
# 清理
sudo umount /mnt
sudo vgchange -an ubuntu-vg
sudo qemu-nbd --disconnect /dev/nbd0
使用 nbd 直接修改镜像
# 直接在镜像中编辑文件
sudo qemu-nbd --connect=/dev/nbd0 disk.qcow2
sudo mount /dev/nbd0p1 /mnt
# 修改配置文件
sudo nano /mnt/etc/hostname
sudo umount /mnt
sudo qemu-nbd --disconnect /dev/nbd0
4.9 磁盘 I/O 调优
缓存模式
| 缓存模式 | 说明 | 安全性 | 性能 |
|---|---|---|---|
none | 直接 I/O,绕过宿主缓存 | 高 | 中等 |
writethrough | 写入时同步刷新(默认) | 最高 | 较低 |
writeback | 写入缓存,异步刷新 | 中等 | 高 |
directsync | 直接 I/O + 同步 | 最高 | 最低 |
unsafe | 忽略所有刷新请求 | 低 | 最高 |
# 使用 writeback 缓存(推荐)
qemu-system-x86_64 \
-drive file=disk.qcow2,format=qcow2,if=virtio,cache=writeback
# 使用 none 缓存(直接 I/O)
qemu-system-x86_64 \
-drive file=disk.qcow2,format=qcow2,if=virtio,cache=none
I/O 模型
# 使用 io_uring(Linux 5.1+,性能最好)
qemu-system-x86_64 \
-drive file=disk.qcow2,format=qcow2,if=virtio,aio=io_uring
# 使用 native AIO
qemu-system-x86_64 \
-drive file=disk.qcow2,format=qcow2,if=virtio,aio=native
# 使用 threads(默认)
qemu-system-x86_64 \
-drive file=disk.qcow2,format=qcow2,if=virtio,aio=threads
I/O 性能测试
# 在虚拟机内使用 fio 测试
sudo apt install fio
# 顺序读测试
fio --name=seqread --rw=read --bs=1M --size=1G --numjobs=1 \
--runtime=30 --time_based --filename=/tmp/fio-test
# 随机读写测试
fio --name=randrw --rw=randrw --bs=4k --size=1G --numjobs=4 \
--runtime=30 --time_based --filename=/tmp/fio-test
要点回顾
| 要点 | 核心内容 |
|---|---|
| qcow2 | 推荐格式,支持快照、压缩、加密、增量 |
| raw | 性能最好,适合固定用途 |
| backing file | 基础镜像 + 差异镜像,节省空间 |
| 备份 | 离线 convert / 快照备份 / 增量备份 |
| 扩容 | qemu-img resize + growpart + resize2fs |
| qemu-nbd | 将镜像挂载为块设备,方便离线操作 |
注意事项
qcow2 vs raw 选择: 大多数场景使用 qcow2。只有在追求极致 I/O 性能且不需要快照时,才使用 raw 格式。
扩容不可逆:
qemu-img resize只能扩大不能直接缩小。缩小需要复杂的转换操作。
备份前验证: 备份完成后务必用
qemu-img check验证备份文件的完整性。
io_uring 需要内核支持:
aio=io_uring需要 Linux 5.1+ 内核,且 QEMU 编译时启用了 liburing 支持。
扩展阅读
下一步
→ 05 - 网络配置:深入学习 QEMU 用户模式网络、桥接、TAP、NAT 与设备直通。