---
url: /courses/virtualization-tech/10-storage-qcow2-pools/index.md
---
# 第10课：虚拟存储抽象、QCOW2 核心机制与无损扩容

![AI生成示意图：虚拟化存储模型与容量架构](./assets/09/qcow2-storage-illustration.png)

::: tip 项目目标
在前面的课程中，虚拟机已经实现了双网卡网络互通与软件更新。但在真实企业生产环境中，随着业务数据激增，服务器往往需要挂接独立的数据盘，并在业务不中断的前提下完成存储空间的弹性扩容。

本课将带领你深入 Linux 虚拟化存储底层体系：建立涵盖\*\*存储池（Pool）—存储卷（Volume）—镜像文件（Image）—虚拟块设备（Block Device）—分区（Partition）—文件系统（Filesystem）\*\*的六层存储抽象认知；彻底搞懂虚拟容量、表观大小、物理占用与存储池余量“四本账”的本质区别；掌握 QCOW2 双级簇映射（Two-Level Cluster Lookup）寻址机理与四种预分配策略；并在 `svr01` 上完成从专用存储池 `lab-images` 创建、QCOW2 数据盘挂接、GPT 分区、ext4 持久化挂载，直至宿主机与来宾系统协同完成“三级平滑无损扩容”的全流程企业级运维实战。
:::

## 一、虚拟化存储抽象模型与目录存储池管理

在深入具体镜像操作前，必须从系统架构层面厘清 Linux 虚拟化存储的分层模型与 libvirt 存储池管理框架。

### 1.1 虚拟化存储六层抽象模型穿透

在物理服务器中，应用程序将文件写入挂载在硬盘分区上的文件系统；而在虚拟化环境中，一个 I/O 请求需要穿透 6 层严密的抽象转换才能最终落盘：

```mermaid
flowchart TD
    subgraph L1_L3 ["宿主机物理与虚拟化管理平面 (Host Layer)"]
        L1["第 1 层：libvirt 存储池 (Storage Pool)<br><code>lab-images</code>（目录 /var/lib/libvirt/lab-images）"]
        L2["第 2 层：libvirt 存储卷 (Storage Volume)<br><code>v9-data.qcow2</code>（逻辑存储实体，声明容量与格式）"]
        L3["第 3 层：宿主机镜像文件 (Host Image File)<br><code>/var/lib/libvirt/lab-images/v9-data.qcow2</code>（QCOW2 Header + L1/L2 簇映射表）"]
        L1 --> L2
        L2 --> L3
    end

    subgraph L4 ["虚拟化抽象与总线中继 (QEMU Emulation)"]
        L4_DEV["第 4 层：QEMU 虚拟块设备 (VirtIO Block Device)<br><code>/dev/vdb</code>（PCI 总线 VirtIO 块设备驱动与环形缓冲区）"]
        L3 --> L4_DEV
    end

    subgraph L5_L6 ["来宾操作系统存储平面 (Guest OS Layer)"]
        L5["第 5 层：来宾磁盘分区 (Guest Partition)<br><code>/dev/vdb1</code>（GPT 分区表扇区划分）"]
        L6["第 6 层：来宾文件系统与挂载点 (Guest Filesystem & Mount Point)<br><code>/data</code>（ext4 超级块、inode 索引与业务数据）"]
        L4_DEV --> L5
        L5 --> L6
    end
```

每一层都有其独立的职责边界与观测工具：

1. **第 1 层：libvirt 存储池（Storage Pool）**：宿主机存储资源的逻辑聚合边界。负责底层存储介质（本地目录、LVM 卷组、NFS 网络存储、Ceph 集群）的配额控制与生命周期管理。
2. **第 2 层：libvirt 存储卷（Storage Volume）**：存储池内被统一纳管的独立虚拟磁盘单元。提供卷的分配、克隆、格式定义与容量统计。
3. **第 3 层：宿主机镜像文件（Host Image File）**：宿主机文件系统上的具体实体文件。包含 QCOW2 文件头、L1/L2 簇表以及按需分配的 64KB 数据簇。
4. **第 4 层：QEMU 虚拟块设备（VirtIO Block Device）**：QEMU 模拟向来宾机暴露的硬件级块设备（`/dev/vdb`），通过 VirtIO 驱动实现极低开销的内存 DMA 传输。
5. **第 5 层：来宾磁盘分区（Guest Partition）**：来宾操作系统内核根据 GPT/MBR 分区表识别的连续扇区段（`/dev/vdb1`）。
6. **第 6 层：来宾文件系统与挂载点（Guest Filesystem）**：在分区上格式化建立的 ext4/xfs 文件系统，并挂载到系统目录树（`/data`），向业务进程提供标准的 POSIX 文件读写接口。

### 1.2 libvirt 存储池类型与目录存储池（Dir Pool）架构

libvirt 支持多种底层存储后端，以满足从单机开发测试到超大规模云数据中心的各种需求：

| 存储池类型 (Type) | 底层物理介质与实现 | 共享与跨节点热迁移 | 存储特性与高级功能 | 典型工业应用场景 |
| :--- | :--- | :--- | :--- | :--- |
| **dir（目录池）** | 本地文件系统普通目录（EXT4/XFS） | 不支持跨物理机共享 | 结构最简单、便于备份与拷贝、开箱即用 | **单机虚拟化、教学实训、本地桌面云** |
| **fs（格式化文件系统池）** | 未挂载的裸块设备，libvirt 自动挂载 | 不支持跨物理机共享 | 独占物理分区或磁盘，避免其他进程干扰 | 专用数据盘隔离部署 |
| **netfs（网络文件系统池）** | NFS / GlusterFS 网络共享挂载点 | **完美支持虚拟机跨节点热迁移** | 集中式存储，网络带宽与 NAS 性能决定 I/O | 中小型企业私有云、虚拟化高可用集群 |
| **logical（LVM 逻辑卷池）** | 物理卷组（Volume Group, VG） | 需配合共享存储（如 SAN/iSCSI） | 基于 LVM 逻辑卷分配，读写性能极高，无宿主文件系统开销 | 数据库虚拟机、高性能计算 (HPC) |
| **iscsi（IP SAN 存储池）** | 远端 IP SAN 目标器 (Target) | 支持集中式共享 | 块级直连，支持物理多路径 (Multipath) | 企业级 SAN 存储阵列接入 |
| **rbd（Ceph 分布式块存储）** | Ceph RADOS Block Device | **云原生标准，秒级跨机迁移与快照** | 强一致性、多副本冗余、支持海量水平扩展 | OpenStack、大型公有云/私有云基础设施 |

