Configuración de la conmutación por error de máquinas virtuales entre redes virtuales

Importante

Esta versión de Virtual Machine Manager (VMM) ha llegado al final del soporte técnico. Se recomienda actualizar a VMM 2022.

En este artículo se describe cómo controlar la replicación y conmutación por error de máquinas virtuales en System Center - Virtual Machine Manager (VMM) entre redes virtuales cuando no usa el servicio Azure Site Recovery para administrar la recuperación ante desastres.

  • Se recomienda que utilice Azure Site Recovery para la replicación de máquinas virtuales. VMM no administra la Réplica de Hyper-V sin Site Recovery y debe usar los cmdlets de PowerShell para la Réplica de Hyper-V a fin de automatizar operaciones de Réplica de Hyper-V.
  • Para la recuperación ante desastres, se recomienda usar redes virtuales principales y secundarias independientes. Las máquinas virtuales principales se conectan a la red principal, y las máquinas virtuales de réplica, a la red secundaria. De esta forma, se garantiza que ambas máquinas virtuales puedan conectarse a una red al mismo tiempo.
  • Si tiene una sola red virtual, use Site Recovery para automatizar la administración de red mediante la característica de asignación de red. Si no utiliza Site Recovery, debe comprobar detenidamente los requisitos previos y el orden en que las máquinas virtuales se conectan a la red. En particular, la máquina virtual de réplica y la máquina virtual principal no deben estar conectadas a una única máquina virtual al mismo tiempo. De lo contrario, los registros CA-PA podrían eliminarse en VMM y provocar una pérdida de conectividad de red.

Solución de ejemplo

En esta solución de ejemplo se describe el siguiente entorno:

  • Un único servidor VMM administra sitios principales y secundarios.
  • Las máquinas virtuales principal y de réplica se hospedan en una única red virtual de Hyper-V.
  • Desea ejecutar una conmutación por error planeada y conservar la dirección IP de la máquina virtual después de la conmutación por error.
  • Las máquinas virtuales tienen direcciones IPv4.

Antes de empezar

  • Asegúrese de que el conmutador virtual y la configuración del conmutador lógico sean válidos y coincidan en el tejido de VMM. De lo contrario, las operaciones de conexión a la red pueden no resultar satisfactorias después de la conmutación por error.

  • La máquina virtual principal debe estar conectada a una red virtual.

  • La máquina virtual de réplica no debe estar conectada a una red.

  • Debe asignarse una única dirección IP a cada adaptador de red de la máquina virtual principal. Ejecute este comando para asegurarse de que así sea. Si hay más de un adaptador de red conectado en la máquina virtual, ejecute el comando para cada adaptador, pero cambie el índice de matriz.

    $VMOnPD = Get-SCVirtualMachine -Name "VM Name" | where {$_.IsPrimaryVM -eq $true}
    Get-SCIPAddress –GrantToObjectId $VMOnPD.VirtualNetworkAdapters[0].ID``
    
  • Asegúrese de que la dirección IP asignada a la máquina virtual por el sistema operativo es la misma que la dirección IP mostrada anteriormente. Inicie sesión en la máquina virtual y ejecute ipconfig para comprobarlo.

  • Compruebe que las tablas de búsqueda estén configuradas correctamente en la réplica y principal. Para ello, ejecute el siguiente comando en cada servidor y asegúrese de que haya una entrada que corresponda a la dirección IP devuelta anteriormente: Get-NetVirtualizationLookupRecord

  • Compruebe que la dirección IP es IPv4 y no IPv6.

  • Asegúrese de que ambas máquinas virtuales están desactivadas antes de ejecutar los scripts.

  • Asegúrese de que el estado de replicación está habilitado en ambas máquinas virtuales.

Ejecución del script de conmutación por error planeada

A continuación se explica el comportamiento de este script:

  1. Para cada adaptador de red de una máquina virtual principal, almacena la dirección IP, la red de máquina virtual y el grupo de direcciones IP.
  2. Revoca todas las direcciones IP para cada adaptador de red en las máquinas virtuales principales y secundarias.
  3. Desconecta todos los adaptadores de red.
  4. Conmuta por error las máquinas virtuales principales y secundarias.
  5. Opcionalmente, inicia la replicación inversa.
  6. Proporciona la misma dirección IP (para cada adaptador de red) a la máquina virtual de réplica.
  7. Asocia cada adaptador de red de la máquina virtual de réplica a las redes de máquina virtual almacenadas en el paso 1.

Ejecute el script.

