Hyper-V backup di macchine virtuali

Per eseguire il backup di macchine virtuali in Hyper-V su Windows server 2008 R2 senza ricorrere a soluzioni a pagamento quali come ad esempio Microsoft System Center Data Protection Manager è possibile utilizzare due approcci.

Approccio 1: Utilizzo di Windows Backup

E’ possibile utilizzare Windows backup dalla parent partition per eseguire il backup delle VM in Hyper-V. Perchè Windows Backup possa eseguire il backup delle VM è necessario registrare il writer VSS HYPER-V Microsoft con Windows Server Backup impostando una chiave di registry come indicato in KB 958662: How to back up Hyper-V virtual machines from the parent partition on a Windows Server 2008-based computer by using Windows Server Backup.

L’utilizzo di questa metodologia di backup comporta le seguenti:

  • Le macchine virtuali che non hanno gli Integration Services installati rimarranno in stato salvato mentre viene creato lo snapshot VSS.
  • Le macchine virtuali che eseguono sistemi operativi che non supportano VSS, ad esempio Microsoft Windows 2000 o Windows XP, rimarranno in stato mentre viene creato lo snapshot VSS.
  • Le macchine virtuali che contengono i dischi dinamici devono essere sottoposti a backup in modalità non in linea.
  • Windows Server Backup non supporta il backup di macchine virtuali HYPER-V su volumi di cluster condiviso (volumi CSV).
  • Non è possibile ripristinare la singola VM con Windows Backup ma solo.
  • Le VM con due o più snapshot non verranno ripristinate (esiste comunque un workaround descritto nella KB 958662.

Approccio 2: Utilizzo di script Powershell

E’ possibile utilizzare la PowerShell management Library for Hyper-V gli script sviluppati da James O’Neill per realizzare uno script Powershell che arresti la VM, compatti i VHD, esporti la VM e avvii nuovamente la VM.

Di seguito riporto uno script che fa esattamente quanto descritto con la possibilità di mantenere un certo numero di backup e di gestire i log.

Per poter eseguire gli script PowerShell locali occorre impostare l’execution policy ad esempio su RemoteSigned con il seguente comando:

Set-ExecutionPolicy RemoteSigned

Di seguito lo script, nella sezione iniziale è possibile impostare le seguenti configurazioni:

  • Nome macchina virtuale
    ($vmName = “VMTest”)
  • Path backup
    ($bkpPath = “F:\HyperV-Export”)
  • Numero di backup da mantenere
    ($bkpCopiesRetained = 2)
  • Compressione VHD della VM prima dell’esportazione
    ($compressVHDs = $TRUE)
  • Timeout shutdown in secondi (vedi [Update 01])
    $timeoutShutdownVM = 300
  • Avvio della VM al termine del backup
    ($startVM = $TRUE)
  • Esecuzione dell’esportazione della VM
    ($exportVM = $TRUE)
  • Path dei file log
    ($logFilePath = $bkpPath)
  • Suffisso del nome dei file log
    ($logFileNameSuffix = “LogBackup”)
  • Numero di file di log da mantenere
    ($logFilesRetained = 3)
  • Path del modulo powershell di Hyper-V della PowerShell management Library for Hyper-V
    ($pathPSModuleHyperv = “C:\Scripts\PSHyperv\HyperV” )

Per avviare lo script è possibile utilizzare il comando:

%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe C:\Scripts\BackupVMTest.ps1

Sorgente script BackupVMTest.ps1:

 

# *** Impostazioni ***
$vmName = “VMTest”
$bkpPath = “F:\HyperV-Export”
$bkpCopiesRetained = 2
$compressVHDs = $TRUE
$timeoutShutdownVM = 300
$startVM = $FALSE
$exportVM = $TRUE
$logFilePath = $bkpPath
$logFileNameSuffix = “LogBackup”
$logFilesRetained = 3
$pathPSModuleHyperv = “C:\Scripts\PSHyperv\HyperV”
# ************************

# *** Import modulo Hyper-V
Import-Module $pathPSModuleHyperv

# *** Inizializzazioni ***
$now = Get-Date
$expPathVM = $bkpPath + “\” + $vmName
$bkpPathVM = $expPathVM + “-” + $now.ToString(“yyyy-MM-dd-HH-mm-ss”)
$logFileNameBase = $logFilePath + “\” + $logFileNameSuffix + “-” + $vmName
$logFile = $logFileNameBase + “-” + $now.ToString(“yyyy-MM-dd-HH-mm-ss”) + “.txt”
# ************************

# *** Creazione Backup Path
if (!(Test-Path $bkpPath)){
New-Item $bkpPath -type directory
}

# *** Creazione Log Path
if (!(Test-Path $logFilePath)){
New-Item $logFilePath -type directory
}

# *** Avvio backup
(Get-Date).ToString(“yyyy-MM-dd HH:mm:ss”) + ” Avvio Backup” | Out-File $logFile

# **** Shutdown virtual machines
if ((Test-VmHeartbeat $vmName -HeartBeatTimeOut 5).Status -eq “OK”){
(Get-Date).ToString(“yyyy-MM-dd HH:mm:ss”) + ” Arresto VM ” + $vmName | Out-File $logFile -append
Invoke-VMShutdown $vmName -force wait | Out-File $logFile -append
}
else{
(Get-Date).ToString(“yyyy-MM-dd HH:mm:ss”) + ” VM ” + $vmName + ” non attiva”| Out-File $logFile -append
}

 

# *** Compattamento VHD
if ($compressVHDs){
Get-VMDisk $vmName | foreach {
if ($_.DiskPath -ne $NULL) {
if ($_.DiskPath.EndsWith(“.iso”) -eq $FALSE){
(Get-Date).ToString(“yyyy-MM-dd HH:mm:ss”) + ” Compressione VHD ” + $_.DiskPath | Out-File $logFile -append
Compress-VHD $_.DiskPath -wait | Out-File $logFile -append
}
}
}
}

 

# *** Export virtual machines
if ($exportVM){
(Get-Date).ToString(“yyyy-MM-dd HH:mm:ss”) +  ” Esportazione VM ” + $vmName | Out-File $logFile -append
Export-vm $vmName $bkpPath -Wait –copystate | Out-File $logFile -append
}

# *** Avvio VM
if ($startVM){
(Get-Date).ToString(“yyyy-MM-dd HH:mm:ss”) +  ” Avvio VM ” + $vmName | Out-File $logFile -append
Start-Vm $vmName -wait -HeartBeatTimeOut 300 | Out-File $logFile -append
}

# *** Rename directory esportazione
if (Test-Path ($expPathVM)){
(Get-Date).ToString(“yyyy-MM-dd HH:mm:ss”) +  ” Rename directory esportazione” | Out-File $logFile -append
Move-Item $expPathVM $bkpPathVM | Out-File $logFile -append
}

# *** Eliminazione backup obsoleti
$bkpFolders = Get-ChildItem $bkpPath | Where {$_.psIsContainer -eq $true -and $_.FullName -like ($expPathVM + “*”)} | Sort $_.FullName

if ($bkpFolders.Count -gt $bkpCopiesRetained){
for ($i = 1; $i -le $bkpFolders.Count – $bkpCopiesRetained; $i++) {
(Get-Date).ToString(“yyyy-MM-dd HH:mm:ss”) + ” Eliminazione backup directory ” + $bkpFolders[$i-1].FullName | Out-File $logFile -append
Remove-Item $bkpFolders[$i-1].FullName -recurse | Out-File $logFile -append
}
}

 

# *** Eliminazione log file obsoleti
$logFiles = Get-ChildItem $logFilePath | Where {$_.psIsContainer -eq $false -and $_.FullName -like ($logFileNameBase + “*”)} | Sort $_.FullName

if ($logFiles.Count -gt $logFilesRetained){
for ($i = 1; $i -le $logFiles.Count – $logFilesRetained; $i++) {
(Get-Date).ToString(“yyyy-MM-dd HH:mm:ss”) + ” Eliminazione log file ” + $logFiles[$i-1].FullName | Out-File $logFile -append
Remove-Item $logFiles[$i-1].FullName | Out-File $logFile -append
}
}

# *** Termine backup
(Get-Date).ToString(“yyyy-MM-dd HH:mm:ss”) + ” Backup terminato” | Out-File $logFile -append

[Update 01]

Con la versione R2 SP1 della PowerShell management Library for Hyper-V il wait dello shutdown sembra non funzionare (si veda l’issue  Invoke-VMShutdown issue in R2 SP1 version) quindi è necessario modificare la seguente riga:

Invoke-VMShutdown $vmName -force -wait | Out-File $logFile –append

come segue:

Invoke-VMShutdown $vmName -Force -ShutdownTimeOut $timeoutShutdownVM | Out-File $logFile –append