在本课程实训中，我们将构建工业标准的专用目录存储池 `lab-images`，用于专门隔离存放实验用虚拟磁盘卷。

### 1.3 实训：创建与管理 libvirt 专用目录存储池 lab-images

::: tip 实训目标与业务场景
在实际运维中，随意将虚拟磁盘散落在管理员的家目录或根目录下是严重的工程违规：不仅极易因误操作被删除，而且无法统一监控空间配额与实施权限隔离。

**本实训目标**：在宿主机建立一个符合生产权限规范的独立目录存储池 `lab-images`（对应路径 `/var/lib/libvirt/lab-images`），并通过 libvirt 实现生命周期管理与开机自启动，为后续存放虚拟机独立数据盘奠定基石。
:::

#### 步骤 1：宿主机创建目录并配置归属权限

【操作目的与机制原理解析】
libvirt 的虚拟化工作进程 `qemu` 通常以专用的非特权系统用户 `libvirt-qemu` 和属组 `kvm` 运行，以防止虚拟机被攻破后直接获取宿主机的 root 权限。因此，在建立镜像存放目录后，必须显式将目录的所有权赋予 `libvirt-qemu:kvm`，并设置 `775` 权限，确保 QEMU 拥有读写磁盘镜像文件的完整权限。

```bash
# 【宿主机终端 Host】
# 1. 创建专属镜像目录（-p 参数确保父级目录缺失时自动递归创建）
sudo mkdir -p /var/lib/libvirt/lab-images

# 2. 设置属主为 libvirt-qemu，属组为 kvm（确保虚拟化进程具备读写权限）
sudo chown -R libvirt-qemu:kvm /var/lib/libvirt/lab-images
sudo chmod 775 /var/lib/libvirt/lab-images

# 3. 验证目录权限状态
ls -ld /var/lib/libvirt/lab-images
```

【结果观察与原理解释】
执行 `ls -ld` 后，终端应显示 `drwxrwxr-x ... libvirt-qemu kvm`，表明属主与权限配置完全符合虚拟化守护进程的安全运行基线。

#### 步骤 2：使用 virsh pool-define-as 持久化定义存储池

【操作目的与机制原理解析】
libvirt 遵循“声明与激活解耦”的管理哲学：

* `pool-define-as`：仅在 libvirt 的配置数据库中注册一份 XML 元数据定义（位于 `/etc/libvirt/storage/`），声明“有一个名为 `lab-images` 的目录型存储池，对应实际路径是 `/var/lib/libvirt/lab-images`”。
* 此时存储池仅完成登记，尚未处于活动运行状态（`inactive`）。

```bash
# 【宿主机终端 Host】
# 定义名为 lab-images 的 dir 类型存储池
virsh pool-define-as lab-images dir --target /var/lib/libvirt/lab-images

# 查看新定义的存储池（此时处于 inactive 状态）
virsh pool-list --all
```

【结果观察与原理解释】
在输出列表中可以看到 `lab-images` 处于 `inactive`（未激活）且 `Autostart` 为 `no`，说明存储池配置已持久化写入，等待进一步初始化与激活。

#### 步骤 3：构建并激活存储池与配置自启动

【操作目的与机制原理解析】

* `pool-build`：校验并构建存储池底层结构。
* `pool-start`：正式将存储池载入 libvirt 内存运行时，开始监控该目录下的文件对象。
* `pool-autostart`：在 `/etc/libvirt/storage/autostart/` 创建软链接。**这一步在生产中极其关键**，若未设置自启动，宿主机一旦重启，该存储池将处于离线状态，所有依赖该存储池的虚拟机都会因“找不到磁盘文件”而开机失败！

```bash
# 【宿主机终端 Host】
# 1. 构建存储池底层结构
virsh pool-build lab-images

# 2. 激活存储池，使其进入 running 运行状态
virsh pool-start lab-images

# 3. 设置存储池随宿主机虚拟化服务开机自启
virsh pool-autostart lab-images

# 4. 验证存储池最终状态
virsh pool-list --all
```

【结果观察与原理解释】
预期输出中，`lab-images` 的 State 必须为 `active`，Autostart 必须为 `yes`：

```text
 Name         State    Autostart
----------------------------------
 default      active   yes
 lab-images   active   yes
```

#### 步骤 4：验证存储池状态与物理容量指标

【操作目的与机制原理解析】
通过 `virsh pool-info` 可以直接向 libvirt 查询存储池的整体容量状态。目录存储池的容量并非虚拟的，它直接反映了该目录所在的宿主机根文件系统的实际容量（Capacity）、已用空间（Allocation）与剩余可用余量（Available）。

```bash
# 【宿主机终端 Host】
virsh pool-info lab-images
```

预期输出将展示底层宿主文件系统提供的真实容量底座：

```text
Name:           lab-images
UUID:           a1b2c3d4-e5f6-7890-abcd-ef0123456789
State:          running
Persistent:     yes
Autostart:      yes
Capacity:       50.00 GiB
Allocation:     12.30 GiB
Available:      37.70 GiB
```

## 二、磁盘镜像格式与存储容量四大核心维度

在虚拟化领域，虚拟磁盘镜像文件的底层组织结构直接决定了存储效率、读写性能与管理功能。

### 2.1 RAW 格式与 QCOW2 格式深度对比

虚拟化中存在两大主流磁盘格式：\*\*RAW（裸格式）\*\*与 **QCOW2（QEMU Copy-On-Write 格式第二代）**：

| 特性维度 | RAW 裸格式镜像 | QCOW2 高级虚拟磁盘镜像 |
| :--- | :--- | :--- |
| **底层存储结构** | 纯平面二进制字节流（与物理硬盘扇区 1:1 映射） | 包含 Header、L1/L2 双级簇映射表与按需数据簇 |
| **空间分配机制** | 占用连续物理扇区（或依赖宿主文件系统的 sparse 特性） | **写时分配（Copy-On-Write / Allocate-on-Demand）** |
| **快照与备份支持** | 原生不支持内部快照（需依赖底层 LVM 或外部机制） | **原生支持内部快照、差分增量盘（Backing File）与外部快照** |
| **压缩与加密** | 不支持文件级原生压缩与加密 | **原生支持 zlib/zstd 簇级压缩与 AES 加密** |
| **读写 I/O 性能** | 零元数据寻址开销，性能最高（等同物理盘） | 有轻微 L1/L2 查表开销；写入时有新簇分配系统调用开销 |
| **格式通用性** | 跨平台兼容性最高，所有虚拟化平台通用 | KVM / QEMU 原生首选格式 |
| **典型适用场景** | 极端追求 I/O 极限的生产数据库与裸设备替代 | **绝大多数生产虚拟机、云桌面、快速克隆、教学实训** |