El script admite dos argumentos:

  • $VMName: nombre de la máquina virtual
  • $ReverseRep: argumento booleano para especificar si se debe realizar o no la replicación inversa
    • Si se pasa $true, la replicación inversa se inicia inmediatamente y no se puede cancelar la conmutación por error más adelante.
    • Después de que este script se ha completado correctamente con $ReverseRep como $true:
      • La máquina virtual principal debe tener un estado de replicación Preparado para conmutación por error planeada.
      • La máquina virtual de réplica debe tener un estado de replicación Conmutación por error completada.
    • Si se acepta $false, no realizará la replicación inversa. ReverseRepORCancelFO.ps1 se puede usar para realizar la replicación inversa o cancelar la conmutación por error.
    • Después de que este script se ha completado correctamente con $ReverseRep como $false:
      • La máquina virtual principal debe tener un estado de replicación Preparado para conmutación por error planeada.
      • La máquina virtual de réplica debe tener un estado de replicación Conmutación por error completada.

Si el script no realiza ninguno de los pasos, debe completar manualmente los pasos con errores y después volver a la ventana de PowerShell. Los pasos del script incluyen la conmutación por error de la máquina virtual principal, la conmutación por error de la máquina virtual de réplica y, opcionalmente, la replicación inversa.

Ejecute el script:

 Param(
 [Parameter(Mandatory=$True)]
   [string]$VMName,
 [Parameter(Mandatory=$true)]
   [boolean]$ReverseRep
)

# the script running on system with SCVMM Console/PowerShell installed. Also, requires Hyper-V powershell module.``

Import-Module hyper-v

## Refresh VM configuration and initialize
Write-Host -ForegroundColor Green (Get-Date) ".....Refreshing the VMs..."
Get-SCVirtualMachine -Name $VMName | Read-SCVirtualMachine

$VMOnPD = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $true}
$VMOnDR = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $false}

if ($VMOnPD.StatusString -ne "Stopped")
{
    write-host -ForegroundColor Red (Get-Date) "....VM is not in stopped state. Actual State " $VMOnPD.StatusString
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$error.Clear()
$VMRepConfig = Get-VMReplication -ComputerName $VMOnPD.HostName -VMName $VMOnPD.Name
$VMRepConfig = Get-VMReplication -ComputerName $VMOnDR.HostName -VMName $VMOnPD.Name

if ($error -ne 0)
{
    $temp = $VMOnPD.HostName.Split(".")
    $primaryHostName = $temp[0]

    $temp = $VMOnDR.HostName.Split(".")
    $recoveryHostName = $temp[0]

    write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using FQDN, switching to Hostname"
    write-host -ForegroundColor Yellow (Get-Date) "....Primary Hostname: " $primaryHostName " Replica Hostname: " $recoveryHostName

    $error.Clear()
    $VMRepConfig = Get-VMReplication -ComputerName $primaryHostName -VMName $VMOnPD.Name
    $VMRepConfig = Get-VMReplication -ComputerName $recoveryHostName -VMName $VMOnPD.Name

    if ($error -ne 0)
    {
        write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using Hostname"
        write-host -ForegroundColor Red (Get-Date) "....Exiting"
        exit 1
    }

    write-host -ForegroundColor Green (Get-Date) "....Successful in getting VM Replication state using Hostname"
}
else
{
    $primaryHostName = $VMOnPD.HostName
    $recoveryHostName = $VMOnDR.HostName
}

$VMOnPDAdapter = Get-SCVirtualNetworkAdapter -VM $VMonPD
$VMOnDRAdapter = Get-SCVirtualNetworkAdapter -VM $VMonDR

$fileName = $VMName + (Get-Date).ToString() + ".txt"
$fileName = $fileName.Replace("/","_")
$fileName = $fileName.Replace(":","_")

Write-Host -ForegroundColor Yellow (Get-Date) "....Dumping network information for $VMName to file $fileName"
Write-Host -ForegroundColor Yellow (Get-Date) "....Number of Network adapters found: " $VMOnPDAdapter.count

$VMNetwork = @()
$VMSubnet = @()
$Pools = @()

$counter = 0
foreach($vmAdapter in $VMOnPDAdapter)
{
    if ($vmAdapter.VMNetwork -eq $null)
    {
        $VMNetwork = $VMNetwork + $null
        $VMSubnet = $VMSubnet + $null
        $Pools = $Pools + $null
        $counter = $counter + 1
        continue
    }

    $VMNetwork = $VMNetwork + (Get-SCVMNetwork -Name $vmAdapter.VMNetwork.Name -ID $vmAdapter.VMNetwork.ID)
    $VMSubnet = $VMSubnet + (Get-SCVMSubnet -Name $vmAdapter.VMSubnet.Name | where {$_.VMNetwork.ID -eq $vmAdapter.VMNetwork.ID})
    #$PortClassification = Get-SCPortClassification | where {$_.Name -eq "Guest Dynamic IP"}
    $Pools = $Pools + (Get-SCStaticIPAddressPool -IPv4 | where {$_.VMsubnet.name -eq $vmAdapter.VMSubnet.Name})

    Out-File -FilePath $fileName -InputObject $VMNetwork[$counter] -Append
    Out-File -FilePath $fileName -InputObject $VMSubnet[$counter] -Append
    Out-File -FilePath $fileName -InputObject $Pools[$counter] -Append

    $counter = $counter + 1
}

if ($error.Count -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) "....Error is gathering information for $VMName. No changes made"
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$IP = @()
$counter = 0
foreach($vmAdapter in $VMOnPDAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $IP = $IP + $null
        $counter = $counter + 1
        continue
    }

    ## Revoke IP
    $error.Clear()
    $IP = $IP +(Get-SCIPAddress –GrantToObjectId $VMOnPD.VirtualNetworkAdapters[$counter].ID)
    Write-Host -ForegroundColor Yellow (Get-Date) "....Revoking IP " $IP[$counter] "from Primary VM"
    Revoke-SCIPAddress $IP[$counter]
    if ($error.count -eq 0)
    {
        Write-Host -ForegroundColor Green (Get-Date) "....." $IP[$counter] "revoke completed"
    }

    ## Disconnect Primary VM
    Write-Host -ForegroundColor Yellow (Get-Date) "....Disconnecting Primary VM from Network " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnPD.VirtualNetworkAdapters[$counter] -NoLogicalNetwork -NoConnection -NoPortClassification
    Write-Host -ForegroundColor Green (Get-Date) "....Network Adapter '" $counter "' of Primary VM Disconnected"

    $counter = $counter + 1
}

