動的VHDXをFullモードで実際に最適化するには、VMを停止し、対象VHDXをHyper-Vホストへ読み取り専用かつドライブ文字なしでマウントしてからOptimize-VHD -Mode Fullを実行します。非マウントのVHDXへFullを指定するとMicrosoftの仕様上Prezeroedへフォールバックするため、コマンド表示がFullでも約束したFull処理になりません。バックアップ、チェックポイントチェーン、空き容量、対象パスを確認し、finallyで必ずDismount-VHDするのが主手順です。
Optimize-VHDはVHDX内部の仮想最大容量を縮小するResize-VHDではありません。ホスト上のファイル割り当てを詰める処理であり、固定VHD/VHDXは対象外、最適化余地がなければ成功してもファイルサイズが減らないことがあります。
Fullを実行できる対象か読み取りで確認する
対象は動的または差分の仮想ディスクです。本記事の安全な主手順は、チェックポイントのない動的VHDXを想定します。Get-VHDのVhdTypeがDynamic、Pathが対象VMの現在のディスクと一致し、AttachedがFalse、ParentPathが空であることを確認します。AVHDXやParentPathを持つ差分チェーン、共有VHD Set、クラスタ所有ディスク、レプリカ、バックアップ処理中のディスクは、構成所有者と製品手順を確認するまで止めます。
$VmName = 'App01'
$VhdPath = 'D:\VMs\App01\Virtual Hard Disks\App01-OS.vhdx'
Get-VM -Name $VmName | Select-Object Name,State,Status
Get-VMHardDiskDrive -VMName $VmName |
Select-Object ControllerType,ControllerNumber,ControllerLocation,Path
Get-VHD -Path $VhdPath |
Select-Object Path,VhdType,VhdFormat,Attached,ParentPath,FileSize,Size
Get-Item -LiteralPath $VhdPath |
Select-Object FullName,Length,LastWriteTime
期待値はVMのStateがOff、VMHardDiskDriveのPathと指定パスが一致、VhdFormatがVHDX、VhdTypeがDynamic、Attached=False、ParentPathが空です。Running/Saved/Paused、パス不一致、固定ディスク、Attached=True、ParentPathや.avhdxがある、クラスタ所有を確認できない場合は失敗条件です。そこで作業を中止し、Hyper-V ManagerまたはFailover Cluster Managerで正しい所有状態とチェーンを確認します。
ゲスト側の空き領域を安全に準備する
ホストの圧縮前にゲストOSを起動し、アプリ所有者と不要領域を減らします。Windowsの設定にあるストレージ使用量、ディスククリーンアップ、アプリ自身のログ保持、正式なアンインストーラーを使い、削除候補の所有者と保持期間を確認します。データベース、メールキュー、更新キャッシュ、監査ログ、ユーザーフォルダーを一括削除する例は使いません。空き領域を増やしても、ブロックの状態により同じ量だけVHDXが縮むとは限りません。
- ゲストのボリューム空き容量とアプリ正常性を変更前後で記録する。
- OSやアプリのサポートされたクリーンアップ機能だけを使う。
- 削除前にバックアップ対象、保持期限、復元担当を確認する。
- 暗号化、重複除去、非NTFS、データベースなど特殊構成では製品固有手順を優先する。
- クリーンアップ後に一度ゲストを起動して業務確認し、その後正常シャットダウンする。
バックアップとチェックポイントの扱い
アプリ整合性を持つバックアップを取得し、ジョブID、時刻、保存先、復元試験状況を記録します。チェックポイントは短時間のゲスト内変更を戻す補助にはなりますが、別媒体のバックアップではありません。チェックポイントを作った場合はゲスト確認後にHyper-V Managerから削除してマージ完了を待ち、Get-VMSnapshotが空、VMHardDiskDriveが通常のVHDXを指し、ParentPathが空であることを再確認してからFull最適化へ進みます。AVHDXを手作業で移動、改名、削除しません。
Get-VMSnapshot -VMName $VmName
Get-VMHardDiskDrive -VMName $VmName | Select-Object Path
Get-VHD -Path $VhdPath |
Select-Object Path,ParentPath,Attached,VhdType,FileSize,Size
期待値は未処理チェックポイントがなく、バックアップから復元でき、VMが正常終了していることです。バックアップ失敗、マージ進行中、Saved状態、ストレージ空き不足、別ジョブがVHDXを使用中なら開始しません。ホスト側には処理と一時的な割り当て変動に耐える空き容量を確保します。
読み取り専用マウントでFullを実行する
管理者PowerShellで対象を一つに固定します。Mount-VHD -ReadOnly -NoDriveLetterにより、FullがNTFSの空き領域情報とゼロブロックを調べられる条件を作ります。try/finallyを使い、Optimize-VHDが失敗してもマウントを残しません。実行前後でGet-VHDのFileSizeと仮想容量Size、Get-ItemのLengthを取得します。
$VhdPath = 'D:\VMs\App01\Virtual Hard Disks\App01-OS.vhdx'
$beforeVhd = Get-VHD -Path $VhdPath
$beforeFile = Get-Item -LiteralPath $VhdPath
$mounted = $false
try {
Mount-VHD -Path $VhdPath -ReadOnly -NoDriveLetter -ErrorAction Stop
$mounted = $true
Optimize-VHD -Path $VhdPath -Mode Full -ErrorAction Stop
}
finally {
if ($mounted) {
Dismount-VHD -Path $VhdPath -ErrorAction Continue
}
}
$afterVhd = Get-VHD -Path $VhdPath
$afterFile = Get-Item -LiteralPath $VhdPath
[pscustomobject]@{
VirtualSizeGiB = [math]::Round($afterVhd.Size / 1GB, 2)
FileSizeBeforeGiB = [math]::Round($beforeFile.Length / 1GB, 2)
FileSizeAfterGiB = [math]::Round($afterFile.Length / 1GB, 2)
ReclaimedGiB = [math]::Round(($beforeFile.Length - $afterFile.Length) / 1GB, 2)
}
期待出力は例外なくfinallyまで進み、処理後のGet-VHDでAttached=False、VirtualSizeGiBが前後同一、FileSizeAfterGiBが同じか小さいことです。最適化余地がなければReclaimedGiB=0でもcmdletの失敗ではありません。Mount-VHDエラー、Optimize-VHD例外、処理後もAttached=True、仮想容量の変化、ファイル増大、ストレージ警告は失敗条件です。VHDXを接続したままVMを起動せず、Dismount-VHDとイベントログ、バックアップ状態を確認します。
Quick・Full・Prezeroedの選択基準
- Full:読み取り専用マウント時だけ本来のFullとして許可され、ゼロブロックを走査し未使用ブロックも回収する。時間を確保して最大限の圧縮を狙う本手順向け。
- Quick:読み取り専用マウントが必要で、未使用ブロックを回収するがゼロブロック走査はしない。停止時間を抑えたい場合の候補。
- Prezeroed:読み取り専用マウントを要求せず、Quick相当だがNTFSの空き領域を問い合わせられないため検出効率が低い。事前に空き領域をゼロ化したディスクや非NTFSで利用価値がある。
- 非マウントVHDXへFullを指定:Microsoft資料の例ではPrezeroedへフォールバックする。Fullを実行した証拠にはならない。
- 非マウントVHDXへQuickを指定:Pretrimmedへフォールバックする。モード名だけで実処理を判断しない。
Prezeroedを選ぶために、出所不明のゼロ埋めツールやゲスト全空き領域を埋め尽くすコマンドを無検証で実行しません。ログ、更新、データベースの動作領域を枯渇させる危険があります。通常はゲストのサポートされた整理、正常停止、読み取り専用マウント、Fullという順序で十分です。
起動試験とサイズ確認で完了を判定する
- Dismount-VHD後にGet-VHDでAttached=Falseとチェーン状態を再確認する。
- VMを起動し、OSブート、イベントログ、対象ボリューム、アプリサービス、ネットワーク、バックアップエージェントを確認する。
- 業務トランザクションを読み取り中心のテストから始め、アプリ所有者が正常と判定する。
- 正常シャットダウンと再起動を一度行い、再現性を確認する。
- FileSize、Size、回収量、停止時間、バックアップID、試験結果を変更記録へ残す。
FileSizeはホストで実際に占有するVHDXファイルの大きさ、Sizeはゲストから見える仮想ディスクの最大容量です。Full後もSizeは変えません。起動しない、ボリュームが見えない、ファイルシステムエラー、アプリ不整合、バックアップ再開失敗があればロールバックへ移ります。サイズが減ったことだけを成功判定にしません。
失敗時のロールバック
Optimize-VHD中のエラーでは、まずfinallyのDismount-VHDが完了したかを確認し、同じコマンドを連打しません。VMを停止したままHyper-V、System、ストレージのイベント、ホスト空き容量、VHDXチェーンを採取します。最適化後にVMまたはアプリが異常なら、そのVHDXへの書き込みを増やさず、変更前の検証済みバックアップから別場所へ復元して接続先を戻します。短期チェックポイントが残っている構成では、Hyper-Vの管理操作だけで適用または破棄を判断します。
ロールバックの成功条件は、復元したディスクでOSとアプリが起動し、変更前のデータ時点、ネットワーク、バックアップジョブが確認できることです。復元不能、チェーン不明、親子パス不一致、ストレージエラーがある場合はVMを起動せず、バックアップまたは仮想化基盤の担当へ引き継ぎます。
公式情報・参考資料

コメント