### 2.2 存储容量四大核心维度与四种预分配策略

在管理 QCOW2 磁盘时，初学者常常混淆以下“四本账”：

1. **虚拟容量（Virtual Size / 标称容量）**：QEMU 向来宾 OS 呈现的最大逻辑块设备空间（例如 20GiB）。来宾系统格式化与创建分区均以此为依据。
2. **文件表观大小（Stated / Apparent Size）**：宿主机 `ls -lh` 显示的文件名义大小。它反映的是稀疏文件逻辑尾部的偏移量。
3. **实际物理占用（Actual Disk Allocation）**：宿主底层文件系统真正分配的物理 Extents / 数据簇总和（通过 `du -sh` 或 `qemu-img info` 观测）。
4. **存储池可用余量（Pool Available Space）**：宿主物理存储池剩余可用空间（`virsh pool-info` 中的 `Available`）。

#### QCOW2 四种预分配策略（Preallocation Modes）

创建 QCOW2 镜像时，通过 `-o preallocation=<mode>` 参数可精确权衡空间与性能：

* `off`（默认）：纯精简置备，初始仅占几百 KB。按需实时分配，最节约空间，但存在存储超售风险。
* `metadata`：预先分配并初始化全部 L1/L2 簇映射表。写入数据时无需扩展元数据表，大幅降低写锁与碎片，提升随机写性能。
* `falloc`：调用 `posix_fallocate()` 在文件系统预留并锁定全部物理块。性能极高且无物理碎片，彻底杜绝超售爆池风险。
* `full`：全量写入二进制 0x00 填充全部物理扇区。创建耗时最长，性能等同于 RAW 格式。

下面通过交互式动画，完整观察动态分配演进、预分配模式对比以及存储超售风险防线：

::: tip 观察要点与操作指引

1. 在\*\*“四本账核心容量维度”**中，点击**“写入 2GB 数据”**与**“写入 5GB 数据”\*\*，观察虚拟磁盘的“实际物理占用”如何逐步上升，而“虚拟标称上限”始终保持 20GB 不变。
2. 重点对比终端回显面板中 `ls -lh`（表观 20G）与 `du -sh`（实际几百 MB）的巨大差异，体会精简置备的本质。
3. 切换至\*\*“四种预分配策略深度对比”\*\*，观察从 `off` 到 `full` 在初始物理占用、创建耗时与 I/O 性能上的阶梯式权衡。
4. 切换至\*\*“存储超售风险模拟”**，点击**“自动演练”\*\*，观察当宿主物理存储池耗尽时，QEMU 为保护来宾文件系统元数据如何触发 `paused (io-error)` 保护性挂起。
5. 切换至\*\*“六层存储抽象穿透模型”\*\*，点击各层级，梳理从物理存储池到挂载点的全链路对应关系。
   :::

### 2.3 实训：创建 QCOW2 存储卷、挂接虚拟机并完成文件系统初始化

::: tip 实训目标与业务场景
在企业级云计算与生产服务器中，“系统盘与数据盘物理分离”是一条铁律。系统盘仅存放 Linux 操作系统本身，而数据库、日志和业务文件存放在独立挂接的数据盘上。这样即便操作系统崩溃需要推倒重装，数据盘卸载后可以无缝挂接到新机器上，数据安然无恙。

**本实训目标**：

1. 在 `lab-images` 存储池中创建一块 4GiB 的 QCOW2 独立数据卷 `v9-data.qcow2`；
2. 在关机状态下以标准的 VirtIO 高性能总线将其持久附加到 `svr01`（呈现为 `/dev/vdb`）；
3. 通过 SSH 登录虚拟机内部，使用 `parted` 工具进行 1MiB 扇区对齐的 GPT 分区；
4. 格式化为 ext4 文件系统，配置 `/etc/fstab` 实现基于 UUID 的持久化自动挂载，并写入基准测试数据。
   :::

#### 步骤 1：在 lab-images 存储池中创建 4GiB QCOW2 卷

【操作目的与机制原理解析】
使用 `virsh vol-create-as` 命令在存储池内派生一个存储卷。指定 `--format qcow2` 声明使用动态精简置备格式。创建完成后，该卷立即被 libvirt 索引记录。

```bash
# 【宿主机终端 Host】
# 1. 在 lab-images 存储池中创建虚拟容量为 4GiB 的 QCOW2 卷
virsh vol-create-as lab-images v9-data.qcow2 4G --format qcow2

# 2. 检查存储卷详细属性
virsh vol-info v9-data.qcow2 lab-images
```

【结果观察与原理解释】
执行 `vol-info` 后，输出将清晰反映“虚拟标称容量”与“当前物理分配”的巨大反差：

```text
Name:           v9-data.qcow2
Type:           file
Capacity:       4.00 GiB
Allocation:     196.00 KiB
```

虚拟容量（Capacity）为 4.00 GiB，而宿主机当前实际仅分配了 196.00 KiB 用于存放 QCOW2 文件头与初始 L1 映射表。这就是精简置备（Thin Provisioning）的实际体现。

#### 步骤 2：执行关机门禁并离线持久化挂接磁盘

【操作目的与机制原理解析】
为什么必须先关闭虚拟机？
在生产实践中，在线热插（Hotplug）磁盘虽然可行，但若要在虚拟机的 XML 配置文件中永久保留该设备定义，冷添加（Cold Attach）拥有最高确定性和安全性，能避免热插时总线抢占与驱动事件丢失。
命令参数拆解：

* `svr01`：目标虚拟机名称。
* `/var/lib/libvirt/lab-images/v9-data.qcow2`：源镜像文件绝对路径。
* `vdb`：分配给来宾机的第二块虚拟磁盘设备代号（第一块系统盘通常为 `vda`）。
* `--driver qemu --subdriver qcow2`：显式声明使用 QEMU 的 QCOW2 驱动解析，**坚决防御 CVE-2008-2004 格式混淆漏洞**。
* `--targetbus virtio`：使用半虚拟化 VirtIO 块设备驱动，绕过低效的 IDE/SATA 硬件模拟，实现近乎原生硬件的高并发 I/O。
* `--config`：将磁盘设备定义写入虚拟机永久 XML 配置文件，保证重启后依然存在。

