本記事について
Azure LocalをMS-01にインストールしたProxmox VE上の仮想マシンとして構築し、クラスターをデプロイする際のポイントを記載しています。
Proxmoxの環境に無理やりAzure Localを構築するための試行錯誤の情報ですので、純粋にAzure Localの検証がおこないたいという目的であればHyper-V上に構築するのがおススメです。
Azure Localのバージョンは24H2を利用しています。
ProxmoxのVM設定
VM設定のレベルでAzure Localを実行する際に考慮しておくべきものが3つあります。
- ハードディスクのSSDエミュレート
- ハードディスクのシリアル番号の重複排除
- cpuのフラグ設定
1. ハードディスクのSSDエミュレート
Azure LocalではディスクがSSDであることが必須なため、ハードディスクの設定でSSDエミュレーションを有効化しておきます。
2. ハードディスクのシリアル番号の重複排除
Azure Localのデプロイ中にクラスターの検証がおこなわれますが、この時にストレージスペースダイレクトのための検証でクラスターを構成するすべてのノードのディスクでシリアル番号が重複していないかのチェックがおこなわれます。
重複があるとデプロイが途中で失敗するため、事前にハードディスクのシリアル番号が重複しないようにしておきます。
ProxomoxのVM設定でハードディスクのシリアル番号を指定することもできると思われますが、
余分にディスクを付与してシリアル番号を確認した上で重複がないように各ノードで余剰なディスクを削りました。
ディスクのシリアル番号はGet-PhysicalDisk
コマンドで確認できます。
3. cpuのフラグ設定
Nestedの仮想化機能を利用するためにcpuのフラグ設定をおこないます。
Proxmox (QEMU)ではcpuの機能を制限したり、パフォーマンスの最適化のためにcpuフラグの設定ができます。
以下のドキュメント等に、ある程度まとまった解説があるため、これらを参考に設定しています。
Proxmox VE Administration Guide
archlinux | QEMU
QEMU | Hyper-V Enlightenments
ただ、私のMS-01の環境では上記の情報だけではVMでの仮想化機能(Hyper-VやWSL等)の利用に問題が出ていました。
Hyper-V等を有効化した場合にVMが起動しない or 起動ループに陥る事象が発生します。
同様の事例はProxomoxでの仮想化機能(Hyper-V、WSL)の利用で調べると何件か情報が出てきまして、
cpuフラグに-cpu host
を設定するなどの情報が出てくるのですが私の環境では解決しませんでした。
結論としては以下のサイトで提示されているlevel=30
のオプションを付けることで解決しています。
PROXMOX FORUMS | Windows Guest Boot Hangs after Enabling Hyper-V in Windows Guest on 13th Gen Intel CPU
GitLab qemu-project | Intel 12th Gen CPU not working with QEMU Hyper-V nested virtualization
これはcpuidのlevelを定義する設定のようですが、level=30が具体的にどういった機能の定義に該当するのかは調べきれていません。
なお、levelではなく具体的なcpu(例:SandyBridge等)で指定することも試してみましたが、こちらの方法では解決できませんでした。
以上を踏まえて、最終的には以下のcpuフラグの設定を投入しています。
Hyper-V Enlightenmentのフラグでベストプラクティスな設定かどうかは十分に吟味できていませんが、特に問題なく動作はしています。
設定はGUI上ではおこなえないため、VMの設定ファイルに以下の行を追加して設定します。
- /etc/pve/qemu-server/<VM ID>.conf
cpu host args: -cpu host,level=30,hv-relaxed,hv-vapic,hv-spinlocks=0x1fff,hv-vpindex,hv-runtime,hv-time,hv-synic,hv-stimer,hv-tlbflush,hv-ipi,hv-frequencies,hv-evmcs,hv-stimer-direct,hv-emsr-bitmap,hv-xmm-input,hv-tlbflush-ext,hv-tlbflush-direct
Azure Localのハードウェアの検証回避
Azure Localのデプロイ時には環境チェッカーが実行されます。
環境チェッカーではインターネットへの接続性やADの準備状況の確認などがチェックされるのですが、
チェック項目の1つにハードウェアコンポーネントのシステム要件のチェックがあります。
最近のAzure Localではメモリの要件にECCメモリであることが追加されるなど、おうち環境で検証するにはハードルになるチェック項目が出てきています。
また、ネットワークアダプターについてもNdisPhysicalMediumの値が"14"(Ethernet)であることのチェックがなされるのですが、このチェックがProxomoxでは問題になります。
Proxomox (virtio)ではネットワークアダプターの設定でNdisPhysicalMediumの値が"0"に設定されています。
- virtioのネットワークアダプター(NetKVM\2k25\amd64\netkvm.infの抜粋)
NdisPhysicalMediumの値の参考
[kvmnet6.ndi] Characteristics = 0x84 ; NCF_PHYSICAL | NCF_HAS_UI BusType = 5 ; PCI AddReg = kvmnet6.Reg, Parameters CopyFiles = kvmnet6.CopyFiles, netkvmp.CopyFiles *IfType = 6 *MediaType = 0 ; NdisMedium802_3 *PhysicalMediaType = 0 ; NdisPhysicalMediumUnspecified
そのため、ECCメモリとNdisPhysicalMediumの値チェックを回避する必要があるのですが、どちらも正攻法で突破する方法が見出せないため、環境チェッカーのスクリプトを書き換えることで回避しています。
環境チェッカーのスクリプトの書き換えはもちろんサポートされるものではありませんので、あくまで環境チェッカーの内容の理解のための参考情報として捉えていただければと思います。
該当の環境チェッカーのスクリプトは以下のファイルです。
このスクリプト内で以下の箇所を修正して、ノードを再起動することでデプロイ時の環境チェックをパスすることができています。
C:\Program Files\WindowsPowerShell\Modules\AzStackHci.EnvironmentChecker\AzStackHciHardware\AzStackHci.Hardware.Diagnostic.Helpers.psm1
-
ECCメモリ
function Test-MemoryProperties
function Test-MemoryProperties { <# .SYNOPSIS Test Memory .DESCRIPTION Test Memory #> [CmdletBinding()] param ( [System.Management.Automation.Runspaces.PSSession[]] $PsSession ) try { $sb = { $cimParams = @{ ClassName = 'Win32_PhysicalMemory' Property = '*' } $cimData = @(Get-CimInstance @cimParams) return (New-Object PsObject -Property @{ ComputerName = $ENV:ComputerName cimData = $cimData }) } $remoteOutput = if ($PsSession) { Invoke-Command -Session $PsSession -ScriptBlock $sb } else { Invoke-Command -ScriptBlock $sb } $cimTest = Test-CimData -Data $remoteOutput -ClassName PhysicalMemory -Severity WARNING $cimData = $remoteOutput.cimData # Add EEC property $eccSb = { if ($this.TotalWidth -gt $this.DataWidth) { return $true } else { - return $false + return $true } } $cimData | Add-Member -MemberType ScriptProperty -Name ECC -Value $eccSb -Force $PropertyResult = @() $PropertySyncResult = @() $matchProperty = @( - 'ConfiguredClockSpeed' + #'ConfiguredClockSpeed' - 'ConfiguredVoltage' + #'ConfiguredVoltage' - 'MaxVoltage' + #'MaxVoltage' 'MemoryType' - 'SMBIOSMemoryType' + #'SMBIOSMemoryType' - 'Speed' + #'Speed' - 'TotalWidth' + #'TotalWidth' - 'TypeDetail' + #'TypeDetail' ) $warningdesiredPropertyValue = @{ - DataWidth = @{ value = 64; hint = '64-bit' } # x64 + #DataWidth = @{ value = 64; hint = '64-bit' } # x64 FormFactor = @{ value = 8; hint = 'DIMM' } # DIMM } $criticaldesiredPropertyValue = @{ ECC = $true # Error Correction Code (ECC) memory } Log-CimData -cimData $cimData -Properties $warningdesiredPropertyValue,$criticaldesiredPropertyValue,$matchProperty # Check property sync for nodes individually $SystemNames = $cimData.CimSystemProperties.ServerName | Sort-Object | Get-Unique foreach ($systemName in $SystemNames) { $sData = $CimData | Where-Object { $_.CimSystemProperties.ServerName -eq $systemName } $instanceIdStr = 'Write-Output "Machine: $($Instance.CimSystemProperties.ServerName), Class: $ClassName, Instance: $($instance.DeviceLocator), Tag: $($instance.Tag)"' $PropertyResult += Test-DesiredProperty -CimData $sData -desiredPropertyValue $warningdesiredPropertyValue -InstanceIdStr $InstanceIdStr -ValidatorName Hardware -Severity Warning $PropertyResult += Test-DesiredProperty -CimData $sData -desiredPropertyValue $criticaldesiredPropertyValue -InstanceIdStr $InstanceIdStr -ValidatorName Hardware -Severity CRITICAL $PropertySyncResult += Test-PropertySync -CimData $sData -MatchProperty $matchProperty -ValidatorName Hardware -Severity Warning } # Check property sync for all nodes as well $PropertySyncResult += Test-PropertySync -CimData $cimData -MatchProperty $matchProperty -ValidatorName Hardware -Severity Warning return @($PropertyResult + $PropertySyncResult + $cimTest) } catch { throw $_ } }
-
ネットワークアダプターのNdisPhysicalMedium
function Test-NetAdapter
function Test-NetAdapter { <# .SYNOPSIS Test Network Adapter .DESCRIPTION Test Network Adapter #> [CmdletBinding()] param ( [System.Management.Automation.Runspaces.PSSession[]] $PsSession ) try { $sb = { - $cimData = @(Get-NetAdapter -Physical | Where-Object { $_.NdisMedium -eq 0 -and $_.Status -eq 'Up' -and $_.NdisPhysicalMedium -eq 14 -and $_.PnPDeviceID -notlike 'USB\*'}) + $cimData = @(Get-NetAdapter -Physical | Where-Object { $_.NdisMedium -eq 0 -and $_.Status -eq 'Up' -and $_.NdisPhysicalMedium -eq 0 -and $_.PnPDeviceID -notlike 'USB\*'}) return (New-Object PsObject -Property @{ ComputerName = $ENV:ComputerName cimData = $cimData }) } $remoteOutput = if ($PsSession) { Invoke-Command -Session $PsSession -ScriptBlock $sb } else { Invoke-Command -ScriptBlock $sb } $cimTest = Test-CimData -Data $remoteOutput -ClassName NetAdapter -Severity CRITICAL -Detail $lhwTxt.NICSupportExplanation $cimData = $remoteOutput.cimData $PropertyResult = @() $PropertySyncResult = @() $GroupResult = @() $CountResult = @() # Blocking properties $criticalMatchProperty = @( 'DriverDate' 'DriverDescription' 'DriverMajorNdisVersion' 'DriverMinorNdisVersion' 'DriverProvider' 'DriverVersionString' 'MajorDriverVersion' 'MinorDriverVersion' ) # non-block warning properties $warningMatchProperty = @( 'ActiveMaximumTransmissionUnit' 'ReceiveLinkSpeed' 'Speed' 'TransmitLinkSpeed' 'VlanID' 'MtuSize' ) $desiredPropertyValue = @{ AdminLocked = $false ConnectorPresent = $true EndpointInterface = $false ErrorDescription = $null FullDuplex = $true HardwareInterface = $true Hidden = $false IMFilter = $false InterfaceAdminStatus = @{ value = 1; hint = 'Up' } # Up InterfaceOperationalStatus = @{ value = 1; hint = 'Up' } # Up iSCSIInterface = $false LastErrorCode = $null MediaConnectState = @{ value = 1; hint = 'Connected' } # Connected MediaDuplexState = 2 NdisMedium = @{ value = 0; hint = '802.3' } # 802.3 - NdisPhysicalMedium = @{ value = 14; hint = '802.3' } # 802.3 + NdisPhysicalMedium = @{ value = 0; hint = '802.3' } # 802.3 OperationalStatusDownDefaultPortNotAuthenticated = $false OperationalStatusDownInterfacePaused = $false OperationalStatusDownLowPowerState = $false OperationalStatusDownMediaDisconnected = $false #PromiscuousMode = $false State = @{ value = 2; hint = 'Started' } # 802.3 # Started #Status = 'Up' Virtual = $false } $groupProperty = @( 'DriverDescription' ) Log-CimData -cimData $cimData -Properties $desiredPropertyValue,$warningMatchProperty,$criticalMatchProperty $minimum = 1 $instanceIdStr = 'Write-Output "Machine: $($instance.SystemName), ClassName: $ClassName, Instance: $($instance.Name), Description: $($instance.InterfaceDescription), Address: $($instance.PermanentAddress)"' # Check property sync for nodes individually $SystemNames = $cimData.CimSystemProperties.ServerName | Sort-Object | Get-Unique foreach ($systemName in $SystemNames) { $sData = $CimData | Where-Object { $_.CimSystemProperties.ServerName -eq $systemName } Log-Info -Message ($lhwTxt.NicCount -f $systemName, $sData.Count) # Make sure each system has the requisite number of Network Adapters $PropertyResult += Test-DesiredProperty -CimData $sData -desiredPropertyValue $desiredPropertyValue -InstanceIdStr $InstanceIdStr -ValidatorName Hardware -Severity Critical $CountResult += Test-Count -CimData $sData -minimum $minimum -ValidatorName 'Hardware' -Severity Critical } # Check property sync for all nodes as well $GroupResult += Test-GroupProperty -CimData $cimData -GroupProperty $groupProperty -MatchProperty $warningMatchProperty -ValidatorName Hardware -Severity Warning $GroupResult += Test-GroupProperty -CimData $cimData -GroupProperty $groupProperty -MatchProperty $criticalMatchProperty -ValidatorName Hardware -Severity Critical $InstanceCount += Test-InstanceCount -CimData $cimData -Severity Critical -ValidatorName 'Hardware' $InstanceCountByGroup += Test-InstanceCountByGroup -CimData $cimData -ValidatorName 'Hardware' -GroupProperty $groupProperty -Severity Critical # Finally, the all properties from the $matchProperty array have to be compared for all instances across all nodes. return @($PropertyResult + $GroupResult + $CountResult + $InstanceCountByGroup + $InstanceCount + $cimTest) } catch { throw $_ } }
まとめ
Hyper-V上で構築する上では大きな問題は無かったAzure Localですが、Proxomoxでの構築にはいくつかつまずきポイントがありました。
cpuidのところは調べきれていないところがあったり、環境チェッカーは強引な回避で突破していたりと不十分なところはありますが今後の参考のために記録しておきます。