## Start failover
Write-Host -ForegroundColor Yellow (Get-Date) ".....We are going to Failover " $VMName " from " $primaryHostName " to " $recoveryHostName

$error.Clear()
Start-VMFailover -ComputerName $primaryHostName -VMName $VMOnPD.Name -Prepare -Confirm:$false

start-sleep 5

Write-Host -ForegroundColor Yellow (Get-Date) ".....Completing Failover on Replica site..."
Start-VMFailover -ComputerName $recoveryHostName -VMName $VMOnDR.Name -Confirm:$false
if ($ReverseRep)
{
    write-host -ForegroundColor Green (Get-Date) ".....Starting Reverse Replication..."
    Set-VMReplication -ComputerName $recoveryHostName -reverse -VMName $VMOnDR.Name
}

if ($error -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) ".....Error occured during Planned Failover for VM $VMName"
    write-host -ForegroundColor Red (Get-Date) ".....Please manually complete Failover before continuing"
    Write-Host -ForegroundColor Red (Get-Date) ".....Press any key to continue..."
    $ignoreKey = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}

Write-Host -ForegroundColor Green (Get-Date) ".....Connecting Network(s) to Failed-over VM"

$counter = 0
foreach($vmAdapter in $VMOnPDAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $counter = $counter + 1
        continue
    }

    Write-Host -ForegroundColor Yellow (Get-Date) "Granting " $IP[$counter] "to Failed-over VM"
    Grant-SCIPAddress -GrantToObjectType "VirtualNetworkAdapter" -GrantToObjectID $VMOnDRAdapter[$counter].ID -StaticIPAddressPool $Pools[$counter] –IPAddress $IP[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Granting IP completed"

    Write-Host -ForegroundColor Yellow (Get-Date) "Connecting Replica VM to " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnDRAdapter[$counter] -IPv4AddressType static -VMNetwork $VMNetwork[$counter] -VMSubnet $VMSubnet[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Network Adapter '" $counter "' of Failed-over VM connected to " $VMNetwork[$counter]

    $counter = $counter + 1
}

Ejecución de la replicación inversa o cancelación del script

A continuación se explica el comportamiento de este script:

  1. Si no ejecuta la replicación inversa en el script de conmutación por error, puede utilizar este script para la replicación inversa o cancelar la conmutación por error.
  2. Si cancela, el script invierte los pasos de red y restaura las conexiones de máquina virtual principal después de desconectar las redes de máquina virtual de réplica.

Ejecute el script.

Este script debe ejecutarse para el script de conmutación por error con $ReverseRep establecido en $false. Este script adopta tres argumentos:

  • $VMName: nombre de la máquina virtual.
  • $ReverseRep: argumento booleano para especificar si se debe realizar la replicación inversa. $true indica que la replicación inversa se ejecuta.
  • $CancelFO: argumento booleano para especificar si se cancela la conmutación por error. $true indica la cancelación en los sitios principal y de recuperación.

Solo uno de los argumentos $ReverseRep y $CancelFO puede aceptar $true al mismo tiempo. Después de que el script se ejecuta correctamente, el estado de ambas máquinas virtuales debe ser Replicación habilitada.

Ejecute el script:

Param(
 [Parameter(Mandatory=$True)]
   [string]$VMName,
 [Parameter(Mandatory=$true)]
   [boolean]$ReverseRep,
 [Parameter(Mandatory=$true)]
   [boolean]$CancelFO
)

# the script running on system with SCVMM Console/PowerShell installed. Also, requires Hyper-V powershell module.

Import-Module hyper-v

if ($ReverseRep -eq $CancelFO)
{
    write-host -ForegroundColor Red (Get-Date) "....Please ensure that one and only one of the parameters -ReverseRep and -CancelFO is passed as $True"
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

## Refresh VM configuration and initialize
Write-Host -ForegroundColor Green (Get-Date) ".....Refreshing the VMs..."
Get-SCVirtualMachine -Name $VMName | Read-SCVirtualMachine

$VMOnPD = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $true}
$VMOnDR = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $false}