```bash
# 【宿主机终端 Host】
# 1. 安全关闭测试虚拟机 svr01
virsh shutdown svr01

# 2. 等待并确认虚拟机已彻底处于 shut off 状态
virsh list --all | grep svr01

# 3. 执行持久化离线挂接
virsh attach-disk svr01 /var/lib/libvirt/lab-images/v9-data.qcow2 vdb \
  --driver qemu \
  --subdriver qcow2 \
  --targetbus virtio \
  --config

# 4. 验证虚拟机 XML 配置中的块设备列表（确认 vda 与 vdb 均已在册）
virsh domblklist svr01
```

【结果观察与原理解释】
预期输出中，`svr01` 的块设备列表明确展示了两块磁盘：

```text
 Target   Source
------------------------------------------------------------
 vda      /var/lib/libvirt/images/svr01.qcow2
 vdb      /var/lib/libvirt/lab-images/v9-data.qcow2
```

#### 步骤 3：启动虚拟机并通过 IP 地址 SSH 登录

【操作目的与机制原理解析】
启动虚拟机后，QEMU 将根据更新后的 XML 配置，在虚拟机的 PCI 总线上实例化一块 VirtIO 块设备控制器，并将 `v9-data.qcow2` 作为后端存储接入。我们通过标准 SSH 协议登录虚拟机，进行操作系统层面的磁盘初始化。

```bash
# 【宿主机终端 Host】
# 1. 启动虚拟机
virsh start svr01

# 2. 查询虚拟机动态获取的 IP 地址（通过 guest-agent 或 DHCP leases）
virsh domifaddr svr01

# 3. 通过 SSH 远程登录虚拟机（假定 IP 为 192.168.122.100）
ssh student@192.168.122.100
```

#### 步骤 4：来宾内确认新增块设备

【操作目的与机制原理解析】
Linux 内核在引导或热加载时，通过 VirtIO-BLK 驱动探测到第二块磁盘，并在 `/dev` 目录下自动生成设备节点 `/dev/vdb`。使用 `lsblk` 命令检查硬件识别情况。

```bash
# 【虚拟机 svr01】
lsblk /dev/vdb
```

【结果观察与原理解释】
预期输出显示一块大小为 4G、未分区的纯裸磁盘：

```text
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vdb  252:16   0   4G  0 disk
```

此时磁盘上没有任何分区表，操作系统无法直接使用，必须先进行分区与格式化。

#### 步骤 5：使用 parted 工具创建 GPT 分区表与主分区

【操作目的与机制原理解析】

* **为什么使用 GPT 分区表？** 传统的 MBR 分区表最多仅支持 2TB 磁盘且只能有 4 个主分区；现代 Linux 生产环境全面采用 GPT（GUID Partition Table），支持海量存储与健全的 CRC32 数据校验。
* **为什么起始扇区要 1MiB 对齐？** 传统机械硬盘以 512 字节为扇区，现代 SSD 和虚拟化宿主文件系统普遍采用 4KB 扇区。从 1MiB（2048 扇区）起始分区，能够保证来宾文件系统的每个 I/O 请求与宿主机底层 4KB 物理块完全对齐，避免因“跨扇区读写（跨块惩罚）”导致 I/O 性能下降 30%~50%！

```bash
# 【虚拟机 svr01】
# 1. 在 /dev/vdb 上创建现代 GPT 分区表标签
sudo parted /dev/vdb mklabel gpt

# 2. 创建占用 100% 空间的 ext4 主分区（起始扇区 1MiB 对齐）
sudo parted /dev/vdb mkpart primary ext4 1MiB 100%

# 3. 查看分区对齐与扇区划分结果
sudo parted /dev/vdb print
```

【结果观察与原理解释】
输出中显示 `Partition Table: gpt`，且生成了编号为 1 的分区 `/dev/vdb1`，起始位置为 `1049kB (1MiB)`，大小为 `4294MB (4GiB)`。

#### 步骤 6：格式化 ext4 文件系统

【操作目的与机制原理解析】
分区只是在磁盘开头划分了扇区范围，要让 Linux 能够读写文件，必须在分区上写入文件系统的元数据（超级块 Superblock、块组描述符、inode 索引表与空闲块位图）。这里使用 Linux 极其稳定的经典日志文件系统 `ext4`，并为该分区打上文件系统卷标 `labdata`。

```bash
# 【虚拟机 svr01】
sudo mkfs.ext4 -L labdata /dev/vdb1
```

【结果观察与原理解释】
终端会输出 block size（4096 字节）、创建的 Inode 数量以及日志记录区（Journal）初始化完成的信息，表明 `/dev/vdb1` 已具备完整的文件系统结构。

#### 步骤 7：配置 /etc/fstab 持久化挂载至 /data 并验证

【操作目的与机制原理解析】

* **为什么挂载必须使用 UUID，而不能使用 `/dev/vdb1`？** 在 Linux 中，设备名称（如 `/dev/vdb1`）是内核在开机时按扫描顺序动态分配的。如果未来为虚拟机添加新网卡、调整 PCI 插槽或挂接其他磁盘，设备名可能漂移变成 `/dev/vdc1`！如果 `/etc/fstab` 写死 `/dev/vdb1`，系统开机将找不到设备而陷入**紧急恢复模式（Emergency Mode）**。UUID 是格式化时固化在文件系统超级块中的全球唯一标识符，永远不会漂移。
* **`/etc/fstab` 参数意义**：`UUID=xxx /data ext4 defaults 0 2`
  * `/data`：挂载点目录。
  * `ext4`：文件系统类型。
  * `defaults`：使用默认挂载参数（rw, suid, dev, exec, auto, nouser, async）。
  * `0`：禁用 dump 备份。
  * `2`：开机 fsck 磁盘检查顺序（根分区为 1，其他数据分区为 2）。
* `sudo mount -a`：强制测试挂载 `/etc/fstab` 中的所有条目，**用于在重启前验证语法正确性，防止写错导致开机卡死**。

```bash
# 【虚拟机 svr01】
# 1. 获取 /dev/vdb1 的唯一 UUID 并暂存到变量
VDB_UUID=$(sudo blkid -s UUID -o value /dev/vdb1)
echo "分区 UUID: $VDB_UUID"

# 2. 创建系统挂载点目录
sudo mkdir -p /data

# 3. 向 /etc/fstab 追加持久化挂载配置
echo "UUID=$VDB_UUID /data ext4 defaults 0 2" | sudo tee -a /etc/fstab

# 4. 测试挂载 fstab 中的全部条目（无报错则说明配置语法完全正确）
sudo mount -a

# 5. 验证挂载状态与可用容量
df -h /data
```

【结果观察与原理解释】
预期输出显示 `/data` 挂载成功，总容量约为 3.9G（由于 ext4 预留了约 5% 的元数据与超级块空间）：

