Azure Lei Zhang的博客

weibo: LeiZhang的微博/QQ: 185165016/QQ群:319036205/邮箱:leizhang1984@outlook.com/TeL:139-161-22926

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  390 随笔 :: 0 文章 :: 394 评论 :: 0 引用

  《Windows Azure Platform 系列文章目录

 

  为什么要写这篇Blog?

  之前遇到过很多客户提问:

  (1)我之前创建的虚拟机,没有加入虚拟网络。现在需要重新加入虚拟机网络,应该如何操作?

  (2)之前创建的虚拟机,想重新设置DNS。但是又想保留虚拟机里面的内容,应该如何操作?

  (3)我之前部署在订阅A下面的一些虚拟机,现在想迁移到订阅B下面,应该如何操作?

  

  在回答这些问题之前,我们先回顾一下Azure虚拟机可能需要的资源:

  (1)虚拟网络(Azure Virtual Network)

  定义Subnet和IP Rang,Azure虚拟机的内网IP地址是由Azure Virtual Network

  (2)云服务(Cloud Service)

  定义了Azure虚拟机的DNS地址

  (3)VHD文件(Virtual Hard Disk)

  我们在虚拟机安装的任何软件和配置,都是持久化保存在Azure VHD文件里的。

  所以只要能保留VHD,就可以迁移Azure虚拟机,同时保留虚拟机里面的内容

 

  好了,看到这里,各位看官明白了,其实我们只要迁移Azure虚拟机所在的VHD文件即可。

  然后还有一个问题请读者注意:

  如果你的Azure VM所在的DNS Name已经固定的公网IP地址(Reserved IP)

  (Azure China (8) 使用Azure PowerShell创建虚拟机,并设置固定Virtual IP Address和Private IP)

 

  我们在迁移Azure虚拟机的时候需要注意,固定公网IP地址只能属于某一个订阅,无法跨订阅,或者是多个订阅共享同一个公网IP地址。

  所以当我们将某一台虚拟机,从订阅A迁移到订阅B的时候,订阅A下的该虚拟机的公网IP地址,是无法转换到订阅B下的。

 

  当我们跨订阅迁移Azure虚拟机的时候,笔者建议的做法是:先将订阅A下的公网IP地址进行释放。然后再订阅B下申请新的公网IP地址。

  如果客户已经设置了自定义域名的A记录,需要重新设置,指向到订阅B下的公网IP地址。

 

  好了,描述了那么多的内容。我们开始今天的文章吧。

  首先笔者先模拟一个场景,假设我们有两个订阅,Subscription_ASubscription_B

  在订阅Subscription_A下有以下部署:

  -  虚拟网络名称VNetA,有一个子网Subnet-1

  -  有1台虚拟机,虚拟机DNS Name为LeiCloudServiceA

  -  该虚拟机的机器名为LeiVMA,操作系统为Windows Server 2012 中文版

  -  这台虚拟机有2块磁盘

 

  我们需要把上述虚拟机,迁移到订阅Subscription_B

  -  手动创建虚拟网络VNetB

  -  新的DNS Name为LeiCloudServiceB,新的虚拟机的机器名为LeiVMB

  -  需要保留原虚拟机里的磁盘内容

 

  关键步骤有以下几点:

  (1)请在Azure PowerShell下运行

  (2)输入源订阅,和目标订阅的Azure用户名和密码

  (3)手动创建目标订阅下的虚拟机网络和子网

  (4)更新PowerShell下的相关参数

  (5)PowerShell会提示:首先把需要迁移的Azure虚拟机关机

  (6)PowerShell会在目标订阅下,创建新的存储账户

  (7)PowerShell会将源订阅的下的,需要迁移的虚拟机的所有VHD文件,拷贝到目标订阅下的,新的存储账号里

  (8)在目标订阅下,将VHD系统文件创建为虚拟机镜像,将VHD数据文件创建为虚拟机磁盘镜像

 

  (9)如果源订阅和目标订阅是同一个订阅的话,会有提示:Please update the Diskname in the configuration file 

  客户手动修改PowerShell同一目录下的,ExportedVMConfig文件,修改以下XML节点:

  <DataVirtualHardDisks>和<OSVirtualHardDisk>

  

  在DiskName里,增加相应的后缀。

  比如$DiskNameSuffix变量,我们赋值为-prem

  则修改上面的红色部分的DiskName,参数增加后缀-prem。如下图:

  

   修改完毕后,直接按Enter 继续执行PowerShell 

 

 

  PowerShell源代码如下:

