QEMU 虚拟化完全指南 / 06 - 快照管理
06 - 快照管理
全面掌握 QEMU 内部快照、外部快照、实时快照的创建、恢复、管理与自动化。
6.1 快照概念
QEMU 支持两种快照方式:内部快照和外部快照。理解它们的区别是正确使用快照的关键。
快照类型对比
| 特性 | 内部快照 | 外部快照 |
|---|---|---|
| 存储位置 | 同一个 qcow2 文件内 | 独立的外部文件 |
| 格式 | 仅 qcow2 | 任意格式 |
| 离线创建 | ✅ | ✅ |
| 在线创建 | ✅ (QEMU Monitor) | ✅ (QMP/libvirt) |
| 磁盘空间 | 增量占用 | 创建新的差异文件 |
| 恢复速度 | 快 | 中等 |
| 删除快照 | 释放空间 | 合并后释放 |
| 链式管理 | 无 | backing chain |
| 性能影响 | 快照越多越慢 | 链越长越慢 |
6.2 内部快照
离线操作(qemu-img)
# 查看快照列表
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-img snapshot -l disk.qcow2 | awk 'NR>2 {print $2}' | while read snap; do
qemu-img snapshot -d "$snap" disk.qcow2
done
在线操作(QEMU Monitor)
# 启动 QEMU 并启用 Monitor
qemu-system-x86_64 \
-enable-kvm -cpu host -m 4G \
-drive file=disk.qcow2,format=qcow2 \
-monitor stdio
在 Monitor 中执行:
# 创建快照
(qemu) savevm snap-before-update
# 查看快照列表
(qemu) info snapshots
ID TAG VM SIZE DATE VM CLOCK
1 snap-before-update 256M 2026-05-10 10:30:00 00:15:23.456
2 snap-clean 128M 2026-05-10 09:00:00 00:05:12.789
# 恢复快照
(qemu) loadvm snap-before-update
# 删除快照
(qemu) delvm snap-before-update
使用 QMP(QEMU Machine Protocol)
# 通过 QMP Socket 交互
qemu-system-x86_64 \
-enable-kvm -cpu host -m 4G \
-drive file=disk.qcow2,format=qcow2 \
-qmp unix:/tmp/qemu.sock,server,nowait
# 使用 socat 发送 QMP 命令
echo '{"execute":"qmp_capabilities"}' | socat - UNIX-CONNECT:/tmp/qemu.sock
# 创建快照
echo '{"execute":"blockdev-snapshot-sync","arguments":{"node-name":"drive0","snapshot-file":"disk.snap.qcow2","snapshot-name":"snap1"}}' | socat - UNIX-CONNECT:/tmp/qemu.sock
6.3 外部快照
外部快照将快照存储在单独的文件中,通过 backing chain 建立层级关系:
外部快照的 backing chain:
原始: disk.qcow2 (base)
│
├── 快照后: disk-snap1.qcow2 (新写入的数据)
│ └── backing: disk.qcow2
│
└── 快照后: disk-snap2.qcow2
└── backing: disk-snap1.qcow2
└── backing: disk.qcow2
创建外部快照
# 方法 1:使用 qemu-img
qemu-img snapshot -c -a snap1 disk.qcow2
# 方法 2:在 QMP 中使用 blockdev-snapshot-sync
# (在运行中的虚拟机上操作)
# 方法 3:使用 qemu-img rebase
# 先创建新文件
qemu-img create -f qcow2 -b disk.qcow2 -F qcow2 disk-snap1.qcow2
# 使用新文件启动
qemu-system-x86_64 ... -drive file=disk-snap1.qcow2,format=qcow2
在线外部快照(QMP)
# 通过 QMP 创建外部快照
cat << 'EOF' | socat - UNIX-CONNECT:/tmp/qemu.sock
{"execute":"qmp_capabilities"}
{"execute":"blockdev-snapshot-sync","arguments":{
"node-name":"drive0",
"overlay-node-name":"overlay0",
"overlay-file":"disk-snap1.qcow2",
"overlay-format":"qcow2",
"snapshot-name":"snap1"
}}
EOF
合并外部快照
# 将快照数据合并回基础镜像
qemu-img commit disk-snap1.qcow2
# 或者使用 block-commit(在线合并)
# (qemu) block-commit drive0
# 将所有层合并为一个独立文件
qemu-img convert -f qcow2 -O qcow2 disk-snap1.qcow2 disk-merged.qcow2
6.4 实时快照(Live Snapshot)
实时快照在虚拟机运行状态下创建,不会中断服务:
使用 libvirt 实时快照
# 创建实时快照
virsh snapshot-create-as ubuntu-vm \
--name "live-snap1" \
--description "Before update" \
--atomic
# 查看快照
virsh snapshot-list ubuntu-vm
# 恢复快照
virsh snapshot-revert ubuntu-vm live-snap1
# 删除快照
virsh snapshot-delete ubuntu-vm live-snap1
QMP 实时快照命令
# 创建外部快照(推荐方式)
echo '{"execute":"blockdev-snapshot-sync","arguments":{"node-name":"drive0","snapshot-file":"snap.qcow2","snapshot-name":"live-snap","format":"qcow2"}}' | socat - UNIX-CONNECT:/tmp/qemu.sock
6.5 快照恢复策略
恢复到指定快照
# 离线恢复(虚拟机关机状态)
qemu-img snapshot -a snap1 disk.qcow2
# 在线恢复(Monitor)
# (qemu) loadvm snap1
# 使用 libvirt
virsh snapshot-revert vm-name snap-name
部分恢复(仅恢复单个磁盘)
# 当虚拟机有多个磁盘时,可能只需要恢复其中一个
# 使用 block-stream 或 block-commit 恢复特定磁盘
# 在 Monitor 中
# (qemu) block-commit disk0 base
快照恢复注意事项
| 场景 | 操作 | 风险 |
|---|---|---|
| 离线恢复 | qemu-img snapshot -a | 低 |
| 在线恢复 | loadvm | 中等(内存状态恢复) |
| 链式快照恢复 | 需要完整 backing chain | 高(链断裂会导致数据丢失) |
6.6 快照管理最佳实践
命名规范
# 推荐的快照命名格式: <用途>-<日期>-<说明>
qemu-img snapshot -c pre-update-20260510-app-upgrade disk.qcow2
qemu-img snapshot -c daily-20260510-0300 disk.qcow2
qemu-img snapshot -c pre-migration disk.qcow2
快照清理策略
#!/bin/bash
# snapshot-cleanup.sh - 清理过期快照
DISK="disk.qcow2"
KEEP_DAYS=7
# 列出所有快照
snaps=$(qemu-img snapshot -l ${DISK} | awk 'NR>2 {print $2}')
for snap in $snaps; do
# 提取日期(假设格式中包含 YYYYMMDD)
date_str=$(echo "$snap" | grep -oP '\d{8}' | head -1)
if [ -n "$date_str" ]; then
snap_date=$(date -d "$date_str" +%s)
cutoff_date=$(date -d "-${KEEP_DAYS} days" +%s)
if [ "$snap_date" -lt "$cutoff_date" ]; then
echo "删除过期快照: $snap"
qemu-img snapshot -d "$snap" "$DISK"
fi
fi
done
快照与备份结合
#!/bin/bash
# 基于快照的增量备份
VM_DISK="disk.qcow2"
BACKUP_DIR="/backup"
SNAP_NAME="backup-$(date +%Y%m%d-%H%M%S)"
# 创建快照
qemu-img snapshot -c "${SNAP_NAME}" "${VM_DISK}"
# 备份快照(可以使用 rsync 增量传输)
rsync -av --progress "${VM_DISK}" "${BACKUP_DIR}/"
# 保留最近 7 个快照
qemu-img snapshot -l "${VM_DISK}" | awk 'NR>2 {print $2}' | \
sort -r | tail -n +8 | while read snap; do
echo "清理旧快照: $snap"
qemu-img snapshot -d "$snap" "${VM_DISK}"
done
6.7 快照故障排查
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 快照创建失败 | 磁盘空间不足 | 清理空间或使用外部快照 |
| 恢复后数据丢失 | 覆盖了快照后的数据 | 恢复前先备份当前状态 |
| backing chain 断裂 | 中间层被删除 | 使用 qemu-img rebase 修复 |
| 快照文件损坏 | 异常断电 | 使用 qemu-img check 检查 |
修复 backing chain
# 检查 backing chain 完整性
qemu-img info --backing-chain disk-snap3.qcow2
# 如果中间层丢失,可以手动指定新的 backing file
qemu-img rebase -b disk-snap1.qcow2 -F qcow2 disk-snap3.qcow2
# 强制断开 backing chain(创建独立副本)
qemu-img convert -f qcow2 -O qcow2 disk-snap3.qcow2 disk-flat.qcow2
6.8 自动化快照管理
定时快照脚本(cron)
#!/bin/bash
# /usr/local/bin/qemu-snapshot-auto.sh
VM_NAME="$1"
ACTION="$2" # create, list, restore, delete
SNAP_NAME="$3"
VM_DISK="/var/lib/qemu/${VM_NAME}/disk.qcow2"
LOG="/var/log/qemu-snapshot.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "${LOG}"
}
case "${ACTION}" in
create)
SNAP="${SNAP_NAME:-auto-$(date +%Y%m%d-%H%M%S)}"
qemu-img snapshot -c "${SNAP}" "${VM_DISK}" 2>&1
log "创建快照: ${VM_NAME} -> ${SNAP}"
# 保留最近 24 个快照(每天一个,保留一个月的)
MAX_SNAPS=24
qemu-img snapshot -l "${VM_DISK}" | awk 'NR>2 {print $2}' | \
sort -r | tail -n +$((MAX_SNAPS + 1)) | while read snap; do
qemu-img snapshot -d "$snap" "${VM_DISK}"
log "清理旧快照: ${VM_NAME} -> ${snap}"
done
;;
list)
qemu-img snapshot -l "${VM_DISK}"
;;
restore)
if [ -z "${SNAP_NAME}" ]; then
echo "用法: $0 <vm> restore <snapshot>"
exit 1
fi
qemu-img snapshot -a "${SNAP_NAME}" "${VM_DISK}" 2>&1
log "恢复快照: ${VM_NAME} -> ${SNAP_NAME}"
;;
delete)
if [ -z "${SNAP_NAME}" ]; then
echo "用法: $0 <vm> delete <snapshot>"
exit 1
fi
qemu-img snapshot -d "${SNAP_NAME}" "${VM_DISK}" 2>&1
log "删除快照: ${VM_NAME} -> ${SNAP_NAME}"
;;
*)
echo "用法: $0 <vm> {create|list|restore|delete} [snapshot-name]"
exit 1
;;
esac
cron 定时任务
# 每天凌晨 3 点创建快照
0 3 * * * /usr/local/bin/qemu-snapshot-auto.sh ubuntu-vm create
# 每周日凌晨 4 点创建周快照
0 4 * * 0 /usr/local/bin/qemu-snapshot-auto.sh ubuntu-vm create weekly-$(date +\%Y\%m\%d)
要点回顾
| 要点 | 核心内容 |
|---|---|
| 内部快照 | 数据存在同一文件中,离线/在线均可使用 |
| 外部快照 | 差异文件独立存储,通过 backing chain 关联 |
| 实时快照 | 虚拟机运行中创建,不中断服务 |
| 恢复策略 | 离线用 qemu-img,在线用 Monitor/QMP |
| 自动化 | cron + 脚本实现定时快照与清理 |
注意事项
快照不是备份: 快照依赖基础磁盘文件,如果基础文件损坏,所有快照都会丢失。快照应与独立备份结合使用。
快照性能影响: 内部快照会增大 qcow2 文件体积,外部快照会增加 I/O 路径长度。建议控制快照数量(不超过 10-20 个)。
在线快照一致性: 在线创建快照时,建议先使用 guest agent 冻结文件系统,确保数据一致性。
扩展阅读
下一步
→ 07 - 虚拟机迁移:学习在线迁移、离线迁移、跨主机迁移与共享存储方案。