在虚拟网络之间配置 VM 故障转移
重要
此版本的 Virtual Machine Manager (VMM) 已终止支持。 建议 升级到 VMM 2022。
本文介绍在不使用 Azure Site Recovery 服务管理灾难恢复时如何在 System Center - Virtual Machine Manager (VMM) 中处理虚拟网络之间的 VM 复制和故障转移。
- 我们建议使用 Azure Site Recovery 来复制 VM。 没有站点恢复时,VMM 不管理 Hyper-V 副本,需要使用 Hyper-V 副本 PowerShell cmdlet 来自动执行 Hyper-V 副本操作。
- 对于灾难恢复,我们建议使用单独的主虚拟网络和辅助虚拟网络。 主 VM 连接到主网络,而副本 VM 连接到辅助网络。 这样可确保两个 VM 能同时连接到网络。
- 如果有单个虚拟网络,请使用 Site Recovery 通过网络映射功能自动执行网络管理。 如果不使用 Site Recovery,则需要仔细检查先决条件,以及 VM 附加到网络的顺序。 尤其是,副本 VM 和主 VM 不得同时连接到一个虚拟网络。 否则,CA-PA 记录可能会在 VMM 中删除,并导致网络连接丢失。
示例解决方案
此示例解决方案介绍以下环境:
- 单个 VMM 服务器管理主站点和辅助站点。
- 主 VM 和副本 VM 托管在一个 Hyper-V 虚拟网络上。
- 你想要运行计划的故障转移,并在故障转移完成后保留 VM 的 IP 地址。
- VM 具有 IPv4 地址。
开始之前
确保虚拟交换机和逻辑交换机设置在 VMM 构造中有效且匹配。 如果不匹配,则网络附加操作可能无法在故障转移完成后成功。
主 VM 应连接到虚拟网络
副本 VM 不应连接到网络
只能将一个 IP 地址分配到主 VM 的每个网络适配器。 运行此命令以确保此操作。 如果在 VM 上有多个连接的网络适配器,则通过更改数组索引运行适配器。
$VMOnPD = Get-SCVirtualMachine -Name "VM Name" | where {$_.IsPrimaryVM -eq $true} Get-SCIPAddress –GrantToObjectId $VMOnPD.VirtualNetworkAdapters[0].ID``
确保操作系统分配给 VM 的 IP 地址与上面显示的 IP 地址相同。 登录到 VM 并运行 ipconfig 以检查。
检查主数据库和副本 (replica) 上是否正确设置了查阅表格。 为此,请在每台服务器上运行以下命令,并确保存在对应于上面返回的 IP 地址的条目:
Get-NetVirtualizationLookupRecord
检查 IP 地址为 IPv4,而非 IPv6
在运行脚本之前,请确保两个 VM 都已关闭。
确保两个 VM 上都启用了复制状态。
运行计划的故障转移脚本
以下是此脚本所执行的操作:
- 对于主 VM 上的每个网络适配器,它将存储 IP 地址、VM 网络和 IP 池。
- 撤销主 VM 和辅助 VM 上每个网络适配器的所有 IP 地址。
- 断开所有网络适配器的连接。
- 在主 VM 和辅助 VM 之间进行故障转移。
- (可选)启动反向复制。
- 为副本 VM 提供同一 IP 地址(针对每个网络适配器)。
- 将副本 VM 上的每个网络适配器都附加到步骤 1 中所存储的 VM 网络。
运行脚本
脚本采用两个参数:
- $VMName – 虚拟机的名称
- $ReverseRep – 布尔参数,用于指定是否应执行反向复制
- 如果传递了$true,则会立即启动反向复制,以后无法取消故障转移。
- 此脚本成功完成,且 $ReverseRep 为 $true 时:
- 主 VM 应处于准备计划的故障转移复制状态。
- 副本 VM 应处于故障转移完成复制状态。
- 如果传递了 $false,则不会执行反向复制。 ReverseRepORCancelFO.ps1 可用于执行反向复制或取消故障转移。
- 此脚本成功完成后,且 $ReverseRep 为 $true 时:
- 主 VM 应处于准备计划的故障转移复制状态。
- 副本 VM 应处于故障转移完成复制状态。
如果脚本没有完成任何步骤,则需手动完成失败的步骤,然后返回到 PowerShell 窗口。 脚本步骤包括主 VM 的故障转移、副本 (replica) VM 的故障转移,以及可选的反向复制。
运行以下脚本:
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
}
运行反向复制/取消脚本
以下是此脚本所执行的操作:
- 如果没有在故障转移脚本中运行反向复制,可以将此脚本用于反向复制,或取消故障转移。
- 如果取消,该脚本将反转网络步骤,并在断开副本 (replica) VM 网络后还原主 VM 连接。
运行脚本
此脚本应为 $ReverseRep 设置为 $false 的故障转移脚本运行。此脚本应使用三个参数:
- $VMName:VM 名称
- $ReverseRep:布尔参数,指定是否应执行反向复制。 $true 指示运行反向复制。
- $CancelFO - 布尔参数,指定是否取消故障转移。 $true 指示在主站点和恢复站点上的取消。
一次只能有 $ReverseRep 和 $CancelFO 中的一个传递 $true。 该脚本成功运行后,两个 VM 上的状态应为 Replication enabled’。
运行以下脚本:
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
}
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