<#
Modified by Lei Zhang on 2016-04-29
#>

Param 
(
    #源订阅ID
    [string] $SourceSubscriptionId="e2eaa986-29d9-48c9-8302-1e2900a4504b",

    #源云服务名称
    [string] $SourceCloudServiceName="lei2012chnvm",

    #源虚拟机名称
    [string] $SourceVMName="lei2012chnvm01",

    #源Azure Storage Container Name
    [string] $SourceStorageContainerName="vhds",

    #目标订阅ID
    [string] $DestSubscritpionId="919ae904-1260-4f4b-85a6-8ac9d44d3106",

    #目标云服务名称
    [string] $DestCloudServiceName="LeiNewVM",

    #目标虚拟机名称
    [string] $DestVMName="LeiNewVM01",

    #目标存储账户名称
    [string] $DestStorageAccountName="leinewvmstorage1",

    #目标Azure Storage Container Name    
    [string] $DestStorageContainerName="vhds",

    #目标虚拟机所在数据中心,分别为China North和China East
    [string] $DestLocationName,

    #目标虚拟机所在虚拟网络名称
    [string] $DestVNetName="LeiDesVNet",

    #目标虚拟机所在子网名称
    [string] $DestSubNet="Subnet-1",

    #目标虚拟机磁盘文件后缀
    [string] $DiskNameSuffix="-prem"
)


$IsSameSub = $false

if (($SourceSubscriptionId -eq $DestSubscritpionId) -or ($DestSubscritpionId -eq ""))
{
    Write-Host "VM is copied at the same subscription!" -ForegroundColor Green
    $IsSameSub = $true
    $DestSubscritpionId = $SourceSubscriptionId
}

if ($SourceStorageContainerName -eq "")
{
    Write-Host "Using the default source storage container vhds!" -ForegroundColor Green
    $SourceStorageContainerName = "vhds"
}

if ($DestStorageContainerName -eq "")
{
    Write-Host "Using the default destination storage container vhds!" -ForegroundColor Green
    $DestStorageContainerName = "vhds"
}

if ($DestLocationName -eq "")
{
    $DestLocationName = "China East"
}

if ($DestSubNet -eq "")
{
    $DestSubNet = "Subnet-1"
}

if (($DiskNameSuffix -eq $null) -or ($DiskNameSuffix -eq ""))
{
    $DiskNameSuffix = "-prem"
    Write-Host "Set the copyed Disk Name Suffix as:"+ $DiskNameSuffix -ForegroundColor Green
}

Write-Host "`t================= Migration Setting =======================" -ForegroundColor Green
Write-Host "`t  Source Subscription ID          = $SourceSubscriptionId           " -ForegroundColor Green
Write-Host "`t Source Cloud Service Name      = $SourceCloudServiceName       " -ForegroundColor Green
Write-Host "`t            Source VM Name      = $SourceVMName                 " -ForegroundColor Green
Write-Host "`t      Dest Subscription ID      = $DestSubscritpionId              " -ForegroundColor Green
Write-Host "`t   Dest Cloud Service Name      = $DestCloudServiceName         " -ForegroundColor Green
Write-Host "`t Dest Storage Account Name      = $DestStorageAccountName       " -ForegroundColor Green
Write-Host "`t Source Storage Container Name = $SourceStorageContainerName   " -ForegroundColor Green
Write-Host "`t Dest Storage Container Name      = $DestStorageContainerName   "   -ForegroundColor Green
Write-Host "`t             Dest Location      = $DestLocationName             " -ForegroundColor Green
Write-Host "`t                 Dest VNET = $DestVNetName                      " -ForegroundColor Green
Write-Host "`t               Dest Subnet = $DestSubNet                           " -ForegroundColor Green
Write-Host "`t               Disk Name Prefix = $DiskNameSuffix              " -ForegroundColor Green
Write-Host "`t===============================================================" -ForegroundColor Green

