Hyper-V ワークグループクラスター構築

本稿では[検証編](https://qiita.com/koutaro_harada/items/0fdef6174b2bf7ef07e5)を受けての構築編をお届けいたします。
掲載しているスクリプトやコマンドの実行については自己責任でお願いします。

## システム構成
都合良く空いている機材がありませんでしたので、手元の Azure Stack HCI 上に Nested Hyper-V を有効化した仮想マシンで構築を行っています。

![4d4eda7110a53dcc13aa_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/e6126b50-0fa3-e81d-bb0e-d510516afd49.png)

- 3ノードのハイパーコンバージドインフラ構成
- ディスク：ノードあたり OS用×1 と 記憶域プール用×4
- NIC：ノードあたり サービスネットワーク用×2 (チーミング構成) と ストレージネットワーク用×2
- ストレージネットワークは直結3セグメント構成

![4d4eda7110a53dcc13aa_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/b157626f-e38c-9a08-ba1e-a0faff50e241.png)

- クォーラム用のストレージアカウントはプライベートエンドポイントを付けて VPN にて接続
- 名前解決は hosts にて実施

## 事前準備
- 作業ユーザーの準備
全ノードに <ins>同一ユーザー名＆同一パスワード＆管理者権限</ins> のローカルユーザーを作成します。
作業はすべてこのユーザーにて実施します。<br><br>

- クォーラムの準備
本稿ではクォーラムにクラウド監視を使用していますので、Azure ストレージアカウントを作成してアカウント名とアカウントキーを控えておきます。
[フェールオーバー クラスターのクラウド監視を展開する](https://learn.microsoft.com/ja-jp/windows-server/failover-clustering/deploy-cloud-witness?tabs=windows)<br><br>

- 名前解決の準備
DNSもしくはhostsにて <ins>ノード×3 および クラスター　および ↑の ストレージアカウントのエンドポイント</ins> の名前解決を行えるようにします。
```
# hostsの例
172.16.0.101 winhost01.wgcluster.local
172.16.0.101 winhost01
172.16.0.102 winhost02.wgcluster.local
172.16.0.102 winhost02
172.16.0.103 winhost03.wgcluster.local
172.16.0.103 winhost03
172.16.0.104 wincluster.wgcluster.local
172.17.0.1 winclustercloudwitness.blob.core.windows.net
```

## Step1：初期設定～リモートデスクトップできるまで (対象：全ノード)
Step1はOSインストール直後の状態でから始まります、まだリモートデスクトップができませんのでコンソール等からコマンドを投入します。
パラメーターはノード別に作成してください。

| パラメーター | 値 | 例 |
|-|-|-|
| \$ComputerName | コンピューター名 | WinHost01 |
| \$DnsSuffix | DNSサフィックス(ドメイン名) | wgcluster.local |
| \$DefaultGateway | デフォルトゲートウェイ | 172.16.0.254 |
| \$DNSServer | DNSサーバー | 172.16.0.1 |
| <ins>NIC1：サービス用</ins> |  |  |
| \$NIC1OldName | NIC1の変更前の名前 | イーサネット |
| \$NIC1Name | NIC1の変更後の名前 | Service1 |
| \$NIC1IPAddress | NIC1のIPアドレス | 172.16.0.101 |
| \$NIC1PrefixLength | NIC1のサブネットマスク | 24  |
| <ins>NIC2：サービス用</ins> ※チーミング用のためIPアドレスの割り当て不要 | | |
| \$NIC2OldName | NIC2の変更前の名前 | イーサネット 2 |
| \$NIC2Name | NIC2の変更後の名前 | Service2 |
| <ins>NIC3：ストレージ用</ins> | | |
| \$NIC3OldName | NIC3の変更前の名前 | イーサネット 3 |
| \$NIC3Name | NIC3の変更後の名前 | Storage1 |
| \$NIC3IPAddress | NIC3のIPアドレス | 192.168.0.1 |
| \$NIC3PrefixLength | NIC3のサブネットマスク | 30 |
| <ins>NIC4：ストレージ用</ins> | | |
| \$NIC4OldName | NIC4の変更前の名前 | イーサネット 4 |
| \$NIC4Name | NIC4の変更後の名前 | Storage3 |
| \$NIC4IPAddress | NIC4のIPアドレス | 192.168.0.10 |
| \$NIC4PrefixLength | NIC4のサブネットマスク | 30 |

コマンド：
```
# コンピューター名の変更
Rename-Computer -NewName $ComputerName

# プライマリ DNS サフィックスの設定
Set-ItemProperty registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters -Name "NV Domain" -Value \$DNSSuffix

# ネットワークの設定
Rename-NetAdapter -Name $NIC1OldName -NewName $NIC1Name
Get-NetAdapter -Name $NIC1Name | New-NetIPAddress -AddressFamily IPv4 -IPAddress $NIC1IPAddress -PrefixLength $NIC1PrefixLength -DefaultGateway $DefaultGateway
Set-DnsClientServerAddress -InterfaceAlias $NIC1Name -ServerAddress $DNSServer

Rename-NetAdapter -Name $NIC2OldName -NewName $NIC2Name

Rename-NetAdapter -Name $NIC3OldName -NewName $NIC3Name
Get-NetAdapter -Name $NIC3Name | New-NetIPAddress -AddressFamily IPv4 -IPAddress $NIC3IPAddress -PrefixLength $NIC3PrefixLength

Rename-NetAdapter -Name $NIC4OldName -NewName $NIC4Name
Get-NetAdapter -Name $NIC4Name | New-NetIPAddress -AddressFamily IPv4 -IPAddress $NIC4IPAddress -PrefixLength $NIC4PrefixLength

# Storage Network の Jumbo Packets の設定
Set-NetAdapterAdvancedProperty -Name $NIC3Name -RegistryKeyword "*JumboPacket" -RegistryValue 9014
Set-NetAdapterAdvancedProperty -Name $NIC4Name -RegistryKeyword "*JumboPacket" -RegistryValue 9014

# Storage Network の ファイアウォール設定
New-NetFirewallRule -DisplayName "Allow Inbound Storage" -Direction Inbound -LocalAddress $NIC3IPAddress,$NIC4IPAddress -Action Allow

# NTP の設定
W32tm /config /manualpeerlist:"$NTPServers" /syncfromflags:manual /update
Restart-Service w32time
W32tm /resync

# リモートデスクトップの有効化
Set-ItemProperty 'registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Terminal Server' -Name "fDenyTSConnections" -Value 0
Enable-NetFirewallRule -Group "@FirewallAPI.dll,-28752"

Restart-Computer
```

再起動が完了しましたら、リモートデスクトップおよびストレージネットワークの疎通確認を行ってください。

![4d4eda7110a53dcc13aa_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/301bbadc-5daa-d203-0b9b-91da9a6e7b67.png)

## Step2：各種設定～役割と機能の有効化まで (対象：全ノード)
ここから各ノードにリモートデスクトップして作業を進めます。

```
# WinRM の設定
Enable-WsManCredSSP -Role Client -DelegateComputer * -Force
Set-Item WSMan:\localhost\Client\TrustedHosts -Value * -Force

# 電源プランを高パフォーマンスに変更
powercfg /setactive "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c"

# 電源ボタンの設定を変更 (電源ボタンを押しても何もしない に設定)
powercfg /SETACVALUEINDEX 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c SUB_BUTTONS PBUTTONACTION 0

# 記憶域スペースの I/O タイムアウトの調整
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\spaceport\Parameters -Name HwTimeout -Value 0x00002710 -Verbose

# ページングファイルの設定
$blockCacheMB = 1024
$pageFilePath = "C:\pagefile.sys"
$initialSize = [Math]::Round(51200 + $blockCacheMB)
$maximumSize = [Math]::Round(51200 + $blockCacheMB)

$system = Get-WmiObject -Class Win32_ComputerSystem -EnableAllPrivileges
if ($system.AutomaticManagedPagefile){
    $system.AutomaticManagedPagefile = $false
    $system.Put()
}

$currentPageFile = Get-WmiObject -Class Win32_PageFileSetting
if ($currentPageFile.Name -eq $pageFilePath){
    $currentPageFile.InitialSize = $InitialSize
    $currentPageFile.MaximumSize = $MaximumSize
    $currentPageFile.Put()
}else{
    $currentPageFile.Delete()
    Set-WmiInstance -Class Win32_PageFileSetting -Arguments @{Name=$pageFilePath;InitialSize = $initialSize; MaximumSize = $maximumSize}
}

# 必要な機能のインストール と再起動
Install-WindowsFeature -Name "Failover-Clustering", "Hyper-V", "Data-Center-Bridging", "FS-FileServer" -IncludeManagementTools -Restart
```

Step1とStep2はまとめて実行すると再起動回数を減らせますので適宜カスタマイズしてください。

## Step3：Hyper-Vの設定 (対象：全ノード)
パラメーターの \$NICPrefix にはStep1 で設定した NIC1・NIC2 の名前との依存関係がありますので留意してください。

| パラメーター | 値 | 例 |
|-|-|-|
| \$SwitchName | Hyper-V 仮想スイッチの名前 | SET_Service |
| \$NICPrefix | チーミングするNIC名のプレフィックス | Service |

```
# Hyper-V インストール後に SET で仮想スイッチを作成
$NetAdapterNames = (Get-NetAdapter -Name "${NicPrefix}*").Name
New-VMSwitch -Name $SwitchName -NetAdapterName $NetAdapterNames -EnableEmbeddedTeaming $true -AllowManagementOS $true

# Hyper-V の拡張セッションモードを有効化
Set-VMHost -EnableEnhancedSessionMode $true
```

## Step4：フェールオーバークラスターの設定 (対象：任意のノード)
Step4 は任意のノード1台で実施してください。
途中目視確認の項目がありますので、コマンドを3つに分けて記載しています。

| パラメーター | 値 | 例 |
|-|-|-|
| $ClusterNode | ノードを配列で指定 | @("winhost01.wgcluster.local", "winhost02.wgcluster.local", "winhost03.wgcluster.local") |
| $ClusterName | クラスター名 | wincluster |
| $ClusterAddress | クラスターIPアドレス | 172.16.0.104/24 |
| $StorageAccountName | クラウド監視用ストレージアカウント名 | winclustercloudwitness |
| $StorageAccessKey | ストレージアカウントキー | ********** |
| $CN1OldName | クラスターネットワーク1の変更前の名前 | クラスター ネットワーク 1 |
| $CN1Name | クラスターネットワーク1の変更後の名前 | Service |
| $CN2OldName | クラスターネットワーク2の変更前の名前 | クラスター ネットワーク 2 |
| $CN2Name | クラスターネットワーク2の変更後の名前 | Storage1 |
| $CN3OldName | クラスターネットワーク3の変更前の名前 | クラスター ネットワーク 3 |
| $CN3Name | クラスターネットワーク3の変更後の名前 | Storage2  |
| $CN4OldName | クラスターネットワーク4の変更前の名前 | クラスター ネットワーク 4 |
| $CN4Name | クラスターネットワーク4の変更後の名前 | Storage3 |

```
# WSFC の構築
New-Cluster -Node $ClusterNode -Name $ClusterName -StaticAddress $ClusterAddress -AdministrativeAccessPoint Dns -NoStorage

# クラスタークォーラムの設定
Set-ClusterQuorum -CloudWitness -AccountName $StorageAccountName -AccessKey $StorageAccessKey

# ノード障害発生時の VM のフェールオーバー待機の調整
(Get-Cluster).ResiliencyDefaultPeriod=0
```

Get-ClusterNetworkInterface にてクラスターネットワークの名前を確認。

![4d4eda7110a53dcc13aa_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/c3243b0d-9404-317f-168a-07c4f5a1b04b.png)

```
# クラスターネットワーク名の設定 (パラメーターは Get-ClusterNetworkInterface の結果を見て修正)
(Get-ClusterNetwork -Name "${CN1OldName}").Name = $CN1Name
(Get-ClusterNetwork -Name "${CN2OldName}").Name = $CN2Name
(Get-ClusterNetwork -Name "${CN3OldName}").Name = $CN3Name
(Get-ClusterNetwork -Name "${CN4OldName}").Name = $CN4Name

# ライブマイグレーションネットワークの除外設定
$clusterResourceType = Get-ClusterResourceType -Name "Virtual Machine"
$serviceNetworkID = Get-ClusterNetwork -Name $CN1Name | Select -ExpandProperty ID
Set-ClusterParameter -InputObject $clusterResourceType -Name MigrationExcludeNetworks -Value $serviceNetworkID
```

## Step5：記憶域スペースダイレクトの設定 (対象：任意のノード)
Step5 も任意のノード1台で実施してください。

| パラメーター | 値 | 例 |
|-|-|-|
| $PoolName | ボリュームを作成する記憶域プール | ワイルドカードをつけて前方一致検索でヒットさせる |
| $VolumeName | ボリューム名 | Volume01 |
| $Size | ボリュームサイズ | 1TB |
| $Directory | 仮想マシンの保存ディレクトリ | Hyper-V |

```
# 記憶域スペースダイレクトの有効化
Enable-ClusterStorageSpacesDirect -Confirm:$false

# ボリュームの作成
New-Volume -StoragePoolFriendlyName $PoolName -FriendlyName $VolumeName -FileSystem CSVFS_ReFS -Size $Size

# 仮想マシン保存ディレクトリの作成
New-Item -Path "C:\ClusterStorage\${VolumeName}" -Name $Directory -ItemType "directory"
```

## Step6：その他の設定 (対象：全ノード)
最後にその他のこまごました設定を投入します。

| パラメーター | 値 | 例 |
|-----------------------|------------------------------|------------------------------------|
| \$VirtualMachinePath | 仮想マシンの既定の保存場所 | C:\ClusterStorage\Volume01\Hyper-V |
| \$VirtualHardDiskPath | 仮想ディスクの既定の保存場所 | C:\ClusterStorage\Volume01\Hyper-V |

```
# 仮想マシン作成時の既定の保存場所の変更
Get-ClusterSharedVolume | Move-ClusterSharedVolume -Node $env:COMPUTERNAME
Set-VMHost -VirtualMachinePath $VirtualMachinePath -VirtualHardDiskPath $VirtualHardDiskPath
```

## 出来上がり
これでひと通りの構築が完了しましたので出来上がりを確認してみましょう。
ワークグループクラスターの場合、同じユーザー/パスワードのローカルユーザーを作成→サインインを行った環境からリモートサーバー管理ツールにて管理することができます。

クラスターノード：
![4d4eda7110a53dcc13aa_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/21299ae5-f82e-1784-0d84-962556078934.png)

クラスター記憶域：
![4d4eda7110a53dcc13aa_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/b51b0bd3-3fc0-cce2-7909-d9d95e7856fc.png)

クラスターネットワーク：
![4d4eda7110a53dcc13aa_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/a626d52e-40a4-7c8f-b1d7-b4906ac95518.png)

Hyper-V：
![4d4eda7110a53dcc13aa_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/b36d7368-bb7e-52e6-796f-791a38fe8a45.png)

ライブマイグレーションはできない：
![4d4eda7110a53dcc13aa_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/0d274b70-5c6b-dfad-24d2-7385d9911ca8.png)

クイックマイグレーションは可能(WinHost01→WinHost03へ移動)：
![4d4eda7110a53dcc13aa_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/4db5552c-9f0c-eb70-9f02-9663650a66d8.png)

障害発生時のフェールオーバー(WinHost03停止でWinHost03→WinHost01へ移動)：
![4d4eda7110a53dcc13aa_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/826f8a97-9572-6111-495d-af8b3bf2cf7e.png)

## おわりに
Hyper-V ワークグループクラスター構築いかがでしたでしょうか？
今回は最低限の構成までですがコマンド化してしまうことでかなりサクサク進めることができました、Active Directoryがないのでドメイン参加などの手間が省けるとこにも工数削減効果を感じますね。
hostsだけで構築可能(インターネット向けの名前解決除く)なところも発見でした。