ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

qcow2外部快照实战:overlay与backing file原理及冷热备操作指南

qcow2外部快照实战:overlay与backing file原理及冷热备操作指南 这个标题如果搁几年前很多做虚拟化运维的人可能一眼就懂了现在倒是经常被各种“overlay”关键词带偏有人以为我在说 Docker 的 overlay2 存储有人以为是游戏录像的直播 overlay。实际上这里聊的是虚拟化里非常经典的一套做法对基础镜像做外部快照生成的快照文件叫 overlay基础镜像在这条链里变成 backing file。这套机制的通俗说法就是“存往后的数据”——把原始镜像冻结成只读底座所有新数据全部落到覆盖层里想回滚就回滚想分支就分支是 KVM/QEMU 环境里最实用的磁盘管理套路之一。这篇文章我会从概念、原理讲到完整实操包含冷备和热备两种方式最后把我在日常维护里踩过的坑一起整理出来。无论你是刚接触 qcow2 的虚拟化新手还是已经被快照链搞到头大的资深运维照着做基本都能跑通。1. 先搞清楚三个名词backing file、overlay、基础镜像1.1 为什么我们需要“把数据往后存”在虚拟化环境里最常见的磁盘格式就是 qcow2。这个格式很好用支持快照、压缩、加密但它的快照机制分两种一种叫内部快照一种叫外部快照。内部快照是把快照状态存在同一个镜像文件里做完之后镜像文件本身会变大而且整个文件的所有快照之间是互相依赖的、耦合的。外部快照的思路完全反过来原始镜像文件保持不变新生成一个很小的覆盖文件专门记录从快照时刻开始的增量数据。这个覆盖文件就是 overlay原始镜像在这条链路里被称为 backing file。我习惯用“账本”来打比方backing file 是一本已经印刷好的历史账本里面所有记录都是固定的overlay 是一本从今天开始才启用的新账本你只往新账本上写新增和变更需要查旧账的时候再往前翻。这样操作的好处是历史记录永远干净不会被后续操作污染想回到过去某个节点只需要把新账本丢掉就行。这就是标题里“存往后的数据”这个说法的底层含义。传统做法是在同一个文件里原地修改数据是“往前覆盖”的改坏了很难恢复外部快照则是让数据“往后叠加”每次变化都不碰底层文件安全性和灵活性都提高了不少。1.2 qcow2 的 copy-on-write 与 overlay 生成原理要理解外部快照必须理解一个核心机制写时复制Copy-On-Write简称 COW。qcow2 格式并不是把虚拟磁盘的数据按线性方式直接铺在文件里的。它把整个磁盘划分成很多固定大小的块叫 cluster默认大小通常是 64KB。当你创建一个 overlay 并关联到 backing file 之后overlay 文件本身只有很小的元数据并没有把 backing file 的数据复制过来。当虚拟机第一次读取某个扇区时QEMU 会先看 overlay 里有没有这个数据如果有就用 overlay 的如果没有就自动跳到 backing file 里读取。当虚拟机第一次写入某个扇区时如果这块区域的数据原本在 backing file 里QEMU 会先把原始数据保留不动把新数据写入到 overlay 里并在 overlay 的元数据区标记“这块数据以后以我为准”。这个设计的核心价值在于快照的创建成本极低不管底层基础镜像多大创建 overlay 都只是生成一个元数据头而已。1GB 的基础镜像和 500GB 的基础镜像创建外部快照的速度差异可以忽略不计真正占用的空间只取决于后续新增写入的数据量。1.3 链式结构与多层 overlay外部快照并不是只能做一层。你可以对 overlay 再做一次外部快照得到第二层 overlay此时第一层 overlay 变成了第二层的 backing file。这样一层套一层就形成了快照链。base.qcow2 - overlay1.qcow2 - overlay2.qcow2 - overlay3.qcow2每层都只存储相对于上一层的变化量。最底层被称为 base image中间层都是 backing file最上层是当前工作层。这条链的优点是分支能力很强。比如我从 base 生成一个 overlay A 做测试再生成一个 overlay B 做生产两个 overlay 的 backing file 指向同一个基础镜像彼此互不干扰。这在批量虚拟机交付、标准化模板部署时尤其好用几百台虚拟机可以共享同一个几百 GB 的基础镜像每台只需要分配几十 GB 的 overlay。不过链越长性能损耗越大因为读取数据时可能需要从最上层一路查到最底层。所以实际生产里快照链不建议铺得太深通常三层以内比较舒服超过五层就该考虑合并优化了。2. 外部快照 vs 内部快照为什么我推荐外部快照2.1 内部快照的局限性内部快照用 qemu-img snapshot 或者 virsh snapshot-create 做出来所有快照数据都写进同一个镜像文件。表面上看挺方便一个文件搞定所有状态但用久了问题非常明显。首先是文件膨胀不可控。内部快照会占用原文件空间每拍一次快照文件体积就会增加一个逻辑层长期使用下来一个原本 20GB 的磁盘可能膨胀到 200GB而且这些历史数据无法简单地清理掉必须通过 qemu-img snapshot -d 删除指定快照来释放。但删除内部快照的过程并不轻松它会把该快照之后的数据复制到其他快照层或当前层耗时和 IO 都很高。其次是基础镜像污染。内部快照一旦创建基础镜像本身就被“改过”了。很多虚拟化平台要求基础镜像保持只读和干净方便批量克隆内部快照机制完全不适合这种场景。2.2 外部快照解决了什么问题外部快照的最大优势是基础镜像不会被修改。基础镜像始终保持原始状态可以随意复用、分发、校验。overlay 文件轻量创建速度快删除也极其简单直接删文件就好不需要做任何数据搬迁。另外外部快照天然支持在线拍摄。QEMU 的 blockdev-snapshot 机制可以在虚拟机运行过程中把一个正在写入的磁盘 Block 层切换到一个新的 overlay整个过程几乎无感知。这对备份系统来说非常重要——我可以在业务高峰期之外对运行中的虚拟机打一个外部快照然后基于 overlay 做差异备份或文件级备份而原虚拟机完全不用停机。回滚思路也更清晰。需要回滚时直接把当前 overlay 挪走或删除新建一个指向同一 backing file 的新 overlay 即可。原来的 overlay 还可以留存当归档证据或者救援目录使用。2.3 选型判断什么时候用哪种快照我个人的判断标准很简单需要保留多个历史状态且状态切换频繁用外部快照层级管理最清晰。需要保证基础镜像不可变用于模板/批量分发用外部快照。只是临时拍个状态之后基本不太可能回滚且对磁盘空间不敏感用内部快照也不是不行但说实话我还是会更倾向外部快照因为删除时更省心。如果使用的是云平台底层存储比如 Ceph RBD那快照多数由存储侧实现不太涉及 qcow2 的这套机制。内部快照唯一让我觉得还行的地方是一个单文件便于携带但在这个动不动就几十 GB 的年代单文件复制也不是什么轻松事。3. 冷备实操qemu-img 创建 overlay 的完整流程3.1 准备基础镜像与校验做外部快照前先把基础镜像准备好。假设你已经有一个 CentOS 云主机模板镜像文件名叫 centos7-base.qcow2。第一步确认镜像没有正在被其他虚拟机借用最好把它的权限改成只读避免误写。ls -lh centos7-base.qcow2 qemu-img info centos7-base.qcow2qemu-img info 输出里能看到 disk size、cluster size、snapshot list 等信息。这一步很重要因为后续创建 overlay 的时候如果基础镜像已经带有内部快照或者格式不标准创建过程可能隐藏问题。建议对基础镜像做一次校验qemu-img check centos7-base.qcow2如果发现泄漏或者错误先把基础镜像修复好再继续不要带着隐患往下走。这里有个细节qemu-img check 在镜像被频繁写入时跑结果可能不准最好确认虚拟机已关机或者这个镜像处于离线状态。3.2 创建 overlay 并验证创建 overlay 的命令非常简单qemu-img create -f qcow2 -F qcow2 -b centos7-base.qcow2 centos7-node01.qcow2-f 指定新镜像格式-F 指定 backing file 的格式-b 指定 backing file 的路径。很多新手会漏写 -F如果基础镜像是 qcow2那么 -F qcow2 其实可以省略因为 qemu-img 会自动探测但显式写出来能让命令更明确也减少后续 rebase 时可能出现的格式不匹配问题。创建完成后用 info 看状态qemu-img info centos7-node01.qcow2输出里会有一行backing file: centos7-base.qcow2 backing file format: qcow2这说明 overlay 已经和基础镜像建立了关联。此时 centos7-node01.qcow2 的体积非常小可能只有 193KB 左右因为它只是元数据壳子还没有实际数据。把 overlay 接入虚拟机的方法有两种。一种是在 XML 定义里直接改磁盘路径另一种是启动时用 -drive 参数qemu-system-x86_64 \ -drive filecentos7-node01.qcow2,ifvirtio,formatqcow2 \ -m 4096 -smp 4虚拟机启动后所有读操作会穿透到 base写操作进入 overlay。你可以在系统里写一个大文件测试一下再观察 overlay 的体积变化。3.3 冷备场景里的路径解析问题这里我必须多说一句也是最容易踩的坑路径解析。overlay 文件内部保存着 backing file 的路径qemu-img create 时默认保存的是创建命令里传入的路径。如果你用相对路径之后把 overlay 和 backing file 拆到不同目录QEMU 可能找不到 backing file启动直接失败。冷备场景里我一般建议统一使用绝对路径并且保持目录结构固定。比如统一放在 /data/kvm/images/ 下基础镜像命名不要乱改。如果你确实需要移动位置可以用 rebase 修复路径qemu-img rebase -b /new/path/centos7-base.qcow2 centos7-node01.qcow2这条命令只是修改 backing file 路径记录并不会动数据。但如果 backing file 和 overlay 之间已经积累了很多差异数据rebase 时可能会出现耗时比较长的情况因为 qemu-img 需要重新校准偏移。所以在冷备环境里我强烈建议把基础镜像目录和 overlay 目录规划成一致且稳定的结构不要频繁搬来搬去。宁可多做一层目录也不要用一个巨长的临时路径不然日后维护会非常痛苦。4. 热备实操让正在运行的虚拟机也做外部快照4.1 virsh snapshot-create-as 快速上手冷备简单但很多场景不允许关机。生产虚拟机 7x24 运行你怎么打外部快照libvirt 本身就支持基于外部快照的磁盘快照一条命令就能搞定virsh snapshot-create-as --domain vm-centos7 \ --disk-only \ --atomic \ --description before-upgrade-20250101--disk-only 的含义是只对磁盘做快照不保存内存状态所以不会中断业务。--atomic 要求整个快照操作原子化要么全部成功要么全部失败避免出现部分盘有快照部分没有的情况。执行成功后libvirt 会自动识别当前虚拟机的每块磁盘并为每块盘生成一个外部快照 overlay同时新的 overlay 会立即替换 XML 定义中的磁盘路径。你可以在虚拟机目录下看到类似 vm-centos7.before-upgrade-20250101.qcow2 的文件。这里要注意磁盘快照是逐盘创建的。如果你的虚拟机有多块磁盘快照后会产生多个 overlay 文件后续做备份时需要把每块磁盘的 overlay 都纳入管理。我自己常用的流程是先查看磁盘列表确认哪几块盘需要纳入快照范围再创建快照然后用 qemu-img info 检查每块 overlay 的 backing file 是否正确。4.2 手动 QMP blockdev-snapshot 背后的原理libvirt 封装了 QMP 命令所以用起来很简洁。但如果你想理解底层机制或者遇到 libvirt 版本较老、平台没有封装的情况就需要用 QMP 手动做。QMP 是 QEMU 的监控协议通过 socket 或者标准输入输出跟 QEMU 通信。手动做外部快照的流程大致有三步。第一步追加一个空的 overlay 到块设备链上{execute: blockdev-add, arguments: { node-name: overlay0, driver: qcow2, file: { driver: file, filename: /data/kvm/images/centos7-node01-overlay.qcow2 }, backing: drive-virtio-disk0 }}第二步执行快照切换让原块设备指向新的 overlay{execute: blockdev-snapshot, arguments: { node: drive-virtio-disk0, overlay: overlay0 }}第三步用 info block 确认当前各节点的 backing 关系{execute: query-block}这里的关键点是blockdev-add 时声明的 backing 必须是原始磁盘的 node-name。整个操作原理就是在原磁盘顶部再叠一层然后把数据面切换到新层。这个过程中QEMU 的 IO 请求会短暂暂停但时间非常短实际业务基本无感知。手动 QMP 的优点是灵活缺点是容易出错。建议脚本化封装并在每次操作前先导出 JSON 配置文件做快速校验避免 QMP 输入错误导致虚拟机磁盘链断裂。4.3 持久化保存快照链的提交与回滚热备快照做完以后overlay 会持续增长。等你确认新数据已经稳定不再需要回滚到快照点就可以把 overlay 合并回 backing file。这个动作叫 commit。传统做法是使用 qemu-img commitvirsh blockcommit vm-centos7 vda --active --pivot这个命令会把 overlay 的数据合并回 backing file然后把 backing file 重新设为当前磁盘文件。这是我喜欢的方式因为它既清理了 overlay又保留了基础镜像的路径和当前视图对上层业务没有感知。如果不用 libvirt也可以先关机再用 qemu-img commitqemu-img commit -d centos7-node01-overlay.qcow2-d 参数表示提交后删除 overlay。这里有个重要的提醒commit 时 backing file 必须保持可写状态。如果你的基础镜像是只读权限commit 会直接报错。所以做合并前记得检查权限。回滚就简单多了。把当前 overlay 从虚拟机里摘除重新创建一个指向原始 backing file 的新 overlay重启虚拟机就回到了快照点。virsh blockcopy vm-centos7 vda \ --wait --finish \ --dest /data/kvm/images/rollback.qcow2实践中很多人会搞混 commit 和 rebase。一句话区分commit 是将差异数据合并到 backing filerebase 是修改 overlay 的 backing file 指向比如换成另一个基础镜像。两者不是一回事不要混淆。5. 常见问题与排查实录5.1 overlay 文件越来越大怎么办这是被问得最多的问题。overlay 的增长是正常且必然的因为所有新写入的数据都落在 overlay 里。但如果你发现 overlay 的体积增长速度远超预期多半是下面几种情况。第一虚拟机内部存在大量日志写入、临时文件写入这类写入会持续压进 overlay。解决办法是定期清理虚拟机内部日志或者把日志目录挂到独立磁盘/临时目录里尽量避免落盘到快照层。第二文件系统层面删除文件并不意味着 overlay 里对应的 cluster 被释放。qcow2 是 copy-on-write 模型虚拟机删除的数据只是文件系统视角删除了块设备底层并不一定执行了 discard 或者 trim。如果虚拟机启用了 fstrim并且磁盘是 virtio-scsi 且配置了 discard才可能把空闲块回传给 overlay。如果 overlay 实在太大最彻底的办法是新建一个 overlay把当前磁盘状态重新做一个快照然后把旧 overlay 卸载调走再手工合并或归档。不过要注意这个操作只能控制未来的增量过去已经写入的数据仍然停留在旧 overlay 中必须通过 commit 或块复制才能真正并入 base。还有一种歪门邪道是直接删 overlay前提是你确定不需要保留快照后的任何数据。删除前一定先确认 backing file 没被其他 overlay 共用否则会让别的链断裂。5.2 backing file 被移动或损坏后的修复backing file 路径对不上是最经典的故障。表现通常是虚拟机启动时报错qemu-img: Could not open overlay.qcow2: Failed to find a suitable image for backing file原因一般有两个一是 backing file 被移动或改文件名二是 overlay 里记录的路径和实际不匹配。排查第一步查看 overlay 里记录的路径qemu-img info overlay.qcow2如果路径不对用 rebase 修正qemu-img rebase -b /correct/path/base.qcow2 overlay.qcow2第二步检查 backing file 是否损坏。基础镜像损坏比较少见但如果 overlay 的数据都指向某个不存在的 base cluster启动后虚拟机会出现 IO 错误。建议先 qemu-img check base再看 overlay。base 文件被误删的时候如果 overlay 里只有少量业务数据重建一个同名同大小的空 base 也可能让读操作恢复但写入部分会丢失这是最万不得已的做法。正常节奏还是要有备份。5.3 警惕同名“overlay”Docker overlay2 清理与误删搜索“overlay”的时候很多人会搜到 Docker。Docker 的 overlay2 存储驱动和 QEMU 的 overlay 完全是两码事但都有同名关键词容易混淆。Docker 的 overlay2 目录位于 /var/lib/docker/overlay2/里面保存的是镜像层和容器可写层的联合挂载数据。如果 Docker 磁盘爆满清理方式通常是docker system prune -a docker image prune -a这些都是从 Docker 生态内部发起的清理会正确处理镜像层引用关系。但很多人图省事直接手动删 /var/lib/docker/overlay2/ 里的目录这是非常危险的因为容器进程可能还在使用这些层删除后轻则容器写入失败重则整个 Docker 数据目录损坏。我的建议是只要碰到 overlay2 目录一律通过 Docker 命令清理不要直接碰文件系统。如果是 KVM 场景下的 overlay文件名一般是自定义的比如 xxx-overlay.qcow2不会出现在 Docker 目录里。先确认背景再动手避免误伤。5.4 快照链路断裂、权限问题和空间预判快照链越深问题越隐蔽。我遇到过 overlay 链中间某层文件权限变成 root-onlyQEMU 进程使用的非 root 用户无法读取虚拟机直接卡死。解决方式很简单确定 QEMU 运行用户把链上所有镜像文件的属组设置为该用户并给到 644 权限。空间预判也是老生常谈。创建 overlay 时它只有 193KB但一旦业务写入起来它是会增长的所以做外部快照前一定要给 overlay 所在分区留出足够冗余。我一般会预留当前基础镜像 1.5 倍的可用空间给快照层宁多勿少。最后是链深度问题。从基础镜像开始连续做多次外部快照后读放大会越来越明显。我建议定期观察虚拟机的磁盘延迟如果发现大量 io wait先用 qemu-img info 看下快照链深度超过四层就考虑 commit 合并掉一部分控制在合理范围。6. 我的一些实操心得外部快照这套机制我前前后后用坏了不知道多少块盘才彻底摸透。给我的最大感受是backing file 和 overlay 的组合本质上是在用最小的空间代价换最高的数据安全性。真的要做生产级快照方案的话不要只靠手工命令。我建议把创建 overlay、检查链状态、定期 commit 全部写成脚本纳入定时任务减少人工干预出错的可能。另外至少保留两条独立的快照链一条给业务运行一条给备份归档避免某一个 overlay 异常时全盘皆输。对刚接触的朋友我的建议是先拿一台测试虚拟机反复练习冷备流程再上热备不要一上来就 QMP 手搓否则出问题的时候你连排查方向都没有。所有工具命令都问一下为什么把 qcow2 的 COW 原理吃透比背一百条命令都管用。
返回列表