#######################################################################
#  Verify Azure Source Subscription and Azure Desination Subscription
#######################################################################
Write-Host "Please verify the Source Azure Subscription" -ForegroundColor Green
Add-AzureAccount -Environment AzureChinaCloud

Write-Host "Please verify the Destination Azure Subscription" -ForegroundColor Green
Add-AzureAccount -Environment AzureChinaCloud

$ErrorActionPreference = "Stop"

try{ stop-transcript|out-null }
catch [System.InvalidOperationException] { }

$workingDir = (Get-Location).Path
$log = $workingDir + "\VM-" + $SourceCloudServiceName + "-" + $SourceVMName + ".log"
Start-Transcript -Path $log -Append -Force

Select-AzureSubscription -SubscriptionId $SourceSubscriptionId

#######################################################################
#  Check if the VM is shut down 
#  Stopping the VM is a required step so that the file system is consistent when you do the copy operation. 
#  Azure does not support live migration at this time.. 
#######################################################################
$sourceVM = Get-AzureVM –ServiceName $SourceCloudServiceName –Name $SourceVMName
if ( $sourceVM -eq $null )
{
    Write-Host "[ERROR] - The source VM doesn't exist. Exiting." -ForegroundColor Red
    Exit
}

# check if VM is shut down
if ( $sourceVM.Status -notmatch "Stopped" )
{
    Write-Host "[Warning] - Stopping the VM is a required step so that the file system is consistent when you do the copy operation. Azure does not support live migration at this time. If you’d like to create a VM from a generalized image, sys-prep the Virtual Machine before stopping it." -ForegroundColor Yellow
    $ContinueAnswer = Read-Host "`n`tDo you wish to stop $SourceVMName now? (Y/N)"
    If ($ContinueAnswer -ne "Y") { Write-Host "`n Exiting." -ForegroundColor Red; Exit }
    $sourceVM | Stop-AzureVM  -StayProvisioned

    # wait until the VM is shut down
    $sourceVMStatus = (Get-AzureVM –ServiceName $SourceCloudServiceName –Name $SourceVMName).Status
    while ($sourceVMStatus -notmatch "Stopped") 
    {
        Write-Host "Waiting VM $vmName to shut down, current status is $sourceVMStatus" -ForegroundColor Green
        Sleep -Seconds 5
        $sourceVMStatus = (Get-AzureVM –ServiceName $SourceCloudServiceName –Name $SourceVMName).Status
    } 
}

# exporting the source vm to a configuration file, you can restore the original VM by importing this config file
# see more information for Import-AzureVM
$vmConfigurationPath = $workingDir + "\ExportedVMConfig-" + $SourceCloudServiceName + "-" + $SourceVMName +".xml"
Write-Host "Exporting VM configuration to $vmConfigurationPath" -ForegroundColor Green
$sourceVM | Export-AzureVM -Path $vmConfigurationPath

#######################################################################
#  Copy the vhds of the source vm 
#  You can choose to copy all disks including os and data disks by specifying the
#  parameter -DataDiskOnly to be $false. The default is to copy only data disk vhds
#  and the new VM will boot from the original os disk. 
#######################################################################

$sourceOSDisk = $sourceVM.VM.OSVirtualHardDisk
$sourceDataDisks = $sourceVM.VM.DataVirtualHardDisks

# Get source storage account information, not considering the data disks and os disks are in different accounts
$sourceStorageAccountName = $sourceOSDisk.MediaLink.Host -split "\." | select -First 1
$sourceStorageAccount = Get-AzureStorageAccount –StorageAccountName $sourceStorageAccountName
$sourceStorageKey = (Get-AzureStorageKey -StorageAccountName $sourceStorageAccountName).Primary 