$error.Clear()
$VMRepConfig = Get-VMReplication -ComputerName $VMOnPD.HostName -VMName $VMOnPD.Name
$VMRepConfig = Get-VMReplication -ComputerName $VMOnDR.HostName -VMName $VMOnPD.Name

if ($error -ne 0)
{
    $temp = $VMOnPD.HostName.Split(".")
    $primaryHostName = $temp[0]

    $temp = $VMOnDR.HostName.Split(".")
    $recoveryHostName = $temp[0]

    write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using FQDN, switching to Hostname"
    write-host -ForegroundColor Yellow (Get-Date) "....Primary Hostname: " $primaryHostName " Replica Hostname: " $recoveryHostName

    $error.Clear()
    $VMRepConfig = Get-VMReplication -ComputerName $primaryHostName -VMName $VMOnPD.Name
    $VMRepConfig = Get-VMReplication -ComputerName $recoveryHostName -VMName $VMOnPD.Name

    if ($error -ne 0)
    {
        write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using Hostname"
        write-host -ForegroundColor Red (Get-Date) "....Exiting"
        exit 1
    }

    write-host -ForegroundColor Green (Get-Date) "....Successful in getting VM Replication state using Hostname"
}
else
{
    $primaryHostName = $VMOnPD.HostName
    $recoveryHostName = $VMOnDR.HostName
}

