心跳丢失时重启 Hyper-V VM

心跳丢失时重启 Hyper-V VM

我有一台 Windows Server 2012R2 hyper-v 主机。它运行着许多虚拟机,包括一些 Linux 虚拟机。

我有一个 Linux VM,它偶尔会在内核的某个地方崩溃,但有点规律,导致 VM 完全没有响应。

我知道 hyper-v 有一个“心跳”功能,它成功地将虚拟机崩溃检测为心跳丢失。
然而,我不知道如何设置 hyper-v,以便它实际上任何有心跳信号的东西。显然在某一时刻,如果心跳丢失超过 1 分钟,则可以重置 VM,但它已被删除,或放在我无法找到的地方。

我只有这台服务器,因此执行集群或故障转移之类的操作实际上不可行(这是一个家庭实验室服务器)。


在任何人发表评论之前,是的,我正在尝试找出虚拟机崩溃的原因,但它的不规则性使事情变得困难。

答案1

powershell 是你的救星:

$VM = Get-VMIntegrationService -VMName test-vm -Name Heartbeat
if ($VM.PrimaryStatusDescription -ne "OK")
{
    write-host "VM Dead ? restarting ..."
    Stop-VM test-vm -Force -TurnOff
    Start-VM test-vm
}

只需将其添加到任务计划程序即可。

如果您的虚拟机不支持心跳,则修改它以 ping 虚拟机而不是检查 HB。

答案2

我需要一个解决方案来监测全部Hyper-V 虚拟机,并在 VM 锁定时自动执行硬重置。您可能已经知道。似乎您可以在拥有群集设置时使用 Hyper-V 执行此操作,但如果只有一台 Hyper-V 主机,则如果没有一些自定义脚本,这是不可能的。

我发现了一个不错的 VBS,它可以监控所有系统的心跳,甚至还有一个可配置重试次数和宽限期在里面最新版本. 原始脚本和描述可以在这里找到这里。将其安排在系统启动时运行是一个好主意(不要只将其放在启动文件夹中,因为这需要 Hyper-V 管理员在脚本开始运行之前登录系统)。

使用方法: - 将 VMHeartBeat.vbs 脚本复制到您选择的位置 - 双击脚本或打开命令提示符,更改为复制脚本的文件夹,然后运行 ​​cscript.exe VMHeartBeat.vbs

注意:如果您双击脚本 wscript.exe,则将使用您的默认脚本引擎或任何其他引擎来运行该脚本。当您使用 cscript.exe 时,您需要始终保持命令窗口打开。

此脚本将在与脚本相同的位置创建一个日志文件,并不断将成功/失败事件附加到其中。日志文件名为 VMHeartBeatEvents.log

就是这样。您已准备好检测 VM 心跳丢失,然后执行硬重置。

要测试脚本是否正常工作,您可以下载 错误修复工具然后将其复制到虚拟机。现在,只需在 Hyper-V 主机上运行 VMHearBeat.vbs 并在虚拟机内打开 notmyfault.exe,选择一个错误并单击“执行错误”。根据您选择的错误类型,虚拟机可能会出现蓝屏。此时,VMHearBeat.vbs 会检测到心跳丢失并对虚拟机执行硬重置。您可以检查前面提到的日志文件,看看硬重置是否成功。

为了保存脚本,我还在下面粘贴了最新版本的 .VBS 代码(带有重试次数和宽限期)。

更新:该脚本的最新版本实际上存在一些错误,因此下面的脚本已被修改,并且我确认它可以在 Server 2012R2 上运行。\v2需要添加到第 20 行,并且10需要更改为函数11的第 4 行VMHardReset

Option Explicit

'Define Event log constants here
Const EVENT_INFO = 4
Const EVENT_ERROR = 1
Const RETRY_COUNT = 3
Const CHECK_EVERY_X_SECS = 6

Dim objSummaryInfo, objWMIService, objVMService, intValArray, objVMitems
Dim InParam, OutParam, objItem, strQuery
Dim Job, objShell
Dim colArgs, strLog, dict, compName

Set colArgs = WScript.Arguments.Named

'Get Command Line arguments in to some variables
strLog = colArgs.Item("log")