Select-AzureSubscription -SubscriptionId $DestSubscritpionId
# Create destination context
$destStorageAccount = Get-AzureStorageAccount | ? {$_.StorageAccountName -eq $DestStorageAccountName} | select -first 1
if ($destStorageAccount -eq $null)
{
    New-AzureStorageAccount -StorageAccountName $DestStorageAccountName -Location $DestLocationName
    $destStorageAccount = Get-AzureStorageAccount -StorageAccountName $DestStorageAccountName
}
$DestStorageAccountName = $destStorageAccount.StorageAccountName
$destStorageKey = (Get-AzureStorageKey -StorageAccountName $DestStorageAccountName).Primary

$sourceContext = New-AzureStorageContext  –StorageAccountName $sourceStorageAccountName -StorageAccountKey $sourceStorageKey -Environment AzureChinaCloud
$destContext = New-AzureStorageContext  –StorageAccountName $DestStorageAccountName -StorageAccountKey $destStorageKey

# Create a container of vhds if it doesn't exist
Set-AzureSubscription -CurrentStorageAccountName $DestStorageAccountName -SubscriptionId $DestSubscritpionId
#if ((Get-AzureStorageContainer -Context $destContext -Name vhds -ErrorAction SilentlyContinue) -eq $null)
if ((Get-AzureStorageContainer -Name $DestStorageContainerName -ErrorAction SilentlyContinue) -eq $null)
{
    Write-Host "Creating a container vhds in the destination storage account." -ForegroundColor Green
#    New-AzureStorageContainer -Context $destContext -Name vhds
    New-AzureStorageContainer -Name $DestStorageContainerName     
}

$allDisks = @($sourceOSDisk) + $sourceDataDisks
$destDataDisks = @()
# Copy all data disk vhds
# Start all async copy requests in parallel.
foreach($disk in $allDisks)
{
    $blobName = $disk.MediaLink.Segments[2]
    # copy all data disks 
    Write-Host "Starting copying data disk $($disk.DiskName) at $(get-date)." -ForegroundColor Green
    $sourceBlob = "https://" + $disk.MediaLink.Host + "/" + $SourceStorageContainerName + "/"
    $targetBlob = $destStorageAccount.Endpoints[0] + $DestStorageContainerName + "/"
    $azcopylog = "azcopy-" + $SourceCloudServiceName + "-" + $SourceVMName +".log"

    Write-Host "Start copy vhd to destination storage account"  -ForegroundColor Green
    #Write-Host .\azcopy\AzCopy\AzCopy.exe /Source:$sourceBlob /Dest:$targetBlob /SourceKey:$sourceStorageKey /DestKey:$destStorageKey /Pattern:$blobName /SyncCopy /v:$azcopylog -ForegroundColor Green

    #cd 'C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy'
    #AzCopy.exe /Source:$sourceBlob /Dest:$targetBlob /SourceKey:$sourceStorageKey /DestKey:$destStorageKey /Pattern:$blobName /SyncCopy /v:$azcopylog
     
    #cd D:\AzCopy
    #.\AzCopy.exe /Source:$sourceBlob /Dest:$targetBlob /SourceKey:$sourceStorageKey /DestKey:$destStorageKey /Pattern:$blobName /SyncCopy /v:$azcopylog 

    #Start-AzureStorageBlobCopy is too slow
    Start-AzureStorageBlobCopy -SrcContainer $SourceStorageContainerName -SrcBlob $blobName -DestContainer $DestStorageContainerName -DestBlob $blobName -Context $sourceContext -DestContext $destContext -Force

    if ($disk –eq $sourceOSDisk)
    {
        $destOSDisk = $targetBlob + $blobName
    }
    else
    {
        $destDataDisks += $targetBlob + $blobName
    }
}


