跳到主要内容

模板管理原理

QVMConsole 的模板系统是实现毫秒级创建虚拟机的核心基础设施。本文档从技术原理角度详细介绍模板的元数据结构、树形版本管理、克隆模式、删除策略和导入导出机制。

模板元数据系统

每个模板由两个文件组成:

文件说明
<模板名>.qcow2模板磁盘文件(qcow2 格式)
<模板名>.meta.json模板元数据文件(由程序自动维护)

元数据文件记录了模板的核心信息:

字段说明
type模板类型:linux / windows / fnos / openwrt / other
category二级分类(Linux 发行版、Windows 版本或 OpenWrt 衍生版)
boot_type启动类型:bios / uefi
boot_verified启动类型是否已确认验证
nvram_pathUEFI NVRAM 变量文件路径(默认同名 .nvram.fd
template_user模板中的默认用户名(用于克隆时的用户名重命名)
cloud_init_mode初始化模式:nocloud / configdrive / fnos / openwrt / none
post_boot_command克隆后的启动后命令(可选)
post_boot_blocking启动后命令是否阻塞等待执行完成
default_config模板默认硬件配置(vCPU、内存、磁盘大小、磁盘总线、网卡模型、显示设备、CPU 拓扑模式、首次重启模式)
template_uid模板族唯一标识(跨节点跟踪模板血缘)
node_id当前模板节点唯一标识
parent_node_id父节点 ID(形成树形结构)
root_node_id根节点 ID
admin_name管理员侧名称
display_name用户侧显示文本
clone_visible是否允许普通用户克隆
disabled是否禁用克隆
md5 / sha256磁盘文件校验哈希
file_size磁盘文件字节数
created_from_vm来源虚拟机名称
created_at创建时间
disk_compressed制作时是否压缩磁盘
source_transfer_mode源磁盘处理方式:copy(复制)/ move(移动)
linux_init_status / linux_init_checked / linux_init_errorLinux 模板 cloud-init 依赖预处理状态(unknown / ready / failed
旧字段兼容

root_password 字段已废弃,仅用于读取旧版元数据;新制作的模板不再写入该字段。磁盘文件缺失 .meta.json 时,模板会按文件名推断类型,且 clone_visible 强制为 false(不允许普通用户克隆)。

文件保护

模板文件和元数据文件在创建后会被自动设置为不可变属性chattr +i),防止意外删除或修改。只有通过模板管理接口才能删除(删除前会先移除不可变属性)。


模板分类体系

系统支持以下模板类型和二级分类:

类型二级分类
LinuxUbuntu(默认)、Debian、CentOS
WindowsWindowsServer2022(默认)、WindowsServer2025、Windows11、Windows10、WindowsServer2012R2、其它
FnOS无二级分类
OpenWrtOpenWrt(默认)、iStoreOS
Other无二级分类

模板类型可通过以下方式确定:

  1. 用户在制作模板时显式指定
  2. 从模板名称自动推断:
    • 名称包含 win / windows 标识为 Windows
    • 名称包含 openwrt / lede / istoreos 标识为 OpenWrt
    • 名称包含 fnos / nas 标识为 FnOS

从虚拟机制作模板时,若选择 other 类型,系统会强制关闭系统初始化(cloud_init_mode=none)并清空模板用户与启动后命令。


模板树形结构

QVMConsole 的模板系统采用树形链式结构管理模板版本关系。当从一个模板 A 克隆出虚拟机,再将该虚拟机制作为新模板 B 时,B 自动成为 A 的子节点。

每个模板节点在树中的属性:

属性说明
level节点层级深度(根节点为 0)
is_root是否为根节点
has_children是否有子模板
direct_vm_count直接关联的链式克隆虚拟机数量
tree_vm_count整棵子树中所有链式克隆虚拟机总数
children_count直接子模板数量

树形结构的构建过程

  1. 扫描模板目录下所有 .qcow2 文件
  2. 加载每个模板的 .meta.json 元数据
  3. 通过 parent_node_id 字段建立父子关系
  4. 收集每个 VM 的模板来源信息(通过 libvirt XML 中的 template-source 注解)
  5. 按树形结构深度优先排序输出

启动类型自动检测

系统会自动检测模板磁盘的启动类型(BIOS 或 UEFI),检测逻辑如下:

UEFI 模板会自动复制 NVRAM 变量文件(.nvram.fd),克隆时为每个虚拟机创建独立的 NVRAM 副本。


模板默认配置继承

从虚拟机制作模板时,系统自动采集源 VM 的硬件配置作为模板默认配置(default_config):

配置项采集方式
vCPU 核心数从 libvirt domain info 获取
内存大小从 libvirt domain info 获取(转换为 GB)
系统盘大小从磁盘信息获取(qemu-img info
系统盘总线从磁盘 XML 配置获取
网卡模型从网络接口 XML 获取
视频模型从 domain XML 解析
CPU 拓扑模式从 domain XML 解析

从模板克隆虚拟机时,这些默认配置会自动应用到创建表单,用户可按需覆盖。


模板制作磁盘策略

从虚拟机制作模板(POST /template/prepare)时,可以选择磁盘的压缩与转移方式:

配置项取值说明
压缩compress 布尔压缩时使用 qemu-img convert -c 生成压缩 QCOW2
磁盘处理方式transfer_modecopy(默认)/ move不压缩时选择复制或移动源系统盘

组合约束

组合行为
不压缩 + 复制(默认)稀疏复制源磁盘并保留 backing 链,源虚拟机不受影响;兼容未传 transfer_mode 的旧调用方
压缩生成压缩 QCOW2;若源虚拟机来自链式克隆,输出显式复用其直接父模板作为 backing,使物理磁盘链与 template_uid / parent_node_id / root_node_id 元数据保持一致
不压缩 + 移动直接迁移源系统盘(保留原 backing),模板完整保存成功后删除源虚拟机
压缩 + 移动非法组合,接口直接拒绝(移动磁盘时固定为不压缩)

移动模式的安全保障

移动磁盘会删除源虚拟机,属于高风险操作(move_vm_disk_to_template 二次验证),并附加以下保障:

  • 提交前校验源虚拟机未被锁定,锁定的虚拟机会被拒绝(提示先解锁)
  • 移动过程中若模板落盘、依赖处理、校验或元数据写入失败,任务会尝试把系统盘移回原路径
  • 只有模板校验和元数据保存成功后才开始删除源虚拟机;源虚拟机的快照、附加磁盘、凭据、锁、用户授权和缓存记录会随删除流程级联清理
移动模式风险提示

移动模式不可逆:源虚拟机在模板保存成功后即被删除。请在目标模板可用之前不要中断任务;任务中途失败时系统会尽力移回磁盘,但请先确认源虚拟机状态再继续后续操作。

两种不压缩方式都会保留源磁盘的 backing,因此制作出的模板仍可作为子模板加入原模板族。


克隆模式

链式克隆

链式克隆通过 qemu-img create -f qcow2 -F qcow2 -b <模板路径> 创建基于模板的差分磁盘(backing file),磁盘只存储与模板的差异数据。

优点:创建速度快(毫秒级)、节省存储空间

缺点:依赖模板文件完整性,模板丢失将导致所有链式克隆虚拟机磁盘损坏

完整克隆

完整克隆通过 qemu-img convert -f qcow2 -O qcow2 将模板数据完整复制到新磁盘文件,与模板完全独立。

优点:独立运行,不依赖模板

缺点:创建较慢(取决于磁盘大小),占用完整存储空间

原生链式克隆

原生链式克隆是一种特殊模式,直接基于模板生成 backing 磁盘并启动虚拟机,不修改模板内的任何配置(主机名、用户名、密码等保持原样)。适用于快速测试和更新模板场景,仅管理员可用。


模板删除策略

系统提供三种模板删除策略:

级联删除(cascade)

删除模板及其整棵子树中的所有模板和关联虚拟机。适用于确认不再需要任何基于该模板的虚拟机和子模板。

提升子节点(promote_children)

将当前模板的子模板和直接关联 VM 的 backing file 通过 qemu-img rebase 指向父模板,然后删除当前节点。所有关联虚拟机必须已关机才能执行。

热提升子节点(promote_children_hot)

与普通提升类似,但支持运行中虚拟机热切换:

  • 对于子模板:先复制一份临时文件,rebase 后通过 virsh blockcopy --pivot 热切换运行中 VM
  • 对于直接关联 VM:通过 virsh blockpull 将运行中 VM 拉平到父模板
热提升限制

热提升仅支持 runningshut off 状态的虚拟机,且不允许存在外部快照。


模板导入导出

导出机制

模板导出为异步任务(template_export),生成 .tar.gz 格式的模板包,包内结构:

<template>-template-export.tar.gz
├── manifest.json # 导出清单
├── <node_id_1>.qcow2 # 节点磁盘文件
├── <node_id_1>.meta.json # 节点元数据
├── <node_id_2>.qcow2
└── <node_id_2>.meta.json

manifest.json 包含:

  • 版本号(v1)、导出时间、导出范围(node / root
  • 模板族 UID、根节点 ID
  • 各节点的文件名、文件大小、MD5/SHA256 哈希校验值与完整元数据

支持两种导出范围:

  • 单节点导出scope=node):仅导出指定模板节点
  • 整棵子树导出scope=root):导出从根节点开始的整棵子树

导出时非 qcow2 格式的磁盘会自动转换为 qcow2 并重算哈希。模板包生成在模板导出目录(template_export_dir),通过 /template/download/:filename 下载;导出文件默认保留 24 小时,过期自动清理,也可手动删除。

导入机制

支持两种导入方式:

导入格式说明
.tar.gz / .tgz标准模板包导入(含元数据和树形结构)
.qcow2旧版单文件导入(自动创建元数据,独立根节点)

模板包分片上传

模板包通过分片上传接口(/template/upload/init/upload/chunk/upload/complete)上传,与"我的存储"共用同一套分片上传引擎:

  • 秒传:初始化时按文件哈希与大小匹配已完成会话或已存在文件(抽样哈希校验),命中则直接完成
  • 断点续传:初始化返回已接收分片列表,前端跳过已上传分片
  • 完成校验:分片到齐后执行抽样哈希校验(2MB 窗口、1GB 步长、最少 3 个样本,尾部追加文件大小与文件名),校验失败会拒绝落盘
  • 自动清理:上传会话 24 小时过期,后台每 30 分钟清理一次;可手动取消已上传的临时包

导入预览与确认

模板包导入采用"预览 → 确认"两步流程:

  1. 预览校验:解析 manifest.json,检查包完整性、每个节点的 MD5 和 SHA256 哈希、节点 ID 和文件名是否与本地模板冲突
  2. 模式判定:根据 template_uid 判断是新建模板族(create)还是更新已有模板族(update);更新模式会先校验本地既有节点的完整性
  3. 确认导入:凭预览令牌提交异步导入任务;非 qcow2 磁盘自动转换为 qcow2;Linux 模板仅当依赖预处理未就绪时才补装 cloud-init 等依赖;导入后重算哈希、回填启动类型、落盘元数据并设置不可变属性

Linux 模板依赖预处理

Linux 模板要求磁盘内预装 cloud-initgrowpart、QEMU Guest Agent 等克隆依赖。系统提供专门的预处理能力:

接口说明
GET /template/:name/prepare-linux/check检查预处理链式依赖(若子树中仍存在链式克隆虚拟机,则禁止原地改写模板磁盘)
POST /template/:name/prepare-linux提交异步任务(template_linux_prepare):离线安装依赖(使用来宾自带软件源)、重算哈希并把 linux_init_status 置为 ready

从虚拟机制作模板时也会自动执行一次依赖预装;安装失败仅记录告警(linux_init_status=failed),不阻断模板制作。


VM 模板来源追踪

每个通过模板克隆的虚拟机,其 libvirt XML 中都会注入一个 template-source 自定义元数据注解,记录:

属性说明
template_name来源模板名称
node_id来源模板节点 ID
clone_mode克隆模式(linked / full

这个信息用于:

  • 模板管理页面展示虚拟机关联关系
  • 删除模板时确定受影响的虚拟机列表
  • 从虚拟机再次制作模板时继承模板族血缘

相关文档