if ($VMOnDR.ReplicationStatus.ReplicationState -ne "Recovered")
{
    write-host -ForegroundColor Red (Get-Date) "....Replica VM is not in Failed over state. Actual State " $VMOnDR.ReplicationStatus.ReplicationState
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$error.Clear()

if ($ReverseRep -eq $true)
{
    write-host -ForegroundColor Green (Get-Date) ".....Starting Reverse Replication..."
    Set-VMReplication -ComputerName $recoveryHostName -reverse -VMName $VMOnDR.Name

    if ($error -ne 0)
    {
        write-host -ForegroundColor Red (Get-Date) ".....Error occured during Reverse Replication for VM $VMName"
        write-host -ForegroundColor Red (Get-Date) ".....Please manually complete Reverse replication"
        exit 1
    }

    write-host -ForegroundColor Green (Get-Date) ".....Reverse Replication completed..."
    exit 0
}

if ($VMOnDR.StatusString -ne "Stopped")
{
    write-host -ForegroundColor Red (Get-Date) "....VM is not in stopped state. Actual State " $VMOnDR.StatusString
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$VMOnPDAdapter = Get-SCVirtualNetworkAdapter -VM $VMonPD
$VMOnDRAdapter = Get-SCVirtualNetworkAdapter -VM $VMonDR

$fileName = $VMName + (Get-Date).ToString() + ".txt"
$fileName = $fileName.Replace("/","_")
$fileName = $fileName.Replace(":","_")

Write-Host -ForegroundColor Yellow (Get-Date) "....Dumping network information for $VMName to file $fileName"
Write-Host -ForegroundColor Yellow (Get-Date) "....Number of Network adapters found on Failed-over VM: " $VMOnDRAdapter.count

$VMNetwork = @()
$VMSubnet = @()
$Pools = @()

$counter = 0
foreach($vmAdapter in $VMOnDRAdapter)
{
    if ($vmAdapter.VMNetwork -eq $null)
    {
        $VMNetwork = $VMNetwork + $null
        $VMSubnet = $VMSubnet + $null
        $Pools = $Pools + $null
        $counter = $counter + 1
        continue
    }

    $VMNetwork = $VMNetwork + (Get-SCVMNetwork -Name $vmAdapter.VMNetwork.Name -ID $vmAdapter.VMNetwork.ID)
    $VMSubnet = $VMSubnet + (Get-SCVMSubnet -Name $vmAdapter.VMSubnet.Name | where {$_.VMNetwork.ID -eq $vmAdapter.VMNetwork.ID})
    #$PortClassification = Get-SCPortClassification | where {$_.Name -eq "Guest Dynamic IP"}
    $Pools = $Pools + (Get-SCStaticIPAddressPool -IPv4 | where {$_.VMsubnet.name -eq $vmAdapter.VMSubnet.Name})

    Out-File -FilePath $fileName -InputObject $VMNetwork[$counter] -Append
    Out-File -FilePath $fileName -InputObject $VMSubnet[$counter] -Append
    Out-File -FilePath $fileName -InputObject $Pools[$counter] -Append

    $counter = $counter + 1
}

if ($error.Count -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) "....Error is gathering information for $VMName. No changes made"
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$IP = @()
$counter = 0
foreach($vmAdapter in $VMOnDRAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $IP = $IP + $null
        $counter = $counter + 1
        continue
    }

    ## Revoke IP
    $error.Clear()
    $IP = $IP +(Get-SCIPAddress –GrantToObjectId $VMOnDR.VirtualNetworkAdapters[$counter].ID)
    Write-Host -ForegroundColor Yellow (Get-Date) "....Revoking IP " $IP[$counter] "from Replica VM"
    Revoke-SCIPAddress $IP[$counter]
    if ($error.count -eq 0)
    {
        Write-Host -ForegroundColor Green (Get-Date) "....." $IP[$counter] "revoke completed"
    }

    ## Disconnect Replica VM
    Write-Host -ForegroundColor Yellow (Get-Date) "....Disconnecting Replica VM from Network " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnDR.VirtualNetworkAdapters[$counter] -NoLogicalNetwork -NoConnection -NoPortClassification
    Write-Host -ForegroundColor Green (Get-Date) "....Network Adapter '" $counter "' of Replica VM Disconnected"

    $counter = $counter + 1
}

## Cancel failover
Write-Host -ForegroundColor Yellow (Get-Date) ".....We are going to Cancel Failover " $VMName " on both " $primaryHostName " and " $recoveryHostName

$error.Clear()
Stop-VMFailover -ComputerName $recoveryHostName -VMName $VMName
Start-Sleep -Seconds 10
Stop-VMFailover -ComputerName $primaryHostName -VMName $VMName

if ($error -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) ".....Error occured during Cancel Failover for VM $VMName"
    write-host -ForegroundColor Red (Get-Date) ".....Please manually Cancel Failover on both Primary and Recovery Server"
    Write-Host -ForegroundColor Red (Get-Date) ".....Press any key to continue..."
    $ignoreKey = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}

Write-Host -ForegroundColor Yellow (Get-Date) ".....Connecting Network(s) back to the Primary VM"

$counter = 0
foreach($vmAdapter in $VMOnDRAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $counter = $counter + 1
        continue
    }

    Write-Host -ForegroundColor Yellow (Get-Date) "Granting " $IP[$counter] "to Primary VM"
    Grant-SCIPAddress -GrantToObjectType "VirtualNetworkAdapter" -GrantToObjectID $VMOnPDAdapter[$counter].ID -StaticIPAddressPool $Pools[$counter] –IPAddress $IP[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Granting IP completed"

    Write-Host -ForegroundColor Yellow (Get-Date) "Connecting Primary VM to " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnPDAdapter[$counter] -IPv4AddressType static -VMNetwork $VMNetwork[$counter] -VMSubnet $VMSubnet[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Network Adapter '" $counter "' of Primary VM connected to " $VMNetwork[$counter]

    $counter = $counter + 1
}