本文目录
背景
假如有一个由N台虚拟机组成的SAP系统,在上海的Azure数据中心落地,但是客户可能会想在北京数据中心搭建一套灾备系统(DR SITE)。虽然每一个数据中心都会标榜自己机房的安全性,稳定性和先进性,但是在天灾人祸不可控因素面前,为了降低业务中断时间,一套能够快速恢复的灾备系统仍旧值得拥有。
灾备系统可以重新部署,然后再同步核心数据。也可以全站复制,然后再调整核心参数。假如选择了后者,就抛出了一个问题:如何在主站不关机的情况下,把主站的虚拟机迁移到灾备站点?
为什么不选择虚拟机映像?
Azure平台上面支持给虚拟机创建映像,但是这样的映像创建时原虚拟机必须关机,并且要被通用化。通用化后,原虚拟机几乎等同于废掉了。所以此方案不可取。
为什么要选择磁盘快照?
因为磁盘快照创建时,磁盘所在的虚拟机可以保持开机。并且磁盘快照支持导出VHD,复制到灾备站点的存储账户中,就可以创建虚拟机了。
为什么需要自动化,批量,并行创建磁盘快照?
基于磁盘备份虚拟机,在不关机的情况下,数据一致性是不可能做到的,那么我们能做的就是减少整个系统进行快照捕获的时间差。假如有15个磁盘,每个手动操作可能需要5分钟,差不多得1个小时左右。如果使用PowerShell脚本自动化并行捕获快照的化,可能只需要2分钟多一点,效率提示了90%多。
脚本编写
多线程并行提高执行速度,在PowerShell中有很多途径,今天我们选择Runspace。脚本分为两部分:
- 第一部分:负责虚拟机的磁盘信息收集。
- 第二部分:负责并行快照捕获。
磁盘信息收集
输入是 Json字符串,包括资源组名称,以及资源组下面的机器名,形如:
[
{
"rg":"SBD-SAP-POC",
"vm":["Jump01","SBDEVICE01","AS01","AS02","NFS01","NFS02","ASCS01","ASCS02"]
}
]
输出也是Json字符串,包含了输入机器中所有的磁盘参数,形如:
[
{
"OsType": 0,
"EncryptionSettings": null,
"Name": "Jump01_OsDisk_1_7cf45e4ffedc4756a7a6ca818238a4b7",
"Vhd": null,
"Image": null,
"Caching": 2,
"WriteAcceleratorEnabled": null,
"CreateOption": "FromImage",
"DiskSizeGB": 127,
"ManagedDisk": "Microsoft.Azure.Management.Compute.Models.ManagedDiskParameters",
"ResouceGroup": "SBD-SAP-POC",
"VmName": "Jump01",
"StorageAccountType": "Standard_LRS",
"ResourceId": "/subscriptions/xxxxxxx-xxx/resourceGroups/SBD-SAP-POC/providers/Microsoft.Compute/disks/Jump01_OsDisk_1_7cf45e4ffedc4756a7a6ca818238a4b7",
"ImageId": "a17aa115-4f46-4d6c-a30c-c8bf0e09efff"
},
上面红色的ImageId,是我自己生成的GUID,在创建快照时,也会作为磁盘快照的名称。所以这一步生成的json文件,不仅仅是中间结果,还是我们创建的快照和原始虚拟机之间的关系映射表,要保存好。
贴脚本:
Import-AzureRmContext -Path .\az.data
#
# 包装 VM的Disk对象,增加资源组和虚拟机名称,并生成一个唯一的镜像Id
#
function New-WrapDisk
{
param(
$Disk,
$ResouceGroup,
$VmName,
$StorageAccountType,
$ResourceId)
$Disk | Add-Member -NotePropertyName "ResouceGroup" -NotePropertyValue $ResouceGroup
$Disk | Add-Member -NotePropertyName "VmName" -NotePropertyValue $VmName
$Disk | Add-Member -NotePropertyName "StorageAccountType" -NotePropertyValue $StorageAccountType
$Disk | Add-Member -NotePropertyName "ResourceId" -NotePropertyValue $ResourceId
$Disk | Add-Member -NotePropertyName "ImageId" -NotePropertyValue (New-Guid)
$Disk
}
# 需要批量创建镜像的磁盘所在的虚拟机列表,这里定义成json,
# 方便后续从文件进行配置读取
#
$vmListJson = @"
[
{
"rg":"SBD-SAP-POC",
"vm":["Jump01","SBDEVICE01","AS01","AS02","NFS01","NFS02","ASCS01","ASCS02"]
}
]
"@ | ConvertFrom-Json
#
# 导出所有虚拟机磁盘的配置信息
#
$vmListJson | ForEach-Object {
$rg = $_.rg
$_.vm | foreach {
$vmName = $_
$vm = Get-AzureRmVM -ResourceGroupName $rg -Name $vmName
$vm.StorageProfile.OsDisk | foreach {
New-WrapDisk -Disk $_ `
-ResouceGroup $rg `
-VmName $vmName `
-StorageAccountType $_.ManagedDisk.StorageAccountType `
-ResourceId $_.ManagedDisk.Id
}
$vm.StorageProfile.DataDisks | foreach {
New-WrapDisk -Disk $_ `
-ResouceGroup $rg `
-VmName $vmName `
-StorageAccountType $_.ManagedDisk.StorageAccountType `
-ResourceId $_.ManagedDisk.Id
}
}
} | ConvertTo-Json -Depth 1 | Out-File vm-disk.config
磁盘快照捕获
脚本还是很精简的:
- 读取第一部分生成的磁盘信息文件。
- 检查目标资源组是否存在,如果不存在则创建。
- 并行处理,没有选择用Job,也没有选择用Workflow,而选择了Runspace,只是为了让所有的工作在同一个会话中,避免Azure账号多次登陆。
#
# 创建磁盘镜像函数定义
#
function New-AzureVMDiskImage
{
param(
[string]$DiskConfigFileName,
[string]$AzureContextFileName,
[string]$ImageResourceGroup,
[string]$Location
)
$diskConfig = Get-Content .\vm-disk.config -Raw | ConvertFrom-Json
Write-Output "配置文件中,共检测到$($diskConfig.Count)个磁盘"
# 登陆
Import-AzureRmContext -Path $AzureContextFileName | Out-Null
# 创建资源组
$rg = Get-AzureRmResourceGroup -Name $ImageResourceGroup -ErrorAction SilentlyContinue
if($rg -eq $null)
{
$rg = New-AzureRmResourceGroup -Name $ImageResourceGroup -Location $Location
}
$sb = {
param(
$cfg,
$Location,
$ImageResourceGroup)
$disk = Get-AzureRmDisk `
-ResourceGroupName $cfg.ResouceGroup `
-DiskName $cfg.Name
$diskSnapshotCfg = new-AzureRmSnapshotConfig `
-SourceUri $disk.Id `
-Location $Location `
-CreateOption 'Copy'
Write-Output "正在创建磁盘【$($disk.Name)】 的镜像.."
New-AzureRmSnapshot `
-Snapshot $diskSnapshotCfg `
-SnapshotName $cfg.ImageId `
-ResourceGroupName $ImageResourceGroup | Out-Null
Write-Output "磁盘【$($disk.Name)】 的镜像创建完毕"
}
$jobs = @()
foreach ($cfg in $diskConfig)
{
$job=[PowerShell]::Create()
$job = $job.AddScript($sb);
$job = $job.AddParameter("cfg",$cfg).AddParameter("Location",$Location).AddParameter("ImageResourceGroup",$ImageResourceGroup)
Write-Output "创建磁盘【$($cfg.Name)】的Job已开始"
$jobs += New-Object PSObject -Property @{
cfg = $cfg
Task = $job
Result = $job.BeginInvoke()
}
}
ForEach ($job in $jobs)
{
$job.Task.EndInvoke($job.Result)
}
}
#
# 调用示例
#
New-AzureVMDiskImage `
-DiskConfigFileName ".\vm-disk.config" `
-AzureContextFileName ".\az.data" `
-ImageResourceGroup "SBD-SAP-POC-Image" `
-Location 'ChinaNorth'
脚本执行的输出如下:
配置文件中,共检测到15个磁盘 创建磁盘【Jump01_OsDisk_1_7cf45e4ffedc4756a7a6ca818238a4b7】的Job已开始 创建磁盘【SBDEVICE01】的Job已开始 创建磁盘【SBDEVICE01-Data-1】的Job已开始 创建磁盘【SBDEVICE01-Data-2】的Job已开始 创建磁盘【SBDEVICE01-Data-3】的Job已开始 创建磁盘【AS01_OsDisk_1_783772817628472a8dd163dd817c7a4f】的Job已开始 创建磁盘【AS01-Data-1】的Job已开始 创建磁盘【AS02_OsDisk_1_e38de3104b494da58f639a0a7b5b022c】的Job已开始 创建磁盘【AS02-Data-1】的Job已开始 创建磁盘【NFS01_OsDisk_1_1c4b77d6c0b241faa65de4b490788d81】的Job已开始 创建磁盘【NFS01-Data-1】的Job已开始 创建磁盘【NFS02_OsDisk_1_b93ab2b39b7948d5ac71047af1916d4c】的Job已开始 创建磁盘【NFS02-Data-1】的Job已开始 创建磁盘【ASCS01_OsDisk_1_ca38c16f7119453d8fb5007196997952】的Job已开始 创建磁盘【ASCS02_OsDisk_1_f2dd7bbc20a040b1bffdecf89c480159】的Job已开始 正在创建磁盘【Jump01_OsDisk_1_7cf45e4ffedc4756a7a6ca818238a4b7】 的镜像.. 磁盘【Jump01_OsDisk_1_7cf45e4ffedc4756a7a6ca818238a4b7】 的镜像创建完毕 正在创建磁盘【SBDEVICE01】 的镜像.. 磁盘【SBDEVICE01】 的镜像创建完毕 正在创建磁盘【SBDEVICE01-Data-1】 的镜像.. 磁盘【SBDEVICE01-Data-1】 的镜像创建完毕 正在创建磁盘【SBDEVICE01-Data-2】 的镜像.. 磁盘【SBDEVICE01-Data-2】 的镜像创建完毕 正在创建磁盘【SBDEVICE01-Data-3】 的镜像.. 磁盘【SBDEVICE01-Data-3】 的镜像创建完毕 正在创建磁盘【AS01_OsDisk_1_783772817628472a8dd163dd817c7a4f】 的镜像.. 磁盘【AS01_OsDisk_1_783772817628472a8dd163dd817c7a4f】 的镜像创建完毕 正在创建磁盘【AS01-Data-1】 的镜像.. 磁盘【AS01-Data-1】 的镜像创建完毕 正在创建磁盘【AS02_OsDisk_1_e38de3104b494da58f639a0a7b5b022c】 的镜像.. 磁盘【AS02_OsDisk_1_e38de3104b494da58f639a0a7b5b022c】 的镜像创建完毕 正在创建磁盘【AS02-Data-1】 的镜像.. 磁盘【AS02-Data-1】 的镜像创建完毕 正在创建磁盘【NFS01_OsDisk_1_1c4b77d6c0b241faa65de4b490788d81】 的镜像.. 磁盘【NFS01_OsDisk_1_1c4b77d6c0b241faa65de4b490788d81】 的镜像创建完毕 正在创建磁盘【NFS01-Data-1】 的镜像.. 磁盘【NFS01-Data-1】 的镜像创建完毕 正在创建磁盘【NFS02_OsDisk_1_b93ab2b39b7948d5ac71047af1916d4c】 的镜像.. 磁盘【NFS02_OsDisk_1_b93ab2b39b7948d5ac71047af1916d4c】 的镜像创建完毕 正在创建磁盘【NFS02-Data-1】 的镜像.. 磁盘【NFS02-Data-1】 的镜像创建完毕 正在创建磁盘【ASCS01_OsDisk_1_ca38c16f7119453d8fb5007196997952】 的镜像.. 磁盘【ASCS01_OsDisk_1_ca38c16f7119453d8fb5007196997952】 的镜像创建完毕 正在创建磁盘【ASCS02_OsDisk_1_f2dd7bbc20a040b1bffdecf89c480159】 的镜像.. 磁盘【ASCS02_OsDisk_1_f2dd7bbc20a040b1bffdecf89c480159】 的镜像创建完毕
这是所有磁盘创建完成后在Azure门户上的列表截图:
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!