```text
Filesystem      Size  Used Avail Use% Mounted on
/dev/vdb1       3.9G   24K  3.7G   1% /data
```

#### 步骤 8：写入基准测试数据与生成 MD5 完整性校验码

【操作目的与机制原理解析】
在现实运维中，对正在使用的数据盘进行扩容，**最核心的考核指标就是原有数据是否 100% 完好无损**。我们在 `/data` 目录下写入一份基准测试数据，并利用不可逆的 MD5 哈希算法生成校验指纹，作为扩容后验证“数据零丢失”的客观黄金标准。

```bash
# 【虚拟机 svr01】
# 1. 向挂载点写入基准测试业务数据
echo "StarRiver Storage Cluster V9 Data Integrity Check - 2026" | sudo tee /data/integrity.txt

# 2. 计算文件的 MD5 哈希值并保存到指纹文件
md5sum /data/integrity.txt | sudo tee /data/integrity.md5

# 3. 查看生成的完整性校验码
cat /data/integrity.md5
```

## 三、QCOW2 双级簇映射与存储扩容三级流水线

掌握了磁盘初始化之后，面对业务容量告急，必须掌握企业级在线无损扩容的核心机理。

### 3.1 QCOW2 双级簇映射寻址机理

QCOW2 之所以能够支持动态增长、快照与稀疏特性，核心在于其\*\*双级簇映射（Two-Level Cluster Table Lookup）\*\*体系：

```mermaid
flowchart LR
    GVA["来宾系统虚拟地址 (GVA)<br>LBA / Byte Offset"] --> SPLIT["二进制位切分<br>(Bit Splitting)"]

    SPLIT -->|高 35 位| L1_IDX["L1 Table Index<br>(offset >> 29)"]
    SPLIT -->|中 13 位| L2_IDX["L2 Table Index<br>(offset >> 16 & 0x1fff)"]
    SPLIT -->|低 16 位| CLUST_OFF["In-Cluster Offset<br>(offset & 0xffff)"]

    L1_IDX --> L1_TAB["L1 映射表<br>(宿主内存 / 文件头 0x30000)"]
    L1_TAB -->|指向 L2 表偏移| L2_TAB["L2 映射表<br>(8192 个表项)"]
    L2_IDX --> L2_TAB

    L2_TAB -->|指向 64KB 簇基址| DATA_C["宿主物理数据簇<br>(64KB Data Cluster)"]
    CLUST_OFF --> DATA_C
    DATA_C --> PHY_IO["宿主底层物理文件系统 I/O"]
```

1. **簇大小（Cluster Size）**：QCOW2 默认以 64 KiB（$2^{16} = 65536$ 字节）为一个基本数据簇。
2. **L1 表（Level 1 Table）**：顶级索引表，记录各个 L2 表在宿主机文件中的物理起始偏移。
3. **L2 表（Level 2 Table）**：二级索引表，每个 L2 表大小为一个簇（64KB），包含 $65536 / 8 = 8192$ 个 64 位表项。每个表项指向一个 64KB 物理数据簇。
4. **按需分配（Allocate-on-Demand）**：当来宾系统初次写入未分配的扇区时，QEMU 在宿主文件尾部分配一个新的 64KB 物理簇，将其物理偏移回填入对应的 L2 表槽位，并完成数据写入。

::: tip 直观理解：双级路由与物流派送类比
双级簇表的寻址过程与现代物流派送网络非常相似：

* **L1 映射表（省级分拨中心）**：虚拟地址的高 35 位作为索引，快速定位包裹应该发往哪一个本地中转站（指向对应的 L2 表物理偏移）。
* **L2 映射表（街道网点/驿站）**：虚拟地址的中间 13 位作为索引，精确查出包裹属于哪个小区的数据货架（指向宿主机文件中的 64KB 数据簇基址）。
* **簇内偏移（门牌号与房号）**：虚拟地址的低 16 位（0~65535 字节），直接定位到货架内部的具体门牌字节位置。

通过这种“以表查表”的分级索引结构，QCOW2 文件初始创建时只需占用几百 KB 存放基础元数据，后续写入数据时再动态追加 64KB 簇，实现了极高的存储利用率与灵活度。
:::

### 3.2 存储扩容三级流水线机制剖析

许多初学者常常误以为“在宿主机执行了扩容命令，虚拟机里的磁盘就自动变大了”。事实上，扩容必须经历严格的**三级联动流水线**：

```mermaid
flowchart TD
    subgraph S1 ["第一级：宿主机底层镜像物理扩容 (Host Layer)"]
        A1["qemu-img resize v9-data.qcow2 6G"] --> A2["QEMU 修改 QCOW2 Header Virtual Size 为 6GiB"]
        A2 --> A3["virsh pool-refresh lab-images（更新 libvirt 池元数据）"]
    end

    subgraph S2 ["第二级：来宾内核块设备感知与分区拉伸 (Guest OS Layer)"]
        B1["来宾启动后识别到 /dev/vdb 总扇区数扩展至 6G"] --> B2["执行 sudo growpart /dev/vdb 1"]
        B2 --> B3["重写 GPT 分区表，将 /dev/vdb1 结束扇区延伸至 6G 末端"]
    end

    subgraph S3 ["第三级：来宾文件系统在线平滑扩展 (Guest Filesystem Layer)"]
        C1["执行 sudo resize2fs /dev/vdb1（无需 umount！）"] --> C2["在线更新 Ext4 超级块与块组描述符 (Block Groups)"]
        C2 --> C3["/data 可用容量瞬间跃升至 5.9G，历史数据 100% 完好"]
    end

    S1 --> S2
    S2 --> S3
```

* **第一级（宿主机）**：`qemu-img resize` 仅修改镜像文件的元数据 Header，扩大虚拟扇区上限。此时物理数据簇并未分配，来宾分区表与文件系统毫不知情。
* **第二级（来宾分区）**：`growpart` 修改来宾磁盘上的 GPT 分区表结构，把分区末尾扇区指针移动到新容量边界。此时分区容量变大，但文件系统超级块仍记录旧块数。
* **第三级（来宾文件系统）**：`resize2fs` 与 Linux 内核 ext4 驱动在线协同，动态新增块组描述符与空闲块位图，将额外空间划入文件系统，全过程**业务零中断、数据零丢失**。

下面通过交互式动画，全链路跟踪双级簇映射寻址与扩容三级流水线：

::: tip 观察要点与操作指引

