還原過程的底層實現
還原虛擬機和磁盤
無法對正在使用的虛擬磁盤進行寫操作。對於完全還原,你必須通過停止虛擬機並關閉電源,確保虛擬磁盤沒有被佔用。下面的代碼演示瞭如何關閉虛擬機:
// At this point weassume that you have a ManagedObjectReference to the VM – vmMoRef.
// Power on would needa ManagedObjectReference to the host running the VM – hostMoRef.
ManagedObjectReferencetaskRef = serviceConnection.powerOffVm(vmMoRef);
對於高級傳輸模式,你還原虛擬磁盤之前,還需要創建一個虛擬機快照。如果還原時已經存在快照,你需要移除它,否則SAN模式還原會失敗。
然後,就要使用VixDiskLib來重新載入虛擬磁盤的內容,所以下面的代碼是C++而不是Java:
// At this point weassume that you already have a VixDiskLib connection to the server machine. uint8mybuffer[some_multiple_of_512]; int mylocalfile =open(“localfile”, openflags); // Contains backup copy of virtual disk read(mylocalfile,mybuffer, sizeof mybuffer); vixError =VixDiskLib_Open(srcConnection, path, flags, &diskHandle); VixDiskLib_Write(diskHandle,startSector, (sizeof mybuffer) / 512, mybuffer);
對於SAN傳輸模式,還需要恢復並刪除快照。如果你忘記了恢復快照,就會因爲CID不匹配導致快照刪除失敗,所以虛擬機不能開機。如果你忘了刪除快照,這個無關的快照就會引起後續備份的還原問題。
創建虛擬機
本節描述如何創建一個虛擬機對象,這比較複雜,但是是必須的,因爲你需要把數據還原到它裏面去。創建這個對象之前,需要創建一個VirtualMachineConfigSpec來描述虛擬機,以及它支持的所有虛擬設備。大部分需要的信息都可以從虛擬機屬性config.hardware.device中得到,它是包含設備配置信息的一張表。設備間的關係由key的值表示,它是設備的唯一標識符。每一個設備都有一個controllerKey,表示設備連接的控制器的key標識符。在VirtualMachineConfigSpec中使用負整數作爲臨時的key值,以保證這些臨時key值不和服務器你分配給他們的實際值衝突。當與默認設備關聯虛擬設備時,controllerKey屬性需要重置爲控制器的key屬性值(When associating virtual deviceswith default devices, the controllerKey property should be reset with the keyproperty of the controller)。下面是用來創建虛擬機的VirutalMachineConfigSpec的簡單配置:
//beginning ofVirtualMachineConfigSpec, ends serval pages later { dynamicType = <unset>, changeVersion = <unset>, //This is the displayname of the VM name = “My New VM”, version = “vmx-04”, uuid = <unset>, instanceUuid = <unset>, npivWorldWideNameType = <unset>, npivDesiredNodeWwns = <unset>, npivDesiredPortWwns = <unset>, npivTemporaryDisabled = <unset>, npivOnNonRdmDisks = <unset>, npivWorldWideNameOp = <unset>, locationId = <unset>, // this is advisory,the disk determines the O/S alternameGuestName = “Microsoft WindowsServer 2008, Enterprise Edition”, annotation = <unset>, files = (vim.vm.FileInfo) { dynamicType = <unset>, vmPathName = “[plat004-local]”, snapshotDirectory =“[plat004-local]”, suspendDirectory = <unset>, logDirectory = <unset>, }, tools = (vim.vm.ToolsConfigInfo) { dynamicType = <unset>, toolsVersion = <unset>, afterPowerOn = true, afterResume = true, beforeGuestStandby = true, beforeGuestShutdown = true, beforeGuestReboot = true, toolsUpgradePolicy =<unset>, pendingCustomization =<unset>, syncTimeWithHost = <unset>, }, flags = (vim.vm.FlagInfo) { dynamicType = <unset>, disableAcceleration =<unset>, enableLoggin = <unset>, useToe = <unset>, runWithDebugInfo = <unset>, monitorType = <unset>, htSharing = <unset>, snapshotDisabled = <unset>, snapshotLocked = <unset>, diskUuidEnabled = <unset>, virtualMmuUsage = <unset>, snapshotPowerOffBehavior =“powerOff”, recordRelayEnabled =<unset>, }, consolePreferences =(vim.vm.ConsolePreperences) null, powerOpInfo = (vim.vm.DefaultPowerOpInfo){ dynamicType = <unset>, powerOffType = “preset”, suspendType = “preset”, resetType = “preset”, defaultPowerOffType =<unset>, defaultSuspendType =<unset>, defaultResetType = <unset>, standbyAction = “powerOnSuppend”, }, // the number of cups numCPUs = 1, // the number ofmemory megabytes memoryMB = 256, memoryHotAddEnabled = <unset>, cpuHotAddEnabled = <unset>, cpuHotRemoveEnabled = <unset>, deviceChange =(vim.vm.device.VirtualDeviceSpec) { (vim.vm.device.VirtualDeviceSpec){ dynamicType =<unset>, operation = “add”, fileOperation =<unset>, //CDRom device =(vim.vm.device.VirtualCdrom) { dynamicType =<unset>, //key number of CDROM key = -42, deviceInfo =(vim.Description)null, backing =(vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo) { dynamicType =<unset>, deviceName =“”, useAutoDetect= <unset>, exclusive =false, }, connectable =(vim.vm.device.VirtualDevice.ConnecInfo) { dynamicType =<unset>, startConnected= false, allowGuestControl= true, connected =false, }, //connects to thiscontroller controllerKey = 200, unitNumber = 0, }, }, (vim.vm.device.VirtualDeviceSpec){ dynamicType =<unset>, operation = “add”, fileOperation =<unset>, //SCSI controller device =(vim.vm.device.VirtualLsiLogicController) { dynamicType =<unset>, // key num of the SCSIcontroller key = -44, deviceInfo =(vim.Description) null, backing =(vim.vm.device.VirtualDevice.BackingInfo) null, connectable =(vim.vm.device.VirtualDevice.Connection) null, controllerKey =<unset>, unitNumber =<unset>, busNumber = 0, hotAddRemove =<unset>, sharedBus =“noSharing”, scsiCtlrUnitNumber =<unset>, }, }, (vim.vm.device.VirtualDeviceSpec){ dynamicType =<unset>, operation = “add”, fileOperation =<unset>, //Network controller divice =(vim.vm.device.VirtualPCNet32) { dynamicType =<unset>, //key number of network controller key = -48, deviceInfo =(vim.Description) null, backing =(vim.vm.device.VirtualEthernetCard.NetworkBackingInfo) { dynamicType= <unset>, deviceName= “Virtual Machine Network”, useAutoDetect= <unset>, network= <unset>, inPassthroughMode= <unset>, }, connectable =(vim.vm.device.VirtualDevice.ConnectInfo) { dynamicType= <unset>, startConected= true, allowGuestControl= true, connected= true, }, controllerKey= <unsert>, unitNumber =<unset>, addressType =“generated”, macAddress =<unset>, wakeOnLanEnabled= true, }, }, (vim.vm.device.VirtualDeviceSpec){ dynamicType =<unset>, operation = “add”, fileOperation =“create”, //SCSI disk one device = (vim.vm.device.VirtualDisk){ dynamicType =<unset>, //key number of the scsi disk one key =-1000000, deviceInfo =(vim.Description) null, backing =(vim.vm.device.VirtualDisk.FlatVer2BacingInfo) { dynamicType= <unset>, filename= “”, datastore= <unset>, diskMode= “persistent”, split= false, writeThrough= false, thinProvisioned= <unset>, eagerlyScrub= <unset>, uuid =<unset>, connectId= <unset>, changeId= <unset>, parent= (vim.vm.device.VirtualDisk.FlagVer2BackingInfo) null, }, connectable =(vim.vm.device.VirtualDevice.ConnectInfo) { dynamicType= <unset>, startConected= true, allowGuestControl= false, connected= true, }, //controller for scsi disk one controllerKey= -44, unitNumber =0, // sizte in MB scsi disk one capacityInKB= 52428, committedSpace= <unset>, shares =(vim.SharesInfo) null, }, }, (vim.vm.device.VirtualDeviceSpec){ dynamicType =<unset>, operation = “add”, fileOperation =“create”, //scsi disk two device =(vim.vm.device.VirtualDisk) { dynamicType =<unset>, //key num of scsi disk two key = -100, deviceInfo =(vim.Description) null, backing =(vim.vm.device.VirtualDisk.FlagVer2BackingInfo) { dynamicType= <unset>, filename= “”, datastore= <unset>, diskMode= “persistent”, split= false, writeThrough= false, thinProvisioned= <unset>, eagerlyScrub= <unset>, uuid =<unset>, connectId= <unset>, changeId= <unset>, connectable= (vim.vm.device.VirtualDevice.ConnectInfo) { dynamicType= <unset>, startConnected= true, allowGuestControl= false, connected= true, }, //controller for scsi disk two controllerKey= -44, unitNumber= 1, //size in MB SCSI disk two capacityInKB= 131072, committedSpace= <unset>, shares= (vim.SharesInfo) null, }, }, }, cpuAllocation =(vim.ResourceAllocationInfo) { dynamicType =<unset>, reservation = 0, expandableReservation= <unset>, limit =<unset>, shares =(vim.SharesInfo) { dynamicType =<unset>, shares = 100, level =“nomal”, }, overheadLimit =<unset>, }, memoryAllocation =(vim.ResourceAllocationInfo) { dynamicType =<unset>, reservation = 0, expandableReservation= <unset>, limit =<unset>, shares =(vim.SharesInfo) { dynamicType =<unset>, shares = 100, level =“normal”, }, overheadLimit =<unset>, }, cpuAffinity =(vim.vm.AffinityInfo) null, memoryAffinity =(vim.vm.AffinityInfo) null, networkShaper = (vim.vm.NetworkShaperInfo)null, swapPlacement =<unset>, swapDirectory =<unset>, preserveSwapOnPowerOff =<unset>, bootOptions =(vim.vm.BootOptions) null, appliance =(vim.vService.ConfigSpec) null, ftInfo =(vim.vm.FaultToleranceConfigInfo) null, applianceConfigRemoved =<unset>, vAssertsEnabled =<unset>, changeTranckingEnabled =<unset>, } //end of VirtualMachineConfigSpec
上面的信息非常複雜,但是很多輸入都是由系統指定的默認值。剩下的信息可以從config.hardware.device表中得到,這是由PropertyCollector返回的。從SDK示例代碼中借鑑如下代碼,建立配置細節:
// Duplicate virtualmachine configuration VirtualMachineConfigSpecconfigSpec = new VirtualMachineConfigSpec(); // Set the VM values configSpec.setName(“MyNew VM”); configSpec.setVersion(“vmx-04”); configSpec.setGuestId(“winNetStandardGuest”); configSpec.setNumCPUs(1); configSpec.setMemoryMB(256); // Set up file storageinfo VirtualMachineFileInfovmfi = new VirtualMachineFileInfo(); vmfi.setVmPathName(“[plat004-local]”); configSpec.setFiles(vmfi); vmfi.setSnapshotDirectory(“[plat004-local]”); // Set up tools configinfo ToolsConfigInfo tools= new ToolsConfigInfo(); configSpec.setTools(tools); tools.setAfterPowerOn(newBoolean(true)); tools.setAfterResume(newBoolean(true)); tools.setBeforeGuestStandby(newBoolean(true)); tools.setBeforeGuestShutdown(newBoolean(true)); tools.setBeforeGuestReboot(newBoolean(true)); // Set flags VirtualMachineInfoflags = new VirtualMachineFlagInfo(); configSpec.setFlags(flags); flags.setSnapshotPowerOffBehavior(“powerOff”); // Set power op info VirtualMachineDefaultPowerOpInfopowerInfo = new VirtualMachineDefaultPowerOpInfo(); configSpec.setPowerOpInfo(powerInfo); powerInfo.setPowerOffType(“preset”); powerInfo.setSuspendType(“preset”); powerInfo.setRetType(“preset”); powerInfo.setStandbyAction(“poerOnSuspend”); // Now add in thedevices VirtualDeviceConfigSpec[]deviceConfigSpec = new VirtualDeviceConfigSpec[5]; configSpec.setDeviceChange(deviceConfigSpec); // Formulate the CDROM deviceConfigSpec[0].setOperation(VirtualDeviceConfigSpecOperation.add); VirtualCdrom cdrom =new VirtualCdrom(); VirtualCdromIsoBacingInfocdDeviceBacking = new VirtualCdromRemotePassthroughBackingInfo(); cdDeviceBacking.setDatastore(datastoreRef); cdrom.setBacking(cdDeviceBacking); cdrom.setKey(-42); cdrom.setControllerKey(newInteger(-200)); // Older java required type for optional properties cdrom.setUnitNumer(newInteger(0)); deviceConfigSpec[0].setDevice(cdrom); // Formulate the SCSIcontroller deviceConfigSpec[1].setOperation(VirtualDeviceConfigSpecOperation.add); VirtualLsiLogicControllerscsiCtrl = new VirtualLsiLogicController(); scsiCtrl.setBusnumber(0); deviceConfigSpec[1].setDevice(scsiCtrl); scsiCtrl.setKey(-44); scsiCtrl.setSharedBus(VirtualSCSISharing.noSharing); // Formulate SCSI diskone deviceConfigSpec[2].setFileOperation(VirtualDeviceConfigSpecFileOperation.ceate); deviceConfigSpec[2].setOperation(VirtualDeviceConfigSpecOperation.add); VirtualDisk disk = newVirtualDisk(); VirtualDiskFlatVer2BackingInfodiskfileBacking = new VirtualDiskFlatVer2BacingInfo(); diskfileBacking.setDatastore(datastoreRef); diskfileBacking.setFileName(volumeName); diskfileBacking.setDiskMode(“persistent”); diskfileBacing.setSplit(newBoolean(false)); diskfileBacking.setWriteThrough(newBoolean(false)); disk.setKey(-1000000); disk.setCotrollerKey(newInteger(-44)); disk.setUnitNumber(newInteger(0)); disk.setBacking(diskfileBacking); disk.setCapacityInKB(524288); deviceConfigSpec[2].setDevice(disk); // Formulate SCSI disktwo deviceConfigSpec[3].setFileOperation(VirtualDeviceConfigSpecFileOperation.create); deviceConfigSpec[3].setOperation(VirtualDeviceConfigSpecOperation.add); VirtualDisk disk2= new VirtualDisk(); VirtualDiskFlatVer2BackingInfodiskfileBacking2 = new VirtualDiskFlatVer2BackingInfo(); diskfileBacking2.setDatastore(datastoreRef); diskfileBacking2.setFileName(volumeName); diskfileBacking2.setDiskMode("persistent"); diskfileBacking2.setSplit(newBoolean(false)); diskfileBacking2.setWriteThrough(newBoolean(false)); disk2.setKey(-100);disk2.setControllerKey(new Integer(-44)); disk2.setUnitNumber(newInteger(1)); disk2.setBacking(diskfileBacking2); disk2.setCapacityInKB(131072); deviceConfigSpec[3].setDevice(disk2); // Finally, formulatethe NIC deviceConfigSpec[4].setOperation(VirtualDeviceConfigSpecOperation.add); com.VMware.vim.VirtualEthernetCardnic = new VirtualPCNet32(); VirtualEthernetCardNetworkBackingInfonicBacking = new VirtualEthernetCardNetworkBackingInfo(); nicBacking.setNetwork(networkRef); nicBacking.setDeviceName(networkName); nic.setAddressType("generated"); nic.setBacking(nicBacking); nic.setKey(-48); deviceConfigSpec[4].setDevice(nic); // Now that it is allput together, create the virtual machine. // Note that folderMo,resourcePool, and hostMo, are moRefs to the Folder, ResourcePool, and Host // where the VM is tobe created ManagedObjectReferencetaskMoRef = serviceConnection.getService().createVM_Task(folderMo, configSpec,resourcePool, hostMo);
使用VirtualMachineConfigInfo
備份程序還可以利用VirtualMachineConfigInfo包含的信息。如果備份保存了描述虛擬機的VirtualMachineConfigInfo的詳細信息,那麼還原還原時創建虛擬機時,就以可以傳遞更多信息到VirtualMachineConfigSpec中。然而,VirtualMachineConfigInfo中的一些信息則是不需要的,如果在Spec中使用的話,虛擬機創建就會失敗。例如,VirtualMachineConfigSpec中叫做“默認設備”包含的信息就會創建失敗。默認設備包括:
vim.vm.device.VirtualIDEController
vim.vm.device.VirtualPS2Controller
vim.vm.device.VirtualPCIController
vim.vm.device.VirtualSIOController
vim.vm.device.VirtualKeybord
vim.vm.device.VirtualVMCIDevice
vim.vm.device.VirtualPointingDevice
但是,其他的控制器和設備必須在VirtualMachineConfigSpec中指明。設備的一些信息不是必須的,並且如果提供了話可能會出問題。每個控制器都有一個vim.vm.device.VirtualController.device域,它是一個所有向該控制器報告的設備數組(which is an array ofdevices that report to the controller)。服務器在創建虛擬機時,使用提供的設備key編號(負數)重建這個數組。使用負數key值表示的控制器和設備的關係,必須和VirtualMachineConfigInfo中硬件數組中的關係一致(The relationship betweencontroller and device must be preserved using negative key numbers in the samerelationship as in the hardware array of VirtualMachineConfigInfo)。
虛擬磁盤backing信息的父屬性必須設置爲null。在創建虛擬機的示例代碼中,可查看vim.vm.device.VirtualDisk.FlatVer2BacingInfo。需要設置爲null,因爲備份前快照導致了父屬性指向了基本磁盤(The null setting isrequired because the pre-backup snapshot causes the parent property to bepopulated with a reference to the base disk)。
其他一些配置信息需要被替代。VirtualMachineConfigInfo包含cpuFeatureMask域,它是一組HostCpuIdInfo。這些數組項需要轉換爲包含VirtualMachineCpuIdInfoSpec的ArrayUpdateSpec項,轉換時使用一個包含ArrayUpdateOperation::add值得“operation”域(The array entries must beconverted to ArrayUpdateSpec entries containg the VirtualMachineCpuIdInfoSpecalong with the “operation” field, which must contain the valueArrayUpdateOperation::add)。VirtualMachineCpuIdInfoSpec還包含一個HostCupIdInfo數組,可以從VirtualMachineConfigInfo的cpuFeatureMask數組複製得到。這在示例代碼中沒有反映到。
其他所有信息都可以從VirtualMachineConfigInfo數據中獲得。
總結:還原虛擬磁盤創建虛擬機時:
從VirtualMachineConfigSpec中排除默認設備以及VirtualController.device。
將父虛擬磁盤backing信息(VirtualDisk.FlatVer2BackingInfo)設置爲null
將HostCpuIdInfo數組項轉換爲ArrayUpdateSpec項,插入ArrayUpdateOperation::add,並從VirtualMachineConfigInfo中複製cupFeatureMask到HostCupIdInfo。
編輯、刪除設備
如果備份客戶端想要刪除或編輯設備,它們需要服務器提供的key來引用已存在的設備。對於key的定義,請查看“創建虛擬機”一節。例如,在源代碼中CDROM的key和ControllerKey的值。key唯一標識了一個設備,而controllerKey則唯一標識了它所連接的控制器。
還原虛擬磁盤數據
“還原過程的底層實現”中,VixDiskLib函數提供的接口,能夠在本地或遠程將數據寫入到虛擬磁盤中。
原始設備映射(RDM)磁盤
使用CreateVM_Task創建RDM磁盤時,用到了一個沒有被佔用且可用的LUN。開發者有時會使用configInfo對象中的LUN uuid,這可能導致錯誤,因爲LUN uuid是數據存儲指定的。
調用QueryConfigTarget獲得ConfigTarget.ScsiDisk.Disk.CanonicalName屬性,設置到VirtualDiskRawDiskMappingVer1BackInfo.deviceName屬性中。調用QueryConfgiTarget獲得ConfigTarget.ScsiDisk.Disk.uuid並設置到VirtualDiskRawDiskMappingVer1BackInfo.lunUuid屬性中。創建虛擬機時,避免使用configInfo中的主機相關的屬性,這些屬性需要設置爲當前正在還原的主機的相關配置。
還原增量備份數據
有時你可能需要還原“虛擬磁盤修改塊跟蹤”一節中獲取的虛擬磁盤數據。主要的步驟包括:
1 關閉虛擬機。
2 使用客戶機操作系統最後一個狀態的VirtualMachineConfigInfo來創建虛擬機,正如“使用VirtualMachineConfigInfo”一節中描述的那樣。
3 使用完全備份重新還原基本虛擬磁盤。
4 創建一個快照,這在SAN模式下還原是必須的。
5 對於SAN模式還原,禁用修改塊跟蹤,當啓用時SAN寫操作時不可用的。
6 依次還原增量備份數據。你可以向前或向後還原。如果你向前還原,還原時一些扇區可能不止寫一次。如果你向後還原,必須記錄哪些扇區已經還原過了,以避免再次寫入舊的數據。
a 從備份記錄中獲取將要還原的增量備份的changeId。你的軟件同樣必須保存修改塊信息,這樣就可以知道要還原虛擬磁盤的哪些扇區。一旦開始還原虛擬磁盤,修改跟蹤機制就會誤報。
b 僅還原快照標識的修改區域到虛擬磁盤(Restore only changed areas to the virtual diskreferred to by the snapshot)。這會保證不會將數據寫到快照創建的重寫日誌中。當還原一個薄的置備(稀疏)磁盤(thin provisioned(sparse) disk),使用星號“*”修改ID避免向未分配塊寫入零值。
c 重複步驟a和步驟b依次還原增量數據。
7 如果可以的話,恢復到基本虛擬磁盤,從而消除了快照。
ESXi主機直連還原失敗
有時你必須直接在ESXi主機上還原虛擬機,例如當vCenter服務器在ESXi上作爲一個虛擬機運行時進行的災難恢復。vSphere5有一個新的特性,就是當ESXi主機由vCenter管理時,就會阻止這種操作。爲了規避這點以還原虛擬機,必須切斷主機和vCenter之間的關聯。在早期的版本中,vCenter管理保存了很少的狀態,但是可以從vCenter撤銷(vCenter management had lessstate but was revocable only from vCenter)。
1使用vSphere客戶端直接連接到ESXi5.0或更新版本的主機。
2在存儲庫(Inventory)左邊欄中,選擇主機,在右邊選擇“簡介(Summary)”。
3在標題爲“主機管理(Host Management)”的對話框中,選擇“取消主機到vCenter服務器的關聯”。你不需要將虛擬機置爲維護模式。
4當vCenter服務器還原成功並運行後,使用它重新獲取主機。
當前並沒有取消主機和vCenter服務器之間的關聯的API。
技巧和最佳實踐
VDDK5.0包含兩個新的VixDiskLib調用(PrepareForAccess以及EndAccess),用來在備份時禁用和啓用存儲遷移。這避免了在執行備份時,虛擬機移動了它的存儲導致舊的磁盤鏡像被遺留了下來(This prevent staledisk images from being left behind if a virtual machine has its storage movedwhile a backup is taking palce)。VMware強烈建議使用這兩個調用。
當ESX/ESXi主機由vCenter服務器管理時,vSphere API不能直接和主機聯繫,它們必須通過vCenter。如果需要的話,特別是在災難恢復時,在能夠直接和主機進行通信前,必須取消ESXi主機和vCenter服務器之間的關聯。
高級傳輸允許程序以最高效的方式傳輸數據。SAN模式只有在物理機有SAN訪問時纔可用。HotAdd在應用模式下可用,備份在虛擬機內部完成。HotAdd要求虛擬機的數據存儲能夠被備份應用訪問。當你只能選擇通過網絡進行備份時,NBDSSL是一個安全備選。
SAN傳輸僅在物理機上支持,而HotAdd傳輸只有虛擬機能支持。SAN需要一個物理代理和ESXi主機共享一個數據存儲依賴的LUN,使得能夠直接訪問原始的數據,並繞過主機的I/O操作。HotAdd需要將一個虛擬磁盤附加到備份代理,就像將一個磁盤附加到虛擬機一樣。
SAN傳輸的最佳實踐
對於基於陣列的存儲,SAN傳輸通常都是運行在物理代理上的備份程序的最佳性能選擇。在虛擬機內部是被禁用的,所以在虛擬代理上使用SCSI HotAdd代替。
SAN傳輸並不總是還原的最佳選擇。它在厚磁盤(thick disks)上提供最佳性能,但是在薄磁盤(thin disks)上具有最差的性能,原因是磁盤管理API的往返,AllocateBlock以及ClearLazyZero。對於薄磁盤的還原,NBDSSL通常更快,並且NBD更快。SAN還原時必須禁用修改塊跟蹤(CBT)。SAN傳輸不支持寫入重寫日誌(快照或子磁盤),僅能還原基本磁盤。
vSphere5.5以前,還原寫入SAN時,磁盤大小必須是底層VMFS塊大小的整數倍,否足寫入磁盤的最後一塊將會失敗。例如,如果虛擬磁盤的塊大小是1MB,而數據存儲是16.3MB,最後0.3MB會寫入失敗,除非還原軟件填充0.7MB的零值。這在ESXi5.5這種已經修改了。
在SAN模式中打開一個本地虛擬磁盤,可能讀沒有問題(磁盤爲空),但是寫會拋出錯誤。儘管程序使用NULL作爲參數調用VixDiskLib_ConnectEx()以接受默認的傳輸模式,但是如果ESXi主機連接了SAN存儲,就會選擇SAN模式。VixDiskLib應該但是不會檢查SAN在打開時是否可用。對於本地磁盤,程序必須顯式指明要求NBD或NBDSSL模式。
HotAdd傳輸的最佳實踐
將代理部署到VMFS-5卷或大塊的VMFS-3捲上,可以備份非常大的虛擬磁盤(Deploy the proxy on VMFS-5volumes, or on VMFS-3 volumes capable of large block size so that the proxy canbackup very large virtual disks)。
在基本磁盤相同的數據存儲上,將會爲HotAdded磁盤創建重寫日誌。當HotAdded磁盤仍然處於附加狀態時,不要移除目標虛擬機(正在備份的虛擬機)。如果移除掉的話,HotAdd無法正確清除重寫日誌,所以虛擬磁盤必須從備份應用中手動移除。在清除之前,也不能移除快照。刪除快照會導致未合併的重寫日誌。
HotAdd是一個SCSI特性,不適用於IDE磁盤。半虛擬化的SCSI控制器(PVSCSI)不支持HotAdd,使用LSI控制器代替。
通過vSphere客戶端已從控制器上的所有磁盤,同樣也會移除控制器。你可能需要在代碼包含一些檢查並檢測這些,並重新配置以添加控制器。
HotAdd備份或還原在Windows上創建的虛擬磁盤需要有和原始磁盤不同的磁盤簽名。NBD模式下在後臺重新讀入和寫入磁盤的第一個扇區。
在刪除快照之前,需要使用VixDiskLib_Cleanup()釋放HotAdded磁盤。清除可能導致修改跟蹤(ctk)文件的不當移除,你可以通過重新啓動虛擬機修復這個問題。
在SAN存儲上運行WindowsServer2008代理的客戶,需要將SAN策略設置爲onlineAll。
NBDSSL傳輸的最佳實踐
不同版本的ESX/ESXi有不同的默認超時。在ESXi5.0以前沒有默認的網絡文件拷貝(NFC)超時,默認的NFC超時值在未來的版本會可能發生變化。
VMware建議在VixDiskLib配置文件中指明默認的NFC超時。如果你沒有設置一個超時,老版本的ESX/ESXi會無限期的保持磁盤打開狀態,知道vpxa或者hostd被重啓。但是如果你設置了一個超時值,你就需要執行一些“keepalive”操作,避免磁盤在服務器端被關閉了。定期的讀取第0塊是一種比較好的保持連接的操作。
開始時,建議設置接受和請求的超時爲3分鐘,讀爲1分鐘,寫爲10分鐘,nfcF***vr和nfcF***vrWrite沒有超時(0)。
通用備份和還原
對於虛擬磁盤的增量備份,通常需要在第一個快照之前啓用修改塊跟蹤(CBT)。當進行虛擬磁盤的完全還原時,在還原期間需要禁用CBT。基於文件的還原影響修改塊跟蹤,但是部分還原時禁用CBT是可選的,除非是SAN傳輸。SAN傳輸寫時需要禁用CBT,因爲文件系統需要統計薄磁盤(thin-disk)的分配以及延遲清零(clear-lazy-zero)操作。
備份軟件需要忽略無法快照的獨立磁盤,這些磁盤不適用於備份。如果創建快照,它們會拋出錯誤。
要備份一個厚磁盤(thick disk),代理的數據存儲必須有足夠的可用空間,存儲備份虛擬機的最大配置磁盤大小。薄置備磁盤(thin-provisioneddisk)通常備份很快。厚磁盤在數據存儲中佔用了所有的分配大小。爲了節省空間,你可以選擇薄置備(think-provisioned)磁盤,它只消耗實際包含數據的空間。
對於vSphere5.1及以後版本中的SSL認證檢查,備份代理中必須配置DNS服務,否則SSL_Verify就會返回“找不到主機”的錯誤。
如果你針對禁用CBT的延遲清零的厚磁盤執行完全備份,軟件讀取所有扇區,將空扇區(延遲清零,lazy-zero)中的數據轉換爲真正的零值。還原時,這個完全備份的數據就會產生貪婪清零(eager-zeroed)的厚磁盤。這也是VMware推薦在第一個快照前啓用CBT的一個原因。
備份、還原薄置備(Thin-Provisioned)磁盤
薄置備磁盤在第一次寫時創建。相比於厚磁盤,針對薄置備磁盤的第一次寫操作會導致額外的開銷,不論是使用NBD,NBDSSL,或者HotAdd。這是由於塊分配的開銷,而不是VDDK高級傳輸的開銷。但是一旦薄置備磁盤創建成功,性能和厚磁盤相似。
當程序對薄置備磁盤上的未分配區域執行隨機的I/O或寫操作時,後續的備份可能比期望的大,儘管CBT已經啓用。有時,磁盤整理可能會減少備份的大小。
虛擬機配置
不要逐字拷貝配置文件,它可能發生改變。例如,.vmx文件的項指向了快照,而不是基本磁盤。.vmx文件包含了虛擬機當前磁盤相關的信息,嘗試還原這個信息可能會失敗。需要使用PropertyCollector並保存ConfigInfo結構。
關於修改塊跟蹤
QueryChangedDiskAreas(“*”)返回虛擬磁盤被使用、分配的區域。當前的實現依賴於VMFS的屬性,類似於SAN傳輸模式用來定位SCSI LUN上的數據的屬性。依賴於虛擬磁盤的未分配區域(file holes),以及VMFS塊的延遲清零定義(designation)。因此,修改塊跟蹤只有在VMFS上產生有意義的結果。在其他存儲類型上,它要麼失敗,要麼返回包含所有磁盤的單個內容。
你應該按照“啓用修改塊跟蹤”一節中講到的順序啓用修改塊跟蹤。第一次調用QueryChangedDiskAreas(“*”)時,它會返回虛擬磁盤上已經分配的區域。後續的調用返回修改的區域,而不是已分配區域。如果在啓用修改塊跟蹤之前,在一個快照後調用QueryChangedDiskAreas,它同樣會返回虛擬磁盤的未分配區域。對於薄置備磁盤,這會包含大量的零值數據。
客戶端系統並不知道修改塊跟蹤的存在。當虛擬機向虛擬磁盤寫入一塊數據時,這個塊就被認爲正在使用。如果修改塊跟蹤已經啓用,“*”請求返回的信息會被計算,並且.ctk文件已經被已分配的塊填充。這種機制在修改塊跟蹤被啓用前不能報告針對虛擬磁盤所在的修改。
Windows和Linux實現
以下的章節討論的一些問題,依賴於虛擬機是運行Windows還是Linux。
使用Microsoft陰影拷貝
Microsoft陰影拷貝,也叫做卷快照服務(Volume Snapshot Service, VSS),是一個Windows服務器的數據備份特性,用來創建數據在指定時間點的一致性副本(叫做陰影拷貝)。
創建Windows虛擬機快照時執行VSS靜止操作(Performing VSS quiescing),VMware工具就會生成一個vss-manifest.zip文件,包含備份組件文檔(BCD)和writer清單。主機代理會將這個清單文件保存到虛擬機的snapshotDir中。備份程序需要獲得這個vss-mainfest.zip文件並保存到備份介質中。有幾種方式可以獲得這個文件:
使用數據存儲的HTTPS訪問協議,例如,通過瀏覽到https://<server-or-host>/folder/並繼續進入快照目錄,直到發現vss-mainfest.zip文件。
調用vSphereAPI中的CopyDatastoreFile_Task方法。該方法使用上面組成的HTTPS URL或者一個數據存儲路徑。(CopyVirtualDisk_Task針對VMDK文件。)
在vMA或vCLI中使用vifs命令行
在PowerCLI中使用Copy-DatastoreItem命令(需要PowerShell以及VMware組件)。
使用的靜止類型(type of quiescing)依賴於備份虛擬機的操作系統,如表7-4所示。ESX/ESXi4.1使用應用層的靜止來支持Windows2008(ESX/ESXi 4.1 addedsupport for Windows 2008 guests using application level quiescing)。
還原必須使用備份應用的客戶端代理來完成。數據保護的vSphere API沒有提供主機代理來支持這種應用。使用SSPI認證的應用無法正確工作,因爲HTTP訪問需要一個用戶名和密碼,除非會話最近已經認證過了。
Windows2008應用層靜止使用硬件快照提供者執行。虛擬機靜止後,硬件快照提供者針對每個磁盤創建了兩個重寫日誌:一個用於運行的虛擬機的寫操作,另一個用於客戶機中的VSS和writers在快照操作後修改磁盤(another for the VSS and writersin the guest to modify the disks after the snapshot operation as part of thequiescing operations)。
快照配置信息將第二個重寫日誌作爲快照的一部分。這個重寫日誌記錄了客戶機中所有應用程序的靜態狀態(This redo logrepresented the quiesced state of all the applications in the guest)。備份時必須使用VDDK1.2或更新版本才能打開這個重寫日誌。舊的VDDK1.1軟件不能打開第二個重寫日誌進行備份。
Windows2008虛擬機上的應用程序的一致性(application consistent quiescing),僅在這些寫虛擬機由vSphere4.1或更新版本中創建時才起作用。vSphere4.0創建的虛擬機通過修改虛擬機的enableUUID屬性,可以啓用應用程序的一致性。
關於VSS的信息,可以查看Microsoft TechNet文章,《How Volume Shadow Copy ServiceWorks》。關於安全支持提供者接口(SSPI)的信息,可以查看MSDN網站。
啓用Windows2008虛擬機應用程序一致性
1打開vSphere客戶端,並登陸到vCenter Server。
2選擇“虛擬機和模板”,並點擊“虛擬機”選項卡。
3右鍵點擊你將要設置磁盤UUID屬性的Windows2008虛擬機,選擇“電源 > 關閉”。等待虛擬機關閉。
4右鍵點擊虛擬機,選擇“編輯設置”。
5點擊“選項”欄,選擇“通用”入口。
6點擊“配置參數”,將會出現配置參數窗口。
7點擊“添加行”。
8在名稱列中填入“disk.EnableUUID”,在值列中寫入“TRUE”。
9點擊“確定”並“保存”。
10打開虛擬機電源。
在UUID屬性啓用後,虛擬機的應用程序一致性就可用了。
備份和還原的程序一致性
下面是執行應用一致性備份和還原的近似過程:
1調用CreateSnapshot_Task函數,並設置quiescent標識爲true。
2使用VDDK打開磁盤的葉節點,讀取基本VMD和快照數據。
3刪除第一步中創建的快照。
4還原時,創建新的虛擬機。
5使用VDDK將數據邪惡如VMDK磁盤。它包含基本和靜態信息。
備份時,如果將quiesce標識設置爲TRUE並創建快照,那麼所有的靜態條件都滿足了,快照創建時調用了VSS,快照磁盤呈現了客戶機系統的應用程序的一致性狀態。你可以通過以下操作來確認這點:下載並解壓VSS manifest.zip文件來檢查是否備份了組件文檔(在這種情況下文件系統執行了靜默操作(file sysem quiescingwas performed))或者同樣檢查writer mainfests文件(這時執行了應用程序的靜默操作(application quiescing wasperformed))。
靜默操作(quiescing)調用了Microsoft設計的VSS機制,關於VSS備份、還原的驗證,可以參考Microsoft提供的VSS文檔。VMware提供一個vss-manifest.zip文件,包含了備份、writer組件的詳細信息。這是備份後VSS機制生成的文件。根據Microsoft VSS文檔驗證這些備份/writer組件的細節,你可以驗證一個具體的應用程序一致性操作是否成功執行。
VMware工具作爲VSS請求者負責初始化VSS快照。用戶發送一個請求到主機(hostd),要求虛擬機的靜默快照。這個請求通過主機傳遞到VMware工具並執行VSS快照。VSS快照完成後,無論失敗還是成功,都將會和主機進程進行通信。VSS快照創建時會生成vss-mainfest文件,或者出錯時沒有該文件。
VSS請求者建立備份操作的所有配置信息,包括快照是否在組件模式下執行,快照是否包含可啓動的系統狀態,以及快照是用於完全備份還是差異備份。如果執行應用程序一致性操作,將會涉及所有的writer和組件。
VMware VSS的實現
在Windows Server 2008上,磁盤UUID必須啓用,才能創建VSS靜默快照。如果虛擬機從硬件版本4升級而來,磁盤UUID也可能沒有啓用。
VMwareVSS不支持使用IDE磁盤的虛擬機,也不支持沒有可用SCSI插槽(with an insufficient number offree SCSI slots)的虛擬機。
vSphere5.1之前,恢復到一個可寫的快照,有時會留下系統沒有移除的孤立虛擬磁盤。在vSphere5.1版本中,可寫快照作爲兄弟快照被正確的記錄(writable snapshotsare corredcly accounted for as sibling snapshots)。這允許更乾淨的管理,因爲磁盤鏈匹配快照結構,並避免了孤立磁盤。Linux備份軟件創建只讀的快照,所有它並不受影響。在Windows上,VSS備份軟件可能創建連個快照,一個通過設置quiesce標識爲true調用CreateSnapshot_Task創建爲可寫快照。
要添加更新的應用程序控制,需要指明:
是否調用冷凍和解凍(pre-freeze and post-thaw)腳本。
是否是靜默操作(quiescing)。
VSS快照上下文(應用程序,文件系統靜默)
VSS備份上下文(完全,差異,增量)
靜默操作時涉及的writer和組件
當一個writer靜默操作失敗時,退出或繼續靜默操作
重試次數
A VSS quiesced snapshot reports as VSS_BT_COPY to VSS, hence no log truncation。VSS manifest文件可以通過HTTP下載。默認情況下,所有VSS writer都會涉及,但是也存在排除已存在writer的機制。
Linux HotAdd以及SCSI控制器ID
當使用HotAdd備份時,通常按數字順序向Linux虛擬機添加SCSI控制器。
Linux系統缺乏一個接口來通知SCSI控制器被分配到哪個總線ID,所以HotAdd假設SCSI控制器的唯一ID和它的總線ID相關。這個假設可能不成立。例如,如果Linux虛擬機的第一個SCSI控制器非配到總線ID 0,但是你添加了一個SCSI控制器並將它分配到總線ID 3,HotAdd高級傳輸模式可能會失敗,因爲它期望唯一ID爲1。爲避免出現問題,當向虛擬機添加SCSI控制器時,必須按順序分配下一個可用的總線編號。
還需要注意的是,如果新添加的虛擬磁盤引用了一個還不存在的控制器,VMware爲隱式的添加一個SCSI控制器來完成bus:disk分配。例如,如果磁盤0:0和0:1已經存在,添加一個磁盤1:0沒有問題,但是添加磁盤3:0就會打破總線ID的順序,隱式的創建了一個順序外的SCSI控制器3.爲了避免HotAdd問題,還需要按數字順序添加虛擬磁盤。