减小Azure虚拟机操作系统磁盘的大小以便下载/导出

6
我想创建一个 Azure VM,其操作系统磁盘大小小于默认的 127GB。但我在 Azure 门户中未找到此选项,因此我试图缩小磁盘,但没有成功。
我知道可以使用碎片整理工具修剪(trim)并缩小卷(使用磁盘管理),但这不会改变硬盘的“物理”大小。也就是说,如果我将磁盘缩小到 40GB,则将只有 87GB 未分配,并且 blob 仍然报告 127GB。
我的目标是将 blob 缩小以匹配已分配空间,从而方便更小的 VM 映像下载/导出(例如 40 vs 127GB)。
非常感谢您提供任何帮助。
4个回答

4
我已经写了一篇博客文章详细阐述了这个答案。但是主要问题在于能否缩小默认为127GB的Azure VM的大小,以便实现更快的导出/下载。我所采用的方法是修剪硬盘,然后使用Disk2VHD创建运行VM的VHD文件。Disk2VHD将创建一个可扩展的磁盘,其大小仅与当前磁盘上的数据相同,而不是整个可用磁盘。在我的情况下,是40GB与127GB。如果将此VHD文件保存到附加的磁盘(即:blob存储),则可以通过HTTP轻松地被整个团队下载。因此,下载现在是40GB而不是127GB。欲知更多,请阅读我的详细博客文章:

http://www.kevinmcloutier.com/?p=263

原始链接已失效: https://web.archive.org/web/20161027213258/http://kevinmcloutier.com/post/4


4
在Azure中创建虚拟机时,会带有一些默认配置。目前不支持通过Azure门户从Azure VM缩小操作系统磁盘(托管或未托管)的大小(例如将128GB缩小为32GB),使用以下方法可以实现此目的,并降低磁盘成本。
步骤1. 打开虚拟机并进入磁盘管理。 enter image description here 步骤2. 打开PowerShell并执行以下命令。 enter image description here 成功执行后,您可以找到以下内容 enter image description here 步骤3. 现在从Azure门户取消分配VM enter image description here 步骤4. 现在转到磁盘的属性窗格并复制资源ID enter image description here 步骤5. 现在从本地系统执行以下PowerShell脚本。 必须更改$DiskID,$VMName,$AzSubscription的值。
    # Variables
$DiskID = ""# eg. "/subscriptions/203bdbf0-69bd-1a12-a894-a826cf0a34c8/resourcegroups/rg-server1-prod-1/providers/Microsoft.Compute/disks/Server1-Server1"
$VMName = "VM-Server1"
$DiskSizeGB = 32
$AzSubscription = "Prod Subscription"

# Script
# Provide your Azure admin credentials
Connect-AzAccount

#Provide the subscription Id of the subscription where snapshot is created
Select-AzSubscription -Subscription $AzSubscription

# VM to resize disk of
$VM = Get-AzVm | ? Name -eq $VMName

#Provide the name of your resource group where snapshot is created
$resourceGroupName = $VM.ResourceGroupName

# Get Disk from ID
$Disk = Get-AzDisk | ? Id -eq $DiskID

# Get VM/Disk generation from Disk
$HyperVGen = $Disk.HyperVGeneration

# Get Disk Name from Disk
$DiskName = $Disk.Name

# Get SAS URI for the Managed disk
$SAS = Grant-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $DiskName -Access 'Read' -DurationInSecond 600000;

#Provide the managed disk name
#$managedDiskName = "yourManagedDiskName" 

#Provide Shared Access Signature (SAS) expiry duration in seconds e.g. 3600.
#$sasExpiryDuration = "3600"

#Provide storage account name where you want to copy the snapshot - the script will create a new one temporarily
$storageAccountName = "shrink" + [system.guid]::NewGuid().tostring().replace('-','').substring(1,18)

#Name of the storage container where the downloaded snapshot will be stored
$storageContainerName = $storageAccountName

#Provide the key of the storage account where you want to copy snapshot. 
#$storageAccountKey = "yourStorageAccountKey"

#Provide the name of the VHD file to which snapshot will be copied.
$destinationVHDFileName = "$($VM.StorageProfile.OsDisk.Name).vhd"

#Generate the SAS for the managed disk
#$sas = Grant-AzureRmDiskAccess -ResourceGroupName $resourceGroupName -DiskName $managedDiskName -Access Read -DurationInSecond $sasExpiryDuration

#Create the context for the storage account which will be used to copy snapshot to the storage account 
$StorageAccount = New-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName -SkuName Standard_LRS -Location $VM.Location
$destinationContext = $StorageAccount.Context
$container = New-AzStorageContainer -Name $storageContainerName -Permission Off -Context $destinationContext

#Copy the snapshot to the storage account and wait for it to complete
Start-AzStorageBlobCopy -AbsoluteUri $SAS.AccessSAS -DestContainer $storageContainerName -DestBlob $destinationVHDFileName -DestContext $destinationContext
while(($state = Get-AzStorageBlobCopyState -Context $destinationContext -Blob $destinationVHDFileName -Container $storageContainerName).Status -ne "Success") { $state; Start-Sleep -Seconds 20 }
$state

# Revoke SAS token
Revoke-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $DiskName

# Emtpy disk to get footer from
$emptydiskforfootername = "$($VM.StorageProfile.OsDisk.Name)-empty.vhd"

# Empty disk URI
#$EmptyDiskURI = $container.CloudBlobContainer.Uri.AbsoluteUri + "/" + $emptydiskforfooter