1. 在\*\*“QCOW2 双级簇映射寻址”\*\*中，点击不同的地址预设（如“Ext4 超级块区域”与“扩容后未写入区域”），观察虚拟地址如何被自动切分为 L1 索引、L2 索引与簇内偏移。
2. 观察未分配区域的仿真反应：读操作直接向来宾返回全零，物理磁盘零占用；写操作将触发 On-Demand 动态簇分配。
3. 切换至\*\*“存储扩容三级流水线”**，点击**“自动演示”\*\*，观察 4 层的同步容量仪表盘：
   * 宿主机扩容后：只有第 1 层变大；
   * growpart 执行后：第 2、3 层变大；
   * resize2fs 执行后：第 4 层文件系统真正变大，且 MD5 校验码 100% 匹配！
4. 切换至\*\*“Magic 幻数与文件头探测”\*\*，点击不同的测试文件名，理解 Linux `file` 命令如何穿透文件名后缀，直接基于前 4 字节 `QFI\xfb` 识别真实格式。
   :::

### 3.3 实训：QCOW2 磁盘离线扩容与来宾文件系统在线平滑扩展

::: tip 实训目标与业务场景
在实际生产中，当 `/data` 分区使用率达到 90% 告警时，运维团队必须在确保业务数据不损坏的前提下实施扩容。

**本实训目标**：

1. 宿主机侧执行离线扩容，将 `v9-data.qcow2` 标称容量从 4GiB 扩展至 6GiB；
2. 虚拟机内部利用 `growpart` 工具在线拉伸 GPT 分区边界；
3. 利用 `resize2fs` 在线扩展 ext4 文件系统超级块；
4. 通过 `md5sum -c` 完整性校验，闭环验证扩容全过程中原有数据零丢失。
   :::

#### 步骤 1：宿主机安全关闭虚拟机

【操作目的与机制原理解析】
QEMU 为保证镜像文件的一致性，在虚拟机运行期间会对镜像文件加上排他锁（Image File Lock）。如果在运行状态下强行使用外部工具修改底层镜像，极易引发元数据损坏。因此，第一步必须先正常优雅关机。

```bash
# 【宿主机终端 Host】
# 1. 正常关闭虚拟机
virsh shutdown svr01

# 2. 确认虚拟机已处于 shut off 状态
virsh list --all | grep svr01
```

#### 步骤 2：宿主机离线扩容 QCOW2 卷至 6GiB 并刷新存储池

【操作目的与机制原理解析】

* `qemu-img resize ... 6G`：打开 QCOW2 镜像文件头，修改 Virtual Disk Size 字段为 $6 \times 1024^3$ 字节。这一步仅修改头部几十个字节的元数据，不会向磁盘写入 2GB 的全零数据，耗时小于 0.01 秒。
* `virsh pool-refresh lab-images`：通知 libvirt 重新扫描存储池目录，同步更新存储卷的元数据缓存。

```bash
# 【宿主机终端 Host】
# 1. 将 v9-data.qcow2 虚拟容量扩容至 6GiB
qemu-img resize /var/lib/libvirt/lab-images/v9-data.qcow2 6G

# 2. 刷新 libvirt 存储池感知容量变化
virsh pool-refresh lab-images

# 3. 检查卷信息（确认容量变为 6.00 GiB，实际分配仍为几百 KB）
virsh vol-info v9-data.qcow2 lab-images
```

【结果观察与原理解释】
执行 `vol-info` 后，`Capacity` 变为 `6.00 GiB`，而 `Allocation` 依然保持初始的几百 KB，证明扩容操作只是扩大了逻辑上限，没有产生多余的物理空间浪费。

#### 步骤 3：启动虚拟机并通过 SSH 登录检查内核块设备识别

【操作目的与机制原理解析】
虚拟机重新开机时，VirtIO 块设备驱动读取宿主机下发的新硬件参数，并在内核中将 `/dev/vdb` 的总扇区数登记为 6GiB。

```bash
# 【宿主机终端 Host】
# 1. 启动虚拟机
virsh start svr01

# 2. 查询 IP 并通过 SSH 登录
virsh domifaddr svr01
ssh student@192.168.122.100
```

登录虚拟机后，执行 `lsblk` 观察内核与分区的识别状态：

```bash
# 【虚拟机 svr01】
lsblk /dev/vdb
```

【结果观察与原理解释】

```text
NAME   MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vdb    252:16   0   6G  0 disk
└─vdb1 252:17   0   4G  0 part /data
```

**关键现象观察**：磁盘总容量 `/dev/vdb` 已经变成 `6G`，但挂载的分区 `/dev/vdb1` 依然停留在 `4G`！这生动证明了：**宿主机扩容只作用于第一级硬件层，不会自动改变来宾系统的分区与文件系统**。

#### 步骤 4：来宾内使用 growpart 在线扩展 GPT 分区表

【操作目的与机制原理解析】
`growpart` 是现代云平台广泛采用的分区在线扩容工具（由 `cloud-guest-utils` 软件包提供）。它能在不卸载分区（无需 `umount`）且不损坏分区内任何数据的前提下，自动重写 GPT 分区表的结束扇区（Ending LBA），将其直接拉伸到磁盘的最末端。

::: tip 提示
若系统提示找不到 `growpart` 命令，可执行 `sudo apt update && sudo apt install -y cloud-guest-utils` 安装扩展工具包。
:::

```bash
# 【虚拟机 svr01】
# 1. 在线扩展 /dev/vdb 的第 1 分区
sudo growpart /dev/vdb 1

# 2. 再次查看块设备与分区层级
lsblk /dev/vdb
```

【结果观察与原理解释】
预期输出显示 `vdb1` 已成功拉伸至 6G：

```text
CHANGED: partition=1 start=2048 old: size=8386560 end=8388608 new: size=12580831 end=12582879
NAME   MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vdb    252:16   0   6G  0 disk
└─vdb1 252:17   0   6G  0 part /data
```

此时若执行 `df -h /data`，会发现可用空间依然是 3.9G。这是因为**分区表虽然扩大了，但 ext4 文件系统的超级块尚未感知到新增的磁盘块**。

#### 步骤 5：来宾内使用 resize2fs 在线扩展 ext4 文件系统

【操作目的与机制原理解析】
`resize2fs` 是 ext2/ext3/ext4 文件系统的在线伸缩工具。在 Linux 内核在线调整特性的支持下，无需卸载 `/data` 挂载点，`resize2fs` 会自动计算分区新增的扇区数，在线追加 ext4 块组（Block Groups）并更新超级块的总块数统计，瞬间将新增空间纳入文件系统管理。

```bash
# 【虚拟机 svr01】
sudo resize2fs /dev/vdb1
```