# Wait until all vhd files are copied.
$CopyStatusReportInterval = 15
$diskComplete = @()
do
{
    Write-Host "`n[WORKITEM] - Waiting for all disk copy to complete. Checking status every $CopyStatusReportInterval seconds." -ForegroundColor Yellow
    # check status every 30 seconds
    Sleep -Seconds $CopyStatusReportInterval
    foreach ( $disk in $allDisks)
    {
        if ($diskComplete -contains $disk)
        {
            Continue
        }
        $blobName = $disk.MediaLink.Segments[2]
        $copyState = Get-AzureStorageBlobCopyState -Blob $blobName -Container vhds -Context $destContext
        if ($copyState.Status -eq "Success")
        {
            Write-Host "`n[Status] - Success for disk copy $($disk.DiskName) at $($copyState.CompletionTime)" -ForegroundColor Green
            $diskComplete += $disk
        }
        else
        {
            if ($copyState.TotalBytes -gt 0)
            {
                $percent = ($copyState.BytesCopied / $copyState.TotalBytes) * 100
                Write-Host "`n[Status] - $('{0:N2}' -f $percent)% Complete for disk copy $($disk.DiskName)" -ForegroundColor Green
            }
        }
    }
} 
while($diskComplete.Count -lt $allDisks.Count)



# Create OS and data disks 
Write-Host "Add VM OS Disk. OS "+ $sourceOSDisk.OS +"diskName:" + $sourceOSDisk.DiskName + "Medialink:"+ $destOSDisk  -ForegroundColor Green

# 设置源VM的Disk Name和目标VM的Disk Name
$disknameOS = $sourceOSDisk.DiskName
if($IsSameSub)
{
    #OSDisk, 如果在同一个订阅下,则增加后缀以区分VHD文件名
    $disknameOS = $sourceOSDisk.DiskName + $DiskNameSuffix
}

Add-AzureDisk -OS $sourceOSDisk.OS -DiskName $disknameOS -MediaLocation $destOSDisk
# Attached the copied data disks to the new VM
foreach($currenDataDisk in $destDataDisks)
{
    $diskName = ($sourceDataDisks | ? {$currenDataDisk.EndsWith($_.MediaLink.Segments[2])}).DiskName
    if($IsSameSub)
    {
        #DataDisk, 如果在同一个订阅下,则增加后缀以区分VHD文件名
        $diskName = ($sourceDataDisks | ? {$currenDataDisk.EndsWith($_.MediaLink.Segments[2])}).DiskName + $DiskNameSuffix
    }
    Write-Host "Add VM Data Disk $diskName" -ForegroundColor Green
    Add-AzureDisk -DiskName $diskName -MediaLocation $currenDataDisk
}

Write-Host "Import VM from " $vmConfigurationPath -ForegroundColor Green
Set-AzureSubscription -SubscriptionId $DestSubscritpionId -CurrentStorageAccountName $DestStorageAccountName


# Manually change the data diskname in the same subscription coz it can't be same
if($IsSameSub)
{
    $ContinueAnswer = Read-Host "`n`tPlease update the Diskname in the configuration file "+ $vmConfigurationPath +", just add your suffix $DiskNameSuffix to the filename! Then press ENTER to continue.."
}
# Import VM from previous exported configuration plus vnet info
if (( Get-AzureService | Where { $_.ServiceName -eq $DestCloudServiceName } ).Count -eq 0 )
{
    New-AzureService -ServiceName $DestCloudServiceName -Location $DestLocationName
}

Write-Host "`n import-AzureVM -Path $vmConfigurationPath | Set-AzureSubnet -SubnetNames $DestSubNet | New-AzureVM -ServiceName $DestCloudServiceName -VNetName $DestVNetName -WaitForBoot" -ForegroundColor Green

Import-AzureVM -Path $vmConfigurationPath | Set-AzureSubnet -SubnetNames $DestSubNet | New-AzureVM -ServiceName $DestCloudServiceName -VNetName $DestVNetName -WaitForBoot

 

posted on 2016-04-11 12:11 Lei Zhang的博客 阅读(...) 评论(...) 编辑 收藏