WindowsホストOSを定期再起動させているHyper-VゲストのWebサービスが「VM再起動」だけでは時々仮想ネットワークの疎通が取れないことがあったので「VM停止(電源OFF)→VM起動」も加えてみた

## 1. 発生していた課題

WindowsホストOSを定期アップデート目的で「タスクスケジューラ」にて「毎週水曜日3:00にホストOS再起動（shutdown -r -t 30）」を行っていました。

「Hyper-V」の「自動開始アクション」でも「サービスが停止したときに実行されていた場合は自動的に起動する」にしているので、大抵はホストOSのWindowsアップデート後の再起動でゲストのVMもWebサービスを再開できているのですが、  
時々仮想ネットワークとの疎通がとれていない事態が発生していたので、  
PowerShellで「curlによるHTTPレスポンスコードが000と404の時にVM再起動（Restart-VM -Name $vm.Name -Force）」を作成し「タスクスケジューラ」にて毎日6:00～7:00の15分間隔で実行するように設定し運用していました。

<details>
<summary><font font color="blue">修正前のPowerShellコード（クリックで表示）</font></summary>

```powershell

# テストでは利用するアカウントを Hyper-V Administrators にも所属させておくこと #

# 監視するURLを入力 #
 $URL = "<監視したいURL>"

# レスポンスコードを取得（ただしこの構文はHTTPのみ。HTTPSは --insecure処理必要） #
 $CODE = curl.exe -o NUL -w "%{http_code}" -s $URL
 write-host $CODE
 $DATE = Get-Date -Format "yyyy/MM/dd HH:mm:ss"
 write-output "$DATE [ $URL ] CODE: $CODE`r`n" |Out-File -NoNewline -Append .\WebAccessRepair.log

# コードが000または404である時は全てのVMを再起動させる #
 if ( $CODE -eq "000" -Or $CODE -eq "404" )
 {
    foreach($vm in Get-VM | Select Name)
	{
	# VM再起動 #
	write-host $vm.Name "VM再起動"
	Restart-VM -Name $vm.Name -Force
	Start-Sleep -s 5	# 5秒待機 #
	}

```

</details>

しかし、この対策でも、HTTPレスポンスコードが000のままWebサービスが再開できていない事象が発生してしまいました。。
![1_status000になってしまった...png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1667967/645059a7-2f6a-5f48-94dd-5637af411b55.png)
（「CODE: 000」のまま復旧しなかった。。）

## 2. 回避策の実施
サービス停止が発生してしまったHyper-Vゲストの状態は「停止」であったり、  
またはゲストOSは起動しているが、仮想ネットワークとは疎通がとれておらず手動再起動させないとならない状態であることも発生していたので、  
ホストOSの再起動のタイミングとHyper-Vサービス終了と再開が上手く合わずにこれら事象が発生していたのでしょうが、それは「VM再起動」だけでは解決できない様でした。

そこで、初回は「VM再起動」を試みるが、２回目もHTTPレスポンスコードが000または404の場合は「VM停止(電源OFF)→VM起動」を行うようにコードに追記しました。
```powershell
# テストでは利用するアカウントを Hyper-V Administrators にも所属させておくこと #

# 監視するURLを入力 #
$URL = "<監視したいURL>"

# レスポンスコードを取得（ただしこの構文はHTTPのみ。HTTPSは --insecure処理必要） #
$CODE = curl.exe -o NUL -w "%{http_code}" -s $URL
write-host $CODE
$DATE = Get-Date -Format "yyyy/MM/dd HH:mm:ss"
write-output "$DATE [ $URL ] CODE: $CODE`r`n" | Out-File -NoNewline -Append .\WebAccessRepair.log

# すべてのVMについてコードが000または404であるかを確認する #
foreach ($vm in Get-VM | Select Name, State) {
    if ($CODE -eq "000" -Or $CODE -eq "404") {
        # VM再起動記録の $vm.Name.logが存在するかを確認 #
        $logFile = "$($vm.Name).log"
        if ((Test-Path $logFile) -or ($vm.State -ne "Running")) {
         write-host "VM現在の状態：" $vm.Name $vm.State "but," $CODE "and," $logFile "--> 2nd phase, Force Stop & Start"
          # VM再起動記録の $vm.Name.logが存在する場合は、該当VMを強制電源OFF後に起動させる #
              write-host $vm.Name "VM停止(電源OFF)"
              Stop-VM -Name $vm.Name -TurnOff
              Start-Sleep -Seconds 5 # 5秒待機 #
              write-host $vm.Name "VM起動"
              Start-VM -Name $vm.Name
              Start-Sleep -Seconds 10 # 10秒待機 #
              # $vm.Name.logを削除する
              Remove-Item $logFile -Force
        } else {
          # VM再起動記録の $vm.Name.logが存在しない場合は、まずはVM再起動を試みる #
            if ($vm.State -eq "Running") {
             write-host "VM現在の状態：" $vm.Name $vm.State "but," $CODE "--> 1st phase, Reboot."
               write-host $vm.Name "VM再起動"
               Restart-VM -Name $vm.Name -Force
               Start-Sleep -Seconds 5 # 5秒待機 #
               # 再起動させた記録 $vm.Name.log を作成する
               write-output $vm.Name | Out-File -NoNewline $logFile
            }
        }
    }
}

# 全てのVMについて処理が終わったらプログラムを終了
```

## 3. 効果の確認
動作ログを見ていましたら、たまたま発生していた記録がありました。  
この発生日もWebサービスはうまく再開できていたようなので気が付きませんでしたが、  
対策は上手くできたと判断しました。
![2_status000から復帰できている.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1667967/5b1957d2-aca4-654b-75e1-c69c4091b482.png)
（「CODE: 000」が発生しましたが、再開できて「CODE: 200」が返ってきている）

以上です。