【结果观察与原理解释】
终端输出 `Filesystem at /dev/vdb1 is mounted on /data; on-line resizing required`，并提示总块数已扩展至 1572603 blocks，说明文件系统在线扩展大功告成！

#### 步骤 6：验证容量跃升与历史数据完整性校验

【操作目的与机制原理解析】
最后一步是关键验收：

1. 检查 `df -h /data` 确认可用空间是否已真正跃升至 5.9G；
2. 执行 `md5sum -c` 校验在步骤 8 写入的基准测试文件，严密证明整个三级扩容过程中没有丢失或损坏任何一个字节。

```bash
# 【虚拟机 svr01】
# 1. 验证文件系统可用容量跃升
df -h /data

# 2. 校验扩容前写入的基准测试数据 MD5 哈希指纹
cd /data
md5sum -c integrity.md5
```

【结果观察与原理解释】
预期输出：

```text
Filesystem      Size  Used Avail Use% Mounted on
/dev/vdb1       5.9G   28K  5.6G   1% /data

integrity.txt: OK
```

`Size` 成功显示为 5.9G，且 `integrity.txt: OK` 表明哈希校验 100% 吻合！这标志着从宿主机底层到虚拟机文件系统的三级平滑无损扩容取得圆满成功！

## 四、磁盘格式特征识别与镜像格式转换

在生产运维与安全审计中，严禁仅凭文件名后缀推测文件类型，必须掌握底层二进制探测与格式转换技术。

### 4.1 镜像文件头 Magic 幻数识别原理与文件类型探测

在 Linux 操作系统中，文件格式由文件开头的 **Magic 幻数** 决定：

* **QCOW2 格式**：偏移 `0x00 - 0x03` 为 `51 46 49 fb`（`QFI\xfb`）。
* **RAW 格式**：无特定文件头，直接由数据扇区或全零稀疏块构成。
* **ISO 光盘镜像**：偏移 `0x8000`（32KB 处）包含 `CD001` 标识符。

```bash
# 【宿主机终端 Host】
# 探测 QCOW2 文件头的前 32 字节十六进制结构
hexdump -C -n 32 /var/lib/libvirt/lab-images/v9-data.qcow2
```

输出将清晰展现 `QFI\xfb` 幻数及版本号：

```text
00000000  51 46 49 fb 00 00 00 03  00 00 00 00 00 00 00 00  |QFI.............|
00000010  00 00 00 00 00 00 00 10  00 00 00 01 80 00 00 00  |................|
```

::: warning 格式混淆漏洞防御（CVE-2008-2004）
早期 QEMU 曾默认采用“自动探测格式（Auto Probe）”策略。恶意攻击者若在 RAW 磁盘中写入 `QFI\xfb` 文件头并指定恶意的 `backing_file` 路径，宿主机在下次启动时会将其误判为 QCOW2 并读取宿主敏感文件，造成致命的虚拟机逃逸与信息泄露。

**规范防御铁律**：在 libvirt 虚拟机 XML 配置中，必须始终显式指定磁盘驱动与格式类型：

```xml
<driver name='qemu' type='qcow2'/>
```

严禁省略 `type='qcow2'` 导致系统退化为不可预测的自动探测！
:::

### 4.2 实训：使用 qemu-img info 与 qemu-img convert 进行格式探测与镜像转换

::: tip 实训目标与业务场景
在实际跨平台迁移与公有云交付中，不同云厂商对镜像格式的要求各异（例如部分超算平台要求 RAW 裸格式以榨取极致性能，而分发归档时需要转换为 QCOW2 压缩包）。

**本实训目标**：

1. 掌握基于文件头二进制特征的真实格式探测方法（`file` 与 `qemu-img info`）；
2. 使用 `qemu-img convert` 工具实现 QCOW2 与 RAW 镜像的无损格式互转；
3. 直观对比不同格式在表观大小与物理实际占用上的本质差异。
   :::

#### 步骤 1：扩展名伪装对比实验

【操作目的与机制原理解析】
在 Windows 中，文件类型往往依赖 `.exe`、`.docx` 等后缀名；而在 Linux 中，文件名后缀纯属人类阅读习惯，操作系统和虚拟化引擎完全基于文件头部的 Magic 字节识别类型。我们将一个真实的 QCOW2 文件重命名为 `.raw`，验证探测工具的真实判断。

```bash
# 【宿主机终端 Host】
# 1. 创建临时实验沙盒
mkdir -p /tmp/format-lab && cd /tmp/format-lab

# 2. 创建一个虚拟容量为 1GiB 的 QCOW2 测试镜像
qemu-img create -f qcow2 test-disk.qcow2 1G

# 3. 故意将其后缀名修改为 .raw
mv test-disk.qcow2 test-disk.raw

# 4. 使用 Linux 底层 file 命令基于 magic 字节探测
file test-disk.raw

# 5. 使用 qemu-img info 解析镜像元数据
qemu-img info test-disk.raw
```

【结果观察与原理解释】
输出证明：尽管文件名被改成了 `test-disk.raw`，但 `file` 输出依然是 `QEMU QCOW2 Image (v3)`，`qemu-img info` 也明确报告 `file format: qcow2`。这印证了：**修改文件名扩展名绝不能改变底层磁盘格式！**

#### 步骤 2：使用 qemu-img convert 将 QCOW2 转换为 RAW 格式

【操作目的与机制原理解析】
要真正改变磁盘格式，必须使用 `qemu-img convert`。它会顺序读取源 QCOW2 文件的 L1/L2 簇表，将分散的数据簇按逻辑 LBA 地址线性写入到新的 RAW 裸格式文件中。
参数解析：

* `-f qcow2`：显式指定源文件格式（防御自动探测攻击）。
* `-O raw`：显式指定输出目标格式为 RAW。

```bash
# 【宿主机终端 Host】
# 执行格式转换
qemu-img convert -f qcow2 -O raw test-disk.raw test-converted.raw

# 验证转换后生成文件的真实类型
file test-converted.raw
qemu-img info test-converted.raw
```

【结果观察与原理解释】
`qemu-img info` 输出显示 `file format: raw`，表明文件内部已成功重构为连续线性的 RAW 格式。

#### 步骤 3：对比 RAW 与 QCOW2 在表观大小与物理实际占用上的巨大差异

【操作目的与机制原理解析】
使用 `ls -lh`（查看文件逻辑表观大小）与 `du -sh`（查看宿主机实际分配的物理块大小）对比两个文件，直观建立对稀疏分配的深刻理解。