Set objShell = Wscript.CreateObject("Wscript.Shell")
Set objWMIService = GetObject("winmgmts:\\" & "." & "\root\virtualization\v2")
Set objVMService = objWMIService.ExecQuery("SELECT * FROM Msvm_VirtualSystemManagementService").ItemIndex(0)
intValArray = Array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199)

Set dict = CreateObject("Scripting.Dictionary")

While (1)
    Wscript.echo(CStr(Now()) +": Testing Loop ")   
    objVMItems = objVMService.GetSummaryInformation(NULL,intValArray,objSummaryInfo)
    for each objItem in objSummaryInfo
        compName = objItem.ElementName
        if (not dict.Exists(compName)) then
            dict.add compName,0
        end if

        If (objItem.EnabledState=2 AND (objItem.HeartBeat = 6 OR objItem.HeartBeat = 13)) Then
            dict.Item(compName)=dict.Item(compName)+1

            WriteLog("    **"+compName+"** missed heartbeat check; count "+CStr(dict.Item(compName)))

            if (dict.Item(compName) >= RETRY_COUNT ) then       
                dict.Item(compName)= 0
                Wscript.echo(CStr(Now()) +": Now performing a hard reset on "+compName)
                VMHardReset(compName)
            end if
        Else
            dict.Item(compName)= 0
        End If
    Next

    Set objVMItems=Nothing
    Set objItem = Nothing
    Set objSummaryInfo = Nothing

    Wscript.Sleep CHECK_EVERY_X_SECS * 1000
Wend

Set objWMIService = Nothing
Set objVMService = Nothing
Set InValArray = Nothing

Function VMHardReset(vmElementName)
    Dim objvm, strLine
    Set objvm = GetComputerSystem(vmElementName)
    Set InParam = objvm.Methods_("RequestStateChange").InParameters.SpawnInstance_()
    InParam.RequestedState=11
    Set OutParam = objvm.ExecMethod_("RequestStateChange",InParam)
    If (TrackJob(OutParam)) Then
        strLine = "Virtual Machine: " & vmElementName & " has been successfully recovered from a hearbeat failure"
        'Write Success Event to text log
        WriteLog(strLine)
        Set strLine=Nothing
        'Write a Windows Event Log
        objShell.LogEvent EVENT_INFO, strLine
    Else
        strLine = "Virtual Machine: " & vmElementName & " could not be recovered from a hearbeat failure"
        'Write Failure Event to text log
        WriteLog(strLine)
        Set strLine=Nothing
        'Write a Windows Event Log
        objShell.LogEvent EVENT_ERROR, strLine

    End If
    Wscript.echo(CStr(Now()) +": "+strLine)
    Set InParam = nothing
    Set objvm = nothing
    Set vmElementName = Nothing
End Function


Function GetComputerSystem(vmElementName)
    strQuery = "select * from Msvm_ComputerSystem where ElementName = '" & vmElementName & "'"
    set GetComputerSystem = objWMIService.ExecQuery(strQuery).ItemIndex(0)
    'Set GetComputerSystem = Nothing
    Set strQuery = Nothing
End Function


Function TrackJob(OutParam)
    Dim Job
    If (OutParam.ReturnValue = 0) Then
        TrackJob=True
    ElseIf (OutParam.ReturnValue <> 4096) Then
        TrackJob=False
    Else
        Set Job = objWMIService.Get(OutParam.Job)
        While (Job.JobState = 3) or (Job.JobState = 4)
            Set Job = objWMIService.Get(OutParam.Job)
        Wend

        If (Job.JobState <> 7) Then
            TrackJob=False
        Else
            TrackJob = True
        End If
    End If
    Set OutParam = Nothing
    Set Job = Nothing
End Function


Sub WriteLog(line)
    Dim objFileStream, objFileSystem
    Set objFileSystem = CreateObject("Scripting.FileSystemObject")
    Set objFileStream = objFileSystem.OpenTextFile("VMHeartBeatEvents.log", 8, true, -1)
    objfileStream.WriteLine CStr(Now()) +": "+line
    objfileStream.Close
    Set objFileSystem = Nothing
End Sub

相关内容