做一份能重复使用的 PVE Windows 模板
做一份能重复使用的 PVE Windows 模板
做 Windows Server 模板,目标不只是“克隆后能进桌面”。模板必须保留可复用的驱动和基础配置,同时让每个克隆生成自己的系统身份、名称、网络配置与管理状态。否则第一次开机省下的时间,会在加域、备份和排障时还回来。
这篇合并黄金镜像、QEMU Guest Agent 和内存气球三份记录。主记录中模板封装后产生两台可启动实例;另外两份记录确认 VM101、VM102 的 qemu-ga Running、PVE ping 成功,并配置了最大 4096 MiB、最小 2048 MiB 的气球内存。这些证明了各项历史操作,不代表每次从模板克隆都自动继承并通过全部验收。
先纠正模板里最容易被复制的错误
原 KB 把几个现场现象写成了普遍规律,合写时先改正:
| 原说法 | 本文采用的判断 |
|---|---|
| 同 SID 的机器根本不能加域 | 复制部署应先 generalize,但本地机器 SID 不能直接等同于 AD 计算机对象 SID;同名、计算机账户密码与信任关系也需分别检查 |
| Server 2022 必须 UEFI | 本次选 q35/OVMF;一次 0xc0000225 不证明操作系统只支持 UEFI,先核对固件、分区与引导路径是否匹配 |
| Eval 转版本后 Sysprep 必失败 | 转换后曾遇到失败;必须查看 Panther 日志定位该实例原因,不能无条件清空所有 Appx 包 |
SynchronousCommand 按序等待执行 | 官方说明 FirstLogonCommands 现在会同时启动这些命令;有依赖关系的步骤应放进同一个受控脚本 |
| 永久自动登录、删掉 XML 就安全 | 自动登录和缓存应答文件是不同的凭据残留,必须分别检查 |
Microsoft 要求复制到其他计算机前泛化,并说明相同硬件下可保留设备安装状态;这才是使用 Sysprep 的依据,而不是“同 SID 一定加域失败”。Sysprep 泛化说明
还要考虑补丁后的版本差异:Microsoft 已确认 Windows 11 24H2/25H2 与 Server 2025 在 2025 年 8 月 29 日及之后的相关更新中增加 SID 检查,重复 SID 可导致 Kerberos/NTLM 认证失败。因此不能反过来写成“重复 SID 无害”;本文的 Server 2022 现场不自动证明这些新版本的行为。始终按受支持方式泛化并验收克隆身份。 Microsoft duplicate-SID authentication guidance
把可复用配置和机器身份分开
基础 VM 应留在 WORKGROUP,未提升 AD DS。已提升域控不作为这条模板路线的输入;域控替换应按独立的重建与验收流程处理。
本次磁盘走 VirtIO SCSI、网络用 VirtIO。模板阶段就安装对应驱动,可以避免克隆后系统盘不可见或网卡失联。驱动安装程序退出成功仍不够:设备管理器中应有可用设备,重启后系统盘和网络仍正常。ISO 文件、来源、版本和哈希应一起留档,不能只有一个随时改变的 latest URL。
名称、静态 IP、域成员关系、监控唯一标识、临时下载、会话信息和部署口令要在克隆阶段设置或清理。事件日志可先导出作为制作记录,不能用“清掉错误”代替修复。软件授权应按实际许可与通道核查,本文不沿用原文把 KMS 状态解释成永久授权的说法。
先查看系统和服务状态,下面为参考检查命令:
Get-ComputerInfo | Select-Object WindowsProductName,WindowsVersion,OsBuildNumber
Get-CimInstance Win32_ComputerSystem | Select-Object Name,PartOfDomain,Domain
Get-Service qemu-ga -ErrorAction SilentlyContinue
Get-PnpDevice -PresentOnly | Where-Object FriendlyName -match 'VirtIO|QEMU'PartOfDomain=False 与不存在 AD DS 角色,是基础模板的检查项。qemu-ga 服务不在,只说明管理集成尚未完成;Windows 能启动与 Agent 能通讯是两个结果。
应答文件处理哪些阶段
generalize 发生在封装时,specialize 在克隆首次启动时处理系统个性化,oobeSystem 处理开箱配置。应答文件要用匹配目标镜像的 Windows System Image Manager 校验,不能因为 XML 语法正确就认定所有组件和设置受支持。
以下是刻意缩小的结构示例:仅展示同构硬件的设备保留与时区,不包含账户密码、自动登录或全套 OOBE 自动化。不要把它当“零交互完整模板”。
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="generalize">
<component name="Microsoft-Windows-PnpSysprep"
processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35"
language="neutral" versionScope="nonSxS">
<PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35"
language="neutral" versionScope="nonSxS">
<TimeZone>China Standard Time</TimeZone>
</component>
</settings>
</unattend>需要自动化 OOBE 时逐项使用目标版本支持的设置。Microsoft 明确提醒不要用 SkipMachineOOBE 来自动化 OOBE,不能把它作为“留着无害的兜底”。OOBE 设置
涉及安装、激活、服务启动、凭据清理的依赖步骤,不要拆成五条 FirstLogonCommands 再假设按 Order 串行完成。更容易验收的设计是一个入口脚本:每步检查返回码、写阶段日志,失败就停止,不用后面的“清理成功”掩盖前面的失败。此处是修订建议,未补做新 Windows 运行测试。FirstLogonCommands 行为
封装、关机、模板化与第一次克隆
先完成补丁与必要重启,再执行 Sysprep;失败时读 C:\Windows\System32\Sysprep\Panther\setupact.log 和 setuperr.log,按错误包或状态逐项处理。不要直接运行原文“删除所有 Appx”脚本,也不要再次启动已封装 VM 来检查桌面:那会开始消耗下一轮 specialize/OOBE。
C:\Windows\System32\Sysprep\Sysprep.exe /generalize /oobe /shutdown /unattend:C:\Deploy\unattend.xml确认 Sysprep 成功并真正关机后,在 PVE 上执行。示例 VMID 必须替换为自己的测试实例:
qm status 100
qm config 100
qm template 100
qm clone 100 110 --name WS-TEST-01 --full 1 --storage local-lvm
qm config 110完整克隆便于得到独立磁盘,但仍受目标存储容量与性能限制。首次克隆先接隔离网络或断网,检查名称、自动登录和旧静态 IP,再接入业务网络。确认第一台合格后再批量复制;不要用两台同时开机的结果替代单机身份检查。
QEMU Guest Agent:宿主与来宾两端都要通
PVE 的 Agent 开关只配置宿主通道,Windows 内必须安装并启动服务。VirtIO 驱动已安装,也不等于 qemu-ga 已存在。原记录使用独立的 qemu-ga-x86_64.msi,从光驱复制到本地后安装。
qm set 110 --agent enabled=1
qm pending 110若通道是新加的虚拟硬件,核对待生效配置并按需要完整关机再启动。Windows 管理员 PowerShell 中:
$install = Start-Process msiexec.exe -Wait -PassThru -ArgumentList '/i','C:\Deploy\qemu-ga-x86_64.msi','/qn','/norestart'
$install.ExitCode
Get-Service qemu-ga安装返回码 0 通常表示成功,3010 表示成功但需要重启;其他码先查看 MSI 日志,不能直接忽略。然后从 PVE 验证实际通道:
qm agent 110 ping
qm agent 110 get-osinfo
qm agent 110 get-fsinfo
qm agent 110 network-get-interfaces每项回答一个问题:能通信、能识别系统、能读文件系统、能看到来宾网络。Agent 会返回多个 IPv6 地址,不能按“地址数量多”判断故障。备份冻结还需结合备份任务日志与恢复结果验收,ping 成功不是恢复保证。PVE Windows 来宾实践
内存气球独立安装、独立验证
气球通过 VirtIO Balloon 设备与驱动工作,不走 qemu-ga 服务。历史两台域控采用 memory: 4096、balloon: 2048,但 2 GiB 不能作为所有域控或业务服务器的通用下限。
qm set 110 --memory 4096 --balloon 2048
qm pending 110
qm status 110从关闭改为开启时,原记录通过完整关机再启动应用设备变化;Windows 内重启通常不会重建 QEMU 设备。启动后检查 pending 和当前状态:
pvesh get /nodes/pve/qemu/110/status/current --output-format jsonballoon_min=2147483648 对应 2 GiB,ballooninfo 表示有运行时信息,但字段随版本核对。不要只看面板内存有没有变小:来宾已用内存、可用内存、气球目标和宿主 QEMU RSS 是不同指标。只有有记录的内存压力测试,才能说明回收量、延迟与业务影响;原记录没有给出完整压力曲线。
交付模板应附一张验收表
| 层次 | 需要留下的证据 | 不足以单独证明成功的现象 |
|---|---|---|
| Windows 身份 | 唯一名称、未意外加域、部署配置已应用 | 能进桌面 |
| 驱动 | 存储、网络、气球设备无未解释错误 | ISO 曾挂载 |
| Agent | 来宾服务 + 宿主 ping/信息查询 | 仅 agent: 1 |
| 内存 | pending 消失、运行时信息、业务负载记录 | RSS 某个时刻较低 |
| 凭据 | 无持续 AutoLogon、缓存文件检查、部署口令轮换 | 删除一份 XML |
| 恢复 | 测试克隆/恢复可启动且身份正确 | 备份文件存在 |
发现问题时销毁的是测试克隆,基础模板保留到有替代版本验证通过。若模板已被污染,回到封装前快照重做;不要在已交付的多台克隆上“补一次 Sysprep”作为统一修复。模板制作完成的标志,是新实例按预期生成并可管理,而不是脚本最后打印一个成功字符串。
本文日期采用主 KB 首次 Git 提交日期 2026-09-15(UTC+8),提交 6d198b9。合并来源保留在元数据中;历史操作时间与入库时间分别注明。修订后的配置示例未在生产设备执行。