```bash
# 【宿主机终端 Host】
# 1. 查看表观大小（逻辑大小）
ls -lh test-disk.raw test-converted.raw

# 2. 查看物理实际磁盘占用
du -sh test-disk.raw test-converted.raw

# 3. 清理实验临时文件
cd ~ && rm -rf /tmp/format-lab
```

【结果观察与原理解释】

* `test-disk.raw`（实际为 QCOW2）：表观大小与实际占用均约为 196KB。
* `test-converted.raw`（实际为 RAW）：表观大小显示为 1.0G，但在支持稀疏文件（Sparse File）的 Ext4/XFS 文件系统上，其实际物理占用（`du -sh`）几乎为 0，只有在写入数据时才会真正占用宿主磁盘块。

## 五、常见问题排错矩阵（10.5）

在虚拟化存储池、磁盘挂接与无损扩容实训中，若遇到异常，请对照下表进行阶梯式分层排查：

| 故障现象 | 优先排查层级 | 定位检查命令 | 根本原因分析 | 规范解决措施 |
| :--- | :--- | :--- | :--- | :--- |
| **`virsh pool-start` 提示 Permission denied** | 宿主目录权限层 | `ls -ld /var/lib/libvirt/lab-images` | 存储池目标目录属主不属于 `libvirt-qemu` 进程，权限不足 | 执行 `sudo chown -R libvirt-qemu:kvm /var/lib/libvirt/lab-images` 并设置 `chmod 775` |
| **`virsh attach-disk` 提示 Target vdb already exists** | 虚拟机 XML 配置层 | `virsh domblklist svr01` | 目标设备别名 `vdb` 已被虚拟机中的其他磁盘或光驱占用 | 检查设备列表，改用下一个空闲别名（如 `vdc` 或 `vdd`）重新挂接 |
| **虚拟机内执行 `growpart` 提示 NOCHANGE** | 来宾分区表层 | `sudo parted /dev/vdb print``lsblk /dev/vdb` | 宿主机尚未对底层镜像执行扩容，或虚拟机尚未感知到新扇区 | 在宿主机确认已执行 `qemu-img resize`；若在线挂接需重启虚拟机或触发块设备重扫描 |
| **执行 `resize2fs` 提示 Bad magic number in super-block** | 文件系统类型层 | `lsblk -f /dev/vdb1``sudo blkid` | 该分区格式化为 XFS 文件系统而非 ext4（XFS 必须使用 `xfs_growfs` 扩展） | 确认文件系统类型，若是 XFS 则使用 `sudo xfs_growfs /data` 进行在线扩展 |
| **宿主机执行 `qemu-img resize` 提示 Image is in use** | 进程文件锁层 | `virsh domstate svr01``lsof \| grep v9-data` | 虚拟机处于运行中状态，QEMU 对镜像文件持有排他锁 (QEMU Image Lock) | 先执行 `virsh shutdown svr01` 关闭虚拟机，再在离线状态下执行镜像扩容 |
| **宿主机物理磁盘满，虚拟机陷入 `paused (io-error)`** | 存储超售与底层物理层 | `df -h``virsh pool-info lab-images` | 动态分配引发超售爆池，宿主返回 `ENOSPC` 错误，QEMU 触发保护性挂起 | 严禁强制拔电！立即为宿主释放物理磁盘空间，随后执行 `virsh resume svr01` 恢复运行 |

## 六、交付与验收标准（10.6）

完成本课实训后，必须提交以下 4 张关键证据截图，用以全面证明虚拟化存储池管理、磁盘挂接与无损扩容的工程建设成果：

### 4 张必交截图清单与验证要点

1. **截图一：专用目录存储池状态与容量截屏**
   * **执行命令**：在【宿主机终端 Host】执行 `virsh pool-list --all` 与 `virsh pool-info lab-images`。
   * **验证要点**：必须展示 `lab-images` 存储池状态为 `active`、`autostart=yes`，并清晰展示底层存储池的 Capacity 与 Available 容量指标。
2. **截图二：QCOW2 卷创建与挂接 XML 配置截屏**
   * **执行命令**：在【宿主机终端 Host】执行 `virsh vol-info v9-data.qcow2 lab-images` 与 `virsh domblklist svr01`。
   * **验证要点**：必须展示 `v9-data.qcow2` 卷在存储池中成功登记，且 `svr01` 的块设备列表中同时存在系统盘 `vda` 与新挂接的数据盘 `vdb`。
3. **截图三：来宾分区格式化与 fstab 持久挂载截屏**
   * **执行命令**：在【虚拟机 svr01】执行 `lsblk -f /dev/vdb` 与 `cat /etc/fstab | grep data`。
   * **验证要点**：必须展示 `/dev/vdb1` 拥有独立的 UUID、文件系统为 ext4、挂载点为 `/data`，且 `/etc/fstab` 中存在基于 UUID 的持久化挂载声明。
4. **截图四：无损扩容三级联动与数据完整性校验截屏**
   * **执行命令**：在【虚拟机 svr01】执行 `df -h /data` 与 `cd /data && md5sum -c integrity.md5`。
   * **验证要点**：必须展示 `/data` 文件系统总容量平滑扩展至约 5.9G，且基准测试文件的 MD5 校验结果输出为 `integrity.txt: OK`，完整闭环证明扩容无损。

## 本章小结

本课带领你深入 Linux 虚拟化存储核心领域，构建了严谨、规范的存储运维体系：

1. **贯通了虚拟化存储六层抽象模型**：清晰厘清了存储池、存储卷、镜像文件、虚拟块设备、分区与文件系统的职责边界，掌握了 `lab-images` 专用目录池的规范构建。
2. **解构了容量“四本账”与预分配策略**：深刻理解了虚拟容量（Virtual Size）、表观大小（Stated Size）、物理实际占用（Actual Disk Size）与存储池余量（Pool Available）的本质差异，掌握了 `off`、`metadata`、`falloc`、`full` 四种预分配模式在性能与防爆池安全性上的工程权衡。
3. **攻克了 QCOW2 双级簇寻址与无损扩容三级流水线**：剖析了 L1/L2 簇表寻址算法与 On-Demand 动态分配机制；熟练掌握了“宿主机 `qemu-img resize` ➔ 来宾分区 `growpart` ➔ 来宾文件系统 `resize2fs`”的三级扩容标准流程，实现了业务零中断与数据零丢失。
4. **掌握了二进制 Magic 幻数识别与安全防御**：理解了 Linux 基于 `QFI\xfb` 幻数探测文件类型的底层机理，掌握了防范 CVE-2008-2004 格式混淆漏洞的显式 XML 声明规范。