$diskConfig = New-AzDiskConfig `
    -Location $VM.Location `
    -CreateOption Empty `
    -DiskSizeGB $DiskSizeGB `
    -HyperVGeneration $HyperVGen

$dataDisk = New-AzDisk `
    -ResourceGroupName $resourceGroupName `
    -DiskName $emptydiskforfootername `
    -Disk $diskConfig

$VM = Add-AzVMDataDisk `
    -VM $VM `
    -Name $emptydiskforfootername `
    -CreateOption Attach `
    -ManagedDiskId $dataDisk.Id `
    -Lun 63

Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM

$VM | Stop-AzVM -Force


# Get SAS token for the empty disk
$SAS = Grant-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername -Access 'Read' -DurationInSecond 600000;

# Copy the empty disk to blob storage
Start-AzStorageBlobCopy -AbsoluteUri $SAS.AccessSAS -DestContainer $storageContainerName -DestBlob $emptydiskforfootername -DestContext $destinationContext
while(($state = Get-AzStorageBlobCopyState -Context $destinationContext -Blob $emptydiskforfootername -Container $storageContainerName).Status -ne "Success") { $state; Start-Sleep -Seconds 20 }
$state

# Revoke SAS token
Revoke-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername

# Remove temp empty disk
Remove-AzVMDataDisk -VM $VM -DataDiskNames $emptydiskforfootername
Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM

# Delete temp disk
Remove-AzDisk -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername -Force;

# Get the blobs
$emptyDiskblob = Get-AzStorageBlob -Context $destinationContext -Container $storageContainerName -Blob $emptydiskforfootername
$osdisk = Get-AzStorageBlob -Context $destinationContext -Container $storageContainerName -Blob $destinationVHDFileName

$footer = New-Object -TypeName byte[] -ArgumentList 512
write-output "Get footer of empty disk"

$downloaded = $emptyDiskblob.ICloudBlob.DownloadRangeToByteArray($footer, 0, $emptyDiskblob.Length - 512, 512)

$osDisk.ICloudBlob.Resize($emptyDiskblob.Length)
$footerStream = New-Object -TypeName System.IO.MemoryStream -ArgumentList (,$footer)
write-output "Write footer of empty disk to OSDisk"
$osDisk.ICloudBlob.WritePages($footerStream, $emptyDiskblob.Length - 512)

Write-Output -InputObject "Removing empty disk blobs"
$emptyDiskblob | Remove-AzStorageBlob -Force


#Provide the name of the Managed Disk
$NewDiskName = "$DiskName" + "-new"

#Create the new disk with the same SKU as the current one
$accountType = $Disk.Sku.Name

# Get the new disk URI
$vhdUri = $osdisk.ICloudBlob.Uri.AbsoluteUri

# Specify the disk options
$diskConfig = New-AzDiskConfig -AccountType $accountType -Location $VM.location -DiskSizeGB $DiskSizeGB -SourceUri $vhdUri -CreateOption Import -StorageAccountId $StorageAccount.Id -HyperVGeneration $HyperVGen

#Create Managed disk
$NewManagedDisk = New-AzDisk -DiskName $NewDiskName -Disk $diskConfig -ResourceGroupName $resourceGroupName

$VM | Stop-AzVM -Force

# Set the VM configuration to point to the new disk  
Set-AzVMOSDisk -VM $VM -ManagedDiskId $NewManagedDisk.Id -Name $NewManagedDisk.Name

# Update the VM with the new OS disk
Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM

$VM | Start-AzVM

start-sleep 180
# Please check the VM is running before proceeding with the below tidy-up steps

# Delete old Managed Disk
Remove-AzDisk -ResourceGroupName $resourceGroupName -DiskName $DiskName -Force;

# Delete old blob storage
$osdisk | Remove-AzStorageBlob -Force

# Delete temp storage account
$StorageAccount | Remove-AzStorageAccount -Force

你能简要解释一下这个脚本的实际功能吗? - Rafs

1

感谢反馈,这确实是建议的路线,但需要上传40GB的数据。根据上传速度,这可能需要8个小时以上(约10mbps)。我们正在尝试在不上传数据的情况下完成此操作,因为我们的下载速度为120mbps+。我正在Azure上测试解决方案,一旦确定可行,将在此发布。 - user5015979
如果您在与最终存储位置相同的数据中心中创建VHD,则可以缩短上传时间。这可能不是可能的,但值得一试。 - theadriangreen

0
Sapnandu的解决方案有效。非常感谢!虽然我仍不确定是什么神奇的标准使其工作,但最终我有了一个运行着缩小磁盘的虚拟机。将其设置为Gen1肯定是其中之一。我尝试了许多类似的方法,它们都在启动时卡住了。
我的方法是:
从图库映像创建虚拟机。您将拥有一个128GB的操作系统磁盘。
根据您的需求调整虚拟机。在这里,您可以运行调整大小脚本。因此,您最终将获得约90GB的未分配空间。
经过几次失败的尝试后,我开始重视已配置的虚拟机,我真的不想再次重复配置。
对该磁盘进行快照,然后从快照制作磁盘。这些选项将由快照和磁盘向您呈现。
然后从磁盘创建新的虚拟机。检查是否启动。引导诊断显示得相当快。
停止新虚拟机后,我查找了Sapnandu帖子中所需的3个参数(subscriptionID、diskID、vmName),并在Azure Cloud Shell(标题中的图标)中执行了脚本(第5步)。

这需要一些时间,大约10分钟左右。 当脚本完成后,新的虚拟机将以较小的磁盘运行。

因此,有一个虚拟机设置,脚本可以完美地工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接