PowerShell批量给Azure虚拟机磁盘创建镜像


背景

假如有一个由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

磁盘快照捕获

脚本还是很精简的:

  1. 读取第一部分生成的磁盘信息文件。
  2. 检查目标资源组是否存在,如果不存在则创建。
  3. 并行处理,没有选择用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门户上的列表截图:

 

本文链接: https://www.pstips.net/create-azure-disk-snapshot-in-batches.html
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!

关于 Mooser Lee

我是一个Powershell的爱好者,创建了PowerShell中文博客,热衷于Powershell技术的搜集和分享。本站部分内容来源于互联网,不足之处敬请谅解,并欢迎您批评指正。

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注