目录
前文列表
VMware 虚拟化编程(1) — VMDK/VDDK/VixDiskLib/VADP 概念简析
VMware 虚拟化编程(2) — 虚拟磁盘文件类型详解
VMware 虚拟化编程(3) —VMware vSphere Web Service API 解析
VMware 虚拟化编程(4) — VDDK 安装
VMware 虚拟化编程(5) — VixDiskLib 虚拟磁盘库详解之一
VMware 虚拟化编程(6) — VixDiskLib 虚拟磁盘库详解之二
VMware 虚拟化编程(7) — VixDiskLib 虚拟磁盘库详解之三
VMware 虚拟化编程(8) — 多线程中的 VixDiskLib
VMware 虚拟化编程(9) — VMware 虚拟机的快照
数据块修改跟踪技术 CBT
CBT(Changed Block Tracking) 数据块修改跟踪技术,是 VMware 实现「增量备份」的底层支撑技术。CBT 的优势在于节约空间,它允许只备份发生了修改的数据。在 CBT 被引入之前,每次都必须要备份整个虚拟机,而不是增量备份。所谓增量备份,即仅备份两个快照时间点之间所被修改过的数据。开启了 CBT 的虚拟机会在其数据储存目录下新建一个 -ctk.vmdk 文件,用于记录数据块修改的跟踪信息。但开启 CBT 会对虚拟机的虚拟磁盘带来一些性能损失,所以默认会关闭 CBT。
CBT 的工作原理就是让 VMKernel 监控自上次快照时间点以来有那些数据块中的数据被改变了,并记录下这些被改变的数据块的偏移量,依靠这些偏移量就能够获取数据块中的修改数据了。
以下场景都支持 CBT:
- 存储在 VMFS 上的虚拟磁盘(SAN or Local)
- NFS 上的虚拟磁盘
- 虚拟兼容模式的 RDM(裸设备映射)
以下场景不支持 CBT:
- 物理兼容模式的 RDM
为虚拟机开启 CBT
默认情况下 CBT 功能是禁用的,因为它会引起一个很小但是可被测量到的性能损耗。开启 CBT 的前提条件需要虚拟机版本为 7 或更高,我们可以使用 PropertyCollector 从 VirutalMachine ManagedObject 中获取这个 CBT 的属性域,如果其中含有 changeTrackingSupported 属性值,就该虚拟机支持 CBT 功能。
- 使用程序设置 CBT:
VirtualMachineConfigSpec configSpec = new VirtualMachineConfigSpec();
configSpec.changeTrackingEnabled = new Boolean(true);
ManagedObjectReference taskMoRef = serviceConnection.getService().ReconfigVm_Task(targetVM_MoRef, configSpec);
- 手动设置 CBT:
- 右击虚拟机,选择「Edit Settings」
- 点击「Options」
- 点击「Advanced section」下的「General」,点击「Configuration Parameters」,开启「Configuration Parameters」窗口后,查找或添加「ctkEnabled」项,设置为「true」,并设置每个磁盘的「ctkEnabled=true」
在设置了 CBT 之后,需要重启虚拟机生效。
NOTE: vSphere WS API 能调用 configSpec.changeTrackingEnabled = new Boolean(true)
来动态的设置 CBT 状态,而不需关闭虚拟机。
CBT 修改数据块偏移量获取函数 QueryChangedDiskAreas
VMware vSphere WS API 中提供的 QueryChangedDiskAreas Method 能够帮助开发者获得虚拟机 CBT 的功能,该函数需要提供以下参数:
_this
:目标 VirtualMachine moRefsnapshot
:虚拟机当前的 Snapshot moRefdeviceKey
:目标虚拟磁盘的 IdstartOffset
:指定开始检查 CBT 的 offset,一般可以为 0changeId
:虚拟磁盘在某个时间点上的状态标识符,是一个格式为<UUID>/<nnn>
的数字序列字符串,如果<UUID>
改变了,跟踪信息就会失效。
QueryChangedDiskAreas 返回的是 DiskChangeInfo 数据对象,它包含一组 DiskChangeInfo.DiskChangeExtent 元素,分别表示已修改数据块磁盘区域的开始位移和长度,DiskChangeInfo 覆盖了整个磁盘区域的开始位移和长度。
EXAMPLE:(offset,length) 表示一个发生了修改的数据块的偏移量
(117768192, 65536)
(132120576, 65536)
(145096704, 43122688)
(265289728, 65536)
(958398464, 65536)
使用 QueryChangedDiskAreas 得到已修改数据块偏移量信息的前提条件是需要在创建快照前启用 CBT 功能,如果在启用 CBT 前调用该 Method 就会触发 FileFault 错误。
changeId
changeId 实际上就是虚拟磁盘在某个时间点的标识符。当我们调用 QueryChangedDiskAreas Method 时,除了可能会为形参 changeId 传入一串数字序列字符串之外,还可能会传入一个「*」号。
在使用 changeId 时,应该注意以下几点:
当虚拟机还没有快照时,虚拟磁盘的 changeId 初始值应该为 none 表示未设置的。
当虚拟机创建第一个快照时,应该将传入「*」号,表示虚拟磁盘上所有的实际已分配的区域,同时忽略稀疏类型磁盘的未分配区域。需要注意的是,只有当虚拟磁盘的 changeId 初始值为 none 时,
changeId=*
才会生效。换句话说,只有当虚拟机仅拥有一个快照时调用 QueryChangedDiskAreas,才能够将 changeId 设置为「*」,并且能够以此获得虚拟机的全量数据偏移量。在此之后的每次创建快照都会生成一个新的 changeId。如果 changeId 不再为 none,则表示虚拟机已经进行过至少一次快照,此后调用 QueryChangedDiskAreas 就能够得到自 changeId 标识的快照时间点以来所发生了修改的数据块偏移量,也就是增量数据偏移量。
总结一下,使用「*」时,存在下面两点限制:
- 虚拟磁盘必须存放在 VMFS 上。
- 启动 CBT 时,虚拟机必须没有快照存在。
获取虚拟磁盘当前的 changeId:
我们能够用过 VirutalMachine ManagedObject 的 vim.vm.device.VirtualDevice.VirtualDisk 配置项中找到虚拟机每一块虚拟磁盘的 backing 信息。如果 backing 的类型是下列中的一个,你就可以使用 BackingInfo 数据对象的 changeId 属性来获得其 changeId:
- vim.vm.device.VirtualDevice.VirtualDiskFlatVer2BackingInfo
- vim.vm.device.VirtualDevice.VirtualDiskSparseVer2BackingInfo
- vim.vm.device.VirtualDevice.VirtualDiskRawDiskMappingVer1BackingInfo
- vim.vm.device.VirtualDevice.VirtualDiskRawDiskVer2BackingInfo
最后总结一下,QueryChangedDiskAreas(..., "*")
实际上会返回虚拟磁盘被实际使用的(Thin)或者整个被分配的(Thick)的数据空间偏移量。CBT 的实现依赖于「虚拟磁盘的未分配区域」以及「 VMFS 的数据块延迟清零」两者的定义和特性。因此,CBT 只有在 VMFS 数据存储上才会返回有意义的结果。在其他存储类型上,要么就失败,要么就返回包含整个磁盘的单个内容。
在开启的 CBT 的前提下,第一次调用 QueryChangedDiskAreas(..., "*")
时,它会返回虚拟磁盘上所有 已经使用的 数据块区域,后续的调用则会返回 已修改的 数据块区域,而不是 已分配的 区域。
在没有开启 CBT 的前提下,在快照后调用 QueryChangedDiskAreas,则会返回 已分配的 区域,对于精简置备虚拟磁盘和延迟清零的厚置备磁盘而言,那些 已分配但未使用的 区域则会使用零值填充。也就是说这种情况下,即便是精简置备虚拟磁盘,其全量备份所得到的数据量等于为其所分配的数据量,而非实际所使用了的数据量。
一个 QueryChangedDiskAreas 的 DEMO
String changeId; //Already initialized: changeId, snapshotMoRef, the VM
ManagedObjectReferencesnapshotMoRef;
ManagedObjectReferencetheVM;
int diskDeviceKey; //Identifies the virtual disk.
VirutalMachine.DiskChangeInfochanges;
long startPostion = 0;
do {
changes =theVM.QueryChangedDiskAreas(snapshotMoRef, diskDeviceKey, startPostion,changeId);
for (int i = 0; i < changes.changedAread.length;i++) {
long length =changes.changedArea[i].length;
long offset =changes.chagedArea[i].startOffset;
//
// Go get and save disk data here
}
startPosition = changes.startOffset +changes.length;
} while (startPosition< diskCapacity);
在这个 DEMO 里,QueryChangedDiskAreas 被反复调用,同时开始检查位置 startPosition 在虚拟磁盘中不断往后移动。这是因为对于大型虚拟磁盘而言,ChangedDiskArea 数组很可能会占用大量的内存。
NOTE:需要注意的是,QueryChangedDiskAreas 获得的是已修改数据块的偏移量,而非实际的已修改数据。
应用 QueryChangedDiskAreas 设计的增量/差异备份算法
假设在 T1 时间点创建了一个初始的全量备份,之后在 T2、T3 时间点分别创建了增量备份。当然,你也可以使用差异备份,只是它会消耗更多的备份时间和带宽,但是拥有更少的还原时间。
T1 时间的全量备份
- 记录虚拟机的配置信息 VirtualMachineConfigInfo。
- 创建虚拟机快照,命名为 snapshot_T1。
- 获得并保存快照中各个虚拟磁盘的 changeId,changeId_T1。
- 备份调用 queryChangedDiskAreas(…, “*”) 所返回的已修改数据块扇区对应的数据。
- 删除快照 snapshot_T1。
T2 时间的增量备份
- 创建虚拟机快照,命名为 snapshot_T2。
- 获得并保存快照中各个虚拟磁盘的 changeId,changeId_T2。
- 备份调用 queryChangedDiskAreas(snapshot_T2, …, changedId_T1)返回的已修改数据块扇区对应的数据。
- 删除快照snapshot_T2。
T3 时间的增量备份
- 创建虚拟机快照,命名为 snapshot_T3 (此时你无法再获得 T1 到 T2 两个时间点之间的以修改数据块列表)
- 获得并保存快照中各个虚拟磁盘的 changeId,changeId_T3。
- 备份调用 queryChangedDiskAreas(snapshot_T3, …, changedId_T2)返回的已修改数据块扇区对应的数据。如果你希望执行差异备份,则调用 queryChangedDiskAreas(snapshot_T3, …, changedId_T1)。
4删除快照snapshot_T3。
T4 时间的灾难恢复
- 使用之前保存的 VirtualMachineConfigInfo 中的配置参数,创建一个没有 GuestOS,没有虚拟磁盘的新虚拟机。
- 从 T3 的增量备份中还原数据,并记录还原了哪些扇区。
- 从 T2 中还原增量备份的数据,跳过 2 中已记录(已还原)的扇区,并记录还原了哪些扇区。如果 T3 是差异备份,则跳过此步骤。
- 从 T1 完全备份中还原数据,并跳过 2、3 中已记录(已还原)的扇区。
- 打开恢复过后的虚拟机。
NOTE:从后往前还原的目的就是获取同一数据块上最新的数据,从而避免不需要的数据拷贝。