Déployer Storage Spaces Direct sur Windows Server

0
(0)

Dans cet article, je vous guide étape par étape pour déployer storage spaces direct (S2D_ « from scratch ». L’approche ici est orientée RoCE (RDMA over Converged Ethernet), ce qui implique une configuration spécifique du réseau via DCB/QoS. Si vous utilisez iWARP (fonctionnant en TCP), vous pourrez ignorer cette partie.

Vous avez des serveurs avec du stockage local et vous cherchez une solution haute disponibilité sans investir dans un SAN ? Storage Spaces Direct (S2D) est probablement la solution idéale. Cette technologie native de Windows Server permet de mutualiser le stockage local entre plusieurs hôtes Hyper-V pour former un cluster résilient, performant, et économique.

Prérequis matériels

  • Au moins deux serveurs identiques, par exemple des Dell PowerEdge R740xd
  • Une carte réseau dual port 10 Gb/s minimum (25 Gb/s recommandé)
  • Une carte HBA non-RAID, car seul le mode JBOD est supporté
  • Suivez ce lien pour savoir ce qu’en pense MS: HardWare Requirement

ℹ️ Dell propose des serveurs préconfigurés nommés « S2D Ready Nodes »
👉 Pour plus d’informations, consultez la documentation officielle de Microsoft : Storage Spaces Direct – Hardware

Prérequis logiciels

Édition de Windows

Il vous faut Windows Server Datacenter 2016 ou supérieur, de préférence en version Core pour réduire la surface d’attaque et la consommation de ressources.

Voici un script PowerShell qui permet de convertir une édition Standard Core en Datacenter Core :

PowerShell
# Convert to Datacenter Edition  
$Edition = Get-WindowsEdition -Online
$RelaseId = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name ReleaseID | Select-Object ReleaseID
If ($Edition.Edition -notlike "ServerDatacenter*") {
    If ($Edition.Edition -like "*Cor") {
         If ( $RelaseId.ReleaseId -ge 1809) {
            dism /online /Set-Edition:ServerDatacenterCor /ProductKey:WMDGN-G9PQG-XVVXX-R3X43-63DFG /AcceptEula /Quiet
         } Else {
            dism /online /Set-Edition:ServerDatacenterCor /ProductKey:CB7KF-BWN84-R7R2Y-793K2-8XDDG /AcceptEula /Quiet
         }
    }
    } Else {
     Return "Nothing to do" 
    }
}

🔍 Ce que fait ce script :

  • Vérifie si l’édition actuelle est déjà Datacenter
  • Si non, il utilise DISM pour basculer vers l’édition ServerDatacenterCor
  • Deux clés produit génériques sont utilisées selon la version de Windows (avant ou après 1809)

Ajout des fonctionnalités Windows

Installez les rôles nécessaires pour S2D et Hyper-V :

PowerShell
# Install Windows Features  
Install-WindowsFeature -Name 'File-Services'
Install-WindowsFeature -Name 'Data-Center-Bridging'
Install-WindowsFeature -Name 'FS-SMBBW'
Install-WindowsFeature -Name 'Failover-Clustering' -IncludeManagementTools
Install-WindowsFeature -Name 'Hyper-V' -IncludeManagementTools -Restart

🧠 FS-SMBBW (SMB Bandwidth Limit) n’est pas obligatoire, mais recommandé pour contrôler la bande passante dédiée à SMB.
📌 Si vous comptez utiliser la réplication de stockage, vous devrez ajouter d’autres rôles, comme Storage Replica.

Configuration réseau

Déploiement du Switch Embedded Teaming (SET)

Le Switch Embedded Teaming (SET) permet d’agréger plusieurs interfaces réseau physiques dans un vSwitch tout en autorisant le passage de trafic de gestion, de stockage et de VM via des vNICs logiques.

Voici un script PowerShell pour automatiser cette configuration :

PowerShell
Function Invoke-NetConfig {
 
    $All = [System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces().Where({ $_.OperationalStatus -eq 'Up' })
    Foreach ( $Item in $All ) {
        $Props = $Item.GetIPProperties()
        If ($null -ne $Props) {
            Try {  $GWMgmt = $Props.GatewayAddresses[0].Address.ToString()
             
                If ( -not [String]::IsNullOrEmpty($GWMgmt)) {
                    $UnicastAddresses = $Props.UnicastAddresses.Where({ $_.Address.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork })
                    $IPMgmt = $UnicastAddresses.Address.ToString()
                    $PLMgmt = $UnicastAddresses.PrefixLength
                    $DNSServer = $Props.DnsAddresses -join ","
                    $Speed = $Item.Speed
                    Break
                }
 
            } Catch { Write-Warning -Message "oupsie... trying next" }
        }
    }

    # Get vlan ID for management vNic
    [Int]$VlanID = (Get-NetAdapterAdvancedProperty -DisplayName 'VLAN ID' | Sort-Object Name | Select-Object -First 1).DisplayValue

    $SwitchName = 'SW-' + ($Speed/ 1000000000) + 'G'
    $AdapterGroup = Get-NetAdapter -Physical | Where-Object Speed -GE 10000000000 | Group-Object DriverDescription | Where-Object Count -EQ 2
    New-VMSwitch -Name $SwitchName -EnableEmbeddedTeaming $True -NetAdapterName $AdapterGroup.Group.Name -AllowManagementOS $True -MinimumBandwidthMode Weight
    Set-VMSwitchTeam -Name $SwitchName -LoadBalancingAlgorithm HyperVPort
 
    $MgmtNic = Get-VMNetworkAdapter $SwitchName -ErrorAction SilentlyContinue -ManagementOS
    If ( $null -eq $MgmtNic ) { # should never happens if -AllowManagementOS parameter is $True on New-VMSwitch execution
        # Create Management vNic
        Add-VMNetworkAdapter -Name Management -SwitchName $SwitchName -ManagementOs
    } Else {
        # Rename Management vNic
        Rename-VMNetworkAdapter -Name $SwitchName -NewName Management -ManagementOs
    }
 
    $CurrentIP = (Get-NetIPAddress -InterfaceAlias 'vEthernet (Management)' -AddressFamily IPv4 -ErrorAction SilentlyContinue).IPAddress
    If ($CurrentIP -ne $IPMgmt) {
        New-NetIPAddress -InterfaceAlias 'vEthernet (Management)' -IPAddress $IPMgmt -PrefixLength $PLMgmt -Type Unicast -DefaultGateway $GWMgmt
        Set-DnsClientServerAddress -InterfaceAlias 'vEthernet (Management)' -ServerAddresses $DNSServer
    }   
    
    If ($null -ne $VlanID) {
        Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName Management -Access -VlanId $VlanID
    }

    Restart-NetAdapter 'vEthernet (Management)'
}
 
Invoke-NetConfig

🔍 Ce que fait ce script :

  • Détecte l’interface réseau ayant une passerelle (souvent la carte de gestion)
  • Crée un VMSwitch avec SET en agrégeant 2 NICs 10+ Gb
  • Crée un vNIC de gestion avec le même IP que précédemment
  • Réapplique la configuration IP et VLAN

🛠️ Ce script peut varier selon vos drivers ou vos cartes (Mellanox, Broadcom). Il est à tester et adapter à votre environnement.

Configuration RoCE

Prononcez « Roky »

Configuration du réseau RoCE avancée

La configuration RoCE (RDMA over Converged Ethernet) nécessite une mise en œuvre rigoureuse de la QoS, des priorités réseau, et des optimisations de bas niveau spécifiques aux cartes compatibles, souvent Mellanox ou Broadcom. Voici un exemple complet de configuration réaliste pour un environnement de production.

PowerShell
$AdapterGroup = Get-NetAdapter -Physical | Where-Object Speed -GE 10000000000 | Sort-Object Name | Group-Object DriverDescription | Where-Object Count -EQ 2
 
$Primary1 = $AdapterGroup.Group[0].Name
$Primary2 = $AdapterGroup.Group[1].Name
 
Remove-NetQosTrafficClass
Remove-NetQosPolicy -Confirm:$False
 
# Configure QoS policies for SMB-Direct (RoCE), Cluster Heartbeat and Default (all other) traffic
New-NetQosPolicy -Name "Cluster" -Cluster -PriorityValue8021Action 7
New-NetQosPolicy -Name "SMB" -NetDirectPortMatchCondition 445 -PriorityValue8021Action 3
New-NetQosPolicy -Name "Default" -Default -PriorityValue8021Action 0
New-NetQosPolicy -Name "TCP" -IPProtocolMatchCondition TCP -PriorityValue8021Action 1
New-NetQosPolicy -Name "UDP" -IPProtocolMatchCondition UDP -PriorityValue8021Action 1
 
# Set minimum bandwidth - 50% for SMB-Direct
New-NetQosTrafficClass "SMB" -Priority 3 -BandwidthPercentage 50 -Algorithm ETS

# This traffic class ensures that there's enough bandwidth reserved for cluster heartbeats:
If ((Get-NetAdapter -Physical | Group-Object speed | ? Count -eq 2).Name -le 10000000000 ) {
    New-NetQosTrafficClass "Cluster" -Priority 7 -BandwidthPercentage 2 -Algorithm ETS
} Else {
    New-NetQosTrafficClass "Cluster" -Priority 7 -BandwidthPercentage 1 -Algorithm ETS
}
 
# Disable flow control for all other traffic
Disable-NetQosFlowControl -Priority 0,1,2,4,5,6,7
 
# Enable flow control for SMB-Direct (RoCE)
Enable-NetQosFlowControl -Priority 3
 
# Apply Quality of Service (QoS) policy to the target adapters
Enable-NetAdapterQos -Name $Primary1, $Primary2
 
# Block DCBX protocol between switches and nodes for all interfaces
Set-NetQosDcbxSetting -Willing $false -Confirm:$false
  
# Set Mellanox dcbxmode to host in charge; requires OOB Mellanox driver
Set-NetAdapterAdvancedProperty -Name $Primary1 -DisplayName 'Dcbxmode' -DisplayValue 'Host in charge'
Set-NetAdapterAdvancedProperty -Name $Primary2 -DisplayName 'Dcbxmode' -DisplayValue 'Host in charge'

# force la carte en mode RoCE (paramètre Mellanox)
Set-NetAdapterAdvancedProperty -Name $Primary1 -RegistryKeyword '*NetworkDirectTechnology' -RegistryValue 4
Set-NetAdapterAdvancedProperty -Name $Primary2 -RegistryKeyword '*NetworkDirectTechnology' -RegistryValue 4

#  SET Switch need the -IeeePriorityTag to add the "Cluster" PriorityTag of 7 on vNIC/MGMT
Set-VMNetworkAdapter -Name Management -ManagementOS -MinimumBandwidthWeight 10
Set-VMNetworkAdapter -ManagementOS -Name Management -IeeePriorityTag on

Voici la configuration RoCE selon les best practices DELL

💡 Ce bloc configure l’environnement RoCE de manière fiable pour un usage avec S2D. Il respecte les préconisations Microsoft et les exigences des cartes Mellanox pour un fonctionnement sans surprises.

ℹ️ Zappez la partie QOS si vous avez des cartes iWarp.

Configuration des Jumbo Frames

Pour garantir des performances optimales en RoCE, on active le jumbo frame (MTU à 9014 ou 9216 selon les constructeurs) sur les interfaces de stockage :

PowerShell
# Enable Jumbo
Set-NetAdapterAdvancedProperty -Name $Primary1 -RegistryKeyword '*JumboPacket' -RegistryValue 9014
Set-NetAdapterAdvancedProperty -Name $Primary2 -RegistryKeyword '*JumboPacket' -RegistryValue 9014

Cela réduit la surcharge CPU en diminuant le nombre de paquets envoyés.

Configuration des vNICs SMB sur vSwitch SET

Après avoir créé notre vSwitch avec SET et configuré les politiques QoS, nous ajoutons maintenant deux vNICs logiques (SMB01 et SMB02) attachés à ce vSwitch. Ces interfaces sont dédiées au trafic SMB Direct (RDMA) entre les nœuds du cluster S2D.

PowerShell
# n'oubliez pas de renseigner ces variables !!
$VlanSMB =
$IPSMB01 =
$IPSMB02 =
$PLSMB =

Ces variables définissent le VLAN, les adresses IP à affecter aux interfaces SMB, ainsi que le prefix length (masque réseau). À adapter selon ton plan d’adressage.

PowerShell
$SwitchName = Get-VMSwitch | select -ExpandProperty Name

Récupère dynamiquement le nom du vSwitch existant (SET) pour éviter de le coder en dur.

PowerShell
# Add Vnic Storage attached to vSwitch
Add-VMNetworkAdapter -Name SMB01 -SwitchName $SwitchName -ManagementOS
Add-VMNetworkAdapter -Name SMB02 -SwitchName $SwitchName -ManagementOS

Crée deux vNICs logiques (SMB01 et SMB02) sur le système hôte, attachés au vSwitch. Chacun servira pour un chemin SMB distinct (utile avec SMB Multichannel).

PowerShell
# Enable RDMA
Enable-NetAdapterRDMA -Name *SMB*

Active RDMA sur les deux vNICs nouvellement créés.

PowerShell
# Set Vlan of storage vNic
Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName SMB01 -Access -VlanId $VlanSMB
Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName SMB02 -Access -VlanId $VlanSMB

Affecte le VLAN de stockage (RoCE) aux vNICs. Il est important que celui-ci corresponde au tagging défini dans la QoS (priorité 3 en général pour SMB Direct).

PowerShell
# Restart net adapters to ensure all is effective
Restart-NetAdapter 'vEthernet (SMB01)'
Restart-NetAdapter 'vEthernet (SMB02)'

Start-Sleep 5

Redémarre les interfaces pour garantir l’application des modifications précédentes. La pause de 5 secondes laisse le temps au système de réinitialiser proprement les interfaces.

PowerShell
# Map each storage vNic on a dedicated physical port
if (-not (Get-NetLbfoTeamNic)) {
    Set-VMNetworkAdapterTeamMapping -ManagementOS -VMNetworkAdapterName SMB01 -SwitchName $SwitchName -PhysicalNetAdapterName $Primary1
    Set-VMNetworkAdapterTeamMapping -ManagementOS -VMNetworkAdapterName SMB02 -SwitchName $SwitchName -PhysicalNetAdapterName $Primary2
}

Si les NICs physiques ne sont pas en teaming (LBFO, ils ne devraient pas l’être), on fait le mapping manuel entre chaque vNIC et une carte physique spécifique du SET. Cela garantit que SMB01 et SMB02 ont chacun un chemin distinct — fondamental pour le multicanal.

PowerShell
# Configure DNS on each interface, but do not register Storage interfaces
Set-DnsClient -InterfaceAlias "vEthernet (SMB0?)" -RegisterThisConnectionsAddress $false

On désactive l’enregistrement DNS pour les interfaces de stockage. Le but est d’éviter que ces adresses n’apparaissent dans les enregistrements DNS classiques, ce qui pourrait provoquer des résolutions incohérentes ou du trafic non souhaité.

PowerShell
# Disable ipv6, because sometime DisabledComponents is forced to 0x20 by GPO :( - It's a non supported way to disable ipv6.
$Reg = Get-ItemProperty "hklm:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" -Name DisabledComponents
If ($Reg.DisabledComponents -ne 10 -and $Reg.DisabledComponents -ne 11 -and $Reg.DisabledComponents -ne 255) {
    Get-NetAdapter *SMB* | Disable-NetAdapterBinding -ComponentID ms_tcpip6
    Set-Net6to4configuration -state disabled
    Set-Netisatapconfiguration -state disabled
    Set-NetTeredoConfiguration -type disabled
}

Si IPv6 n’est pas désactivé proprement (cas typique dans les environnements GPO), cette section le désactive interface par interface. Évite les erreurs réseau subtiles liées à IPv6 résiduel.

PowerShell
# Set IP mellanox card (not needed if configured as IPv6 Local Link)
New-NetIPAddress -InterfaceAlias "vEthernet (SMB01)" -IPAddress $IPSMB01 -PrefixLength $PLSMB -Type Unicast
New-NetIPAddress -InterfaceAlias "vEthernet (SMB02)" -IPAddress $IPSMB02 -PrefixLength $PLSMB -Type Unicast

Affecte une IP statique à chaque vNIC SMB. À noter : si tu utilises IPv6 link-local entre les hôtes pour RoCE, ce bloc peut être omis.

PowerShell
# Enable Jumbo frame.
# The valid keyword values are 1514, 4088, 9014
Set-NetAdapterAdvancedProperty -Name 'vEthernet (SMB01)' -RegistryKeyword '*JumboPacket'  -RegistryValue 9014
Set-NetAdapterAdvancedProperty -Name 'vEthernet (SMB02)' -RegistryKeyword '*JumboPacket'  -RegistryValue 9014

Applique la taille de trame Jumbo. Recommandé (voire requis) pour RoCE afin d’améliorer la latence et la bande passante réelle.

PowerShell
Set-VMNetworkAdapter -ManagementOS -Name SMB01 -IeeePriorityTag on
Set-VMNetworkAdapter -ManagementOS -Name SMB02 -IeeePriorityTag on

Active le tag IEEE 802.1p pour que les priorités définies via QoS soient bien prises en compte au niveau des vNICs. Indispensable si tu veux que la priorité 3 SMB circule jusqu’au switch physique.

PowerShell
# n'oubliez pas de renseigner ces variables !!
$VlanSMB =
$IPSMB01 =
$IPSMB02 =
$PLSMB = 

$SwitchName = Get-VMSwitch | select -ExpandProperty Name

# Add Vnic Storage attached to vSwitch
Add-VMNetworkAdapter -Name SMB01 -SwitchName $SwitchName -ManagementOS
Add-VMNetworkAdapter -Name SMB02 -SwitchName $SwitchName -ManagementOS
 
# Enable RDMA
Enable-NetAdapterRDMA -Name *SMB*
  
# Set Vlan of storage vNic
Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName SMB01 -Access -VlanId $VlanSMB
Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName SMB02 -Access -VlanId $VlanSMB
  
# Restart net adapters to ensure all is effective
Restart-NetAdapter 'vEthernet (SMB01)'
Restart-NetAdapter 'vEthernet (SMB02)'
  
Start-Sleep 5
 
# Map each storage vNic on a dedicated physical port
if (-not (Get-NetLbfoTeamNic)) {
    Set-VMNetworkAdapterTeamMapping -ManagementOS -VMNetworkAdapterName SMB01 -SwitchName $SwitchName -PhysicalNetAdapterName $Primary1
    Set-VMNetworkAdapterTeamMapping -ManagementOS -VMNetworkAdapterName SMB02 -SwitchName $SwitchName -PhysicalNetAdapterName $Primary2
}

# Configure DNS on each interface, but do not register Storage interfaces
Set-DnsClient -InterfaceAlias "vEthernet (SMB0?)" -RegisterThisConnectionsAddress $false
 
# Disable ipv6, because sometime DisabledComponents is forced to 0x20 by GPO :( - It's a non supported way to disable ipv6.
$Reg = Get-ItemProperty "hklm:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" -Name DisabledComponents
If ($Reg.DisabledComponents -ne 10 -and $Reg.DisabledComponents -ne 11 -and $Reg.DisabledComponents -ne 255) {
    Get-NetAdapter *SMB* | Disable-NetAdapterBinding -ComponentID ms_tcpip6
    Set-Net6to4configuration -state disabled
    Set-Netisatapconfiguration -state disabled
    Set-NetTeredoConfiguration -type disabled
}
  
# Set IP mellanox card (not needed if configured as IPv6 Local Link)
New-NetIPAddress -InterfaceAlias "vEthernet (SMB01)" -IPAddress $IPSMB01 -PrefixLength $PLSMB -Type Unicast
New-NetIPAddress -InterfaceAlias "vEthernet (SMB02)" -IPAddress $IPSMB02 -PrefixLength $PLSMB -Type Unicast

# Enable Jumbo frame.
# The valid keyword values are 1514, 4088, 9014
if (Get-NetLbfoTeamNic) {
    Set-NetAdapterAdvancedProperty -Name 'vEthernet (SMB01)' -RegistryKeyword '*JumboPacket'  -RegistryValue 9014
    Set-NetAdapterAdvancedProperty -Name 'vEthernet (SMB02)' -RegistryKeyword '*JumboPacket'  -RegistryValue 9014
}

Set-VMNetworkAdapter -ManagementOS -Name SMB01 -IeeePriorityTag on
Set-VMNetworkAdapter -ManagementOS -Name SMB02 -IeeePriorityTag on

ℹ️ Si vous avez le choix, optez toujours pour une désactivation de l’Ipv6 via la clé DisabledComponents. La seule méthode supportée par MS.

ℹ️ Chaque vNic devrait être sur un VLAN séparé. Ici je ne sépare que la management des deux autres.

Optimisation RSS

Ce code n’est adapté que dans le cas où vous utilisez une seule carte dual port.

C’est une partie alambiquée qui nécessite une certaine expérience pour comprendre les notions de NUMA et de RSS.

Mieux vaut ne rien faire que de faire des bêtises.

Lisez attentivement la page Windows Server 2016 Networking – Part 3- Optimizing Network settings avant de tenter cette config.

⚠️ Le code qui suit n’a jamais été testé en production et n’est là que pour la démo.

PowerShell
# 1. Détection CPU et sockets
$CPU = Get-CimInstance -ClassName Win32_Processor
$TotalSockets = $CPU.Count
$LogicalPerSocket = $CPU[0].NumberOfLogicalProcessors
$TotalLogical = $LogicalPerSocket * $TotalSockets

# 2. Réserver Core 0 pour le système (global)
$GlobalReserved = @(0)

# 3. Réserver un core NUMA-local pour la vNIC Management
$MgmtVnic = "vEthernet (Management)"
$MgmtNuma = (Get-NetAdapterHardwareInfo -Name $MgmtVnic).NumaNode

# Cœurs logiques associés à NUMA de Management
$MgmtCores = (Get-NetAdapterRSS -Name $MgmtVnic).NumaNodeProcessorMask |
    ForEach-Object { $_.ProcessorNumber } |
    Sort-Object

# Réserver le premier cœur dispo pour Management
$MgmtReserved = $MgmtCores | Where-Object { $_ -ne 0 } | Select-Object -First 1

# Fusion des cœurs réservés
$ReservedProcessors = $GlobalReserved + $MgmtReserved

# 4. Traitement des cartes physiques (dual-port)
$PrimaryNICs = Get-NetAdapterHardwareInfo -Name $Primary1, $Primary2

foreach ($nic in $PrimaryNICs) {
    $name = $nic.Name
    $numa = $nic.NumaNode

    # Cœurs logiques NUMA-local à cette carte
    $cores = (Get-NetAdapterRSS -Name $name).NumaNodeProcessorMask |
        ForEach-Object { $_.ProcessorNumber } |
        Where-Object { $_ -notin $ReservedProcessors } |
        Sort-Object

    # Répartition sur 8 cœurs max (modifiable)
    $Base = $cores[0]
    $Count = [math]::Min(8, $cores.Count)
    $Max = $cores[$Count - 1]

    # Appliquer VMQ
    Set-NetAdapterVmq -Name $name -BaseProcessorNumber $Base -MaxProcessors $Count -NumaNode $numa

    # Appliquer RSS aussi, au cas où (utile si non Hyper-V ou pour perf debug)
    Set-NetAdapterRSS -Name $name -BaseProcessorNumber $Base
}

# 5. Configurer la vNIC Management explicitement
Set-NetAdapterRSS -Name $MgmtVnic -BaseProcessorNumber $MgmtReserved

# 6. Redémarrage pour prise en compte
Restart-NetAdapter 'vEthernet (SMB01)'
Restart-NetAdapter 'vEthernet (SMB02)'
Restart-NetAdapter 'vEthernet (Management)'

Configuration de la migration dynamique

PowerShell
Set-VMHost -MaximumVirtualMachineMigrations 2

Mieux vaut 2 machines qui bougent vite, que 10 qui se trainent.

💡 On sera amené à rediscuter le point Live Migration [Dans le chapitre Cluster]({{ « /docs/25-Live-Migration/ » | relative_url }})

PowerShell
If ((Get-NetAdapter -Physical | Group-Object speed | ? Count -eq 2).Name -le 10000000000 ) {
    Set-VMHost –VirtualMachineMigrationPerformanceOption Compression
} Else {
    Set-VMHost –VirtualMachineMigrationPerformanceOption SMB
}

ℹ️ En dessous de 10Gbs, MS suggère la compression pour le live migration.

Limiter la bande passante du live migration

Afin d’éviter une congestion réseau lors d’une migration dynamique, je préconise une limitation à deux VMs en mouvement simultané d’une part, mais également de limiter la bande passante utilisée par ce service.

Ceci garantit un déplacement invisible de la VM, en particulier si un Storage Job est en cours (après un reboot par exemple).

Imaginez le scenario où le cluster est en cours de patching… La reconstruction du CSV et le déplacement des VMs sur leur nœud d’origine peuvent s’effectuer parallèlement ; Selon la quantité de RAM alloué à chaque VM, c’est une vrai tempête qui déferle sur les cartes réseaux.

PowerShell
Set-SmbBandwidthLimit -Category LiveMigration -BytesPerSecond 750MB

Interdir le réseau de management au live migration

Le trafic du live migration n’a pas vocation à transiter par la carte de management, en particulier si vous avez réduit ses possibilités RSS.

PowerShell
Get-ClusterResourceType -Cluster $CNO -Name "Virtual Machine" | Set-ClusterParameter -Cluster $CNO -Name MigrationExcludeNetworks -Value ([String]::Join(";",(Get-ClusterNetwork -Cluster $CNO | Where-Object {$_.Name -eq "Management"}).ID))

Ce bout de code permet de selectionner tous les reseaux possible sauf le Management.

Pour que ce code fonctionne tel quel, il faut avoir renommé les réseaux avant.

Emplacement par defaut des VM

Cette commande permet de modifier l’emplacement par defaut lors de la création de machines virtuelles.

PowerShell
Set-VMHost -VirtualMachinePath 'C:\ClusterStorage\Volume1'

C’est purement cosmétique quand on y pense… 🤔

Mise en place de Storage Spaces Direct

Préparation des disques

❗Ce code est destructif❗

Ce que dis Intel: The drives intended to be used for S2D must be empty and without partitions or other data, or they will not be included in the S2D system. Prepare the drives

PowerShell
# Execute on each node the code below
Update-StorageProviderCache
Get-StoragePool | ? IsPrimordial -eq $False | Set-StoragePool -IsReadOnly $False -ErrorAction SilentlyContinue
Get-StoragePool | ? IsPrimordial -eq $False | Get-VirtualDisk | Remove-VirtualDisk -Confirm:$False -ErrorAction SilentlyContinue
Get-StoragePool | ? IsPrimordial -eq $False | Get-VirtualDisk | Remove-StoragePool -Confirm:$False -ErrorAction SilentlyContinue
Get-PhysicalDisk | Reset-PhysicalDisk -ErrorAction SilentlyContinue
Get-Disk| ? Number -ne $null| ? IsBoot -ne $true |? IsSystem -ne $true |? PartitionStyle -ne RAW | % {
    $_ | Set-Disk -IsOffline $False
    $_ | Set-Disk -IsReadOnly $False
    $_ | Clear-Disk -RemoveData -RemoveOEM -Confirm:$false
    $_ | Set-Disk -IsReadOnly $true
    $_ | Set-Disk -IsOffline $true
}

Bref. C’est une RAZ des disques. Ne le faite qu’une fois à la creation du cluster

Vérification des disques

PowerShell
$nodes  = 'nodea', 'nodeb', 'nodec', 'noded'
icm $nodes {
    Get-Disk | ? Number -ne $null | ? IsBoot -ne $true | ? IsSystem -ne $true | ? PartitionStyle -eq RAW | Group -NoElement -Property FriendlyName
} | Sort -Property PsComputerName, Count

Ceci afin de s’assurer que la préparation à fonctionné.

Validation préliminaire du cluster

Dès lors que tous les nœuds héritent de la même configuration, on peut passer a la jonction du cluster en tant que tel.

Bien sur, vous vous êtes assurés que chaque vNic de chaque nœud peut joindre chaque vNic de tous les autres nœuds ?

Si ce n’est pas le cas, le Cmdlet Test-Cluster le fera pour vous.

Ouvrez une session sur l’un des nœuds,

PowerShell
$Nodes = 'nodea', 'nodeb', 'nodec', 'noded'
Test-Cluster -Node $nodes –Include "Storage Spaces Direct", "Inventory", "Network", "System Configuration"

Apres quelques minutes, un rapport sera généré et reflètera les déviances et autres anomalies.

Vous pouvez ignorer les warning (⚠️), mais pas les échecs (❌).

Donc si c’est jaune, vous corrigerez plus tard, si c’est rouge, c’est mort.

Création du cluster

PowerShell
#Tell to powershell to clusterize all nodes for you
New-Cluster -Name $CNO -Node $nodes -NoStorage -staticAddress [CNOIPAddress]

Le cmdlet nécessite que vous ayez les droits de création d’objets Computer sur l’AD. Si vous êtes dans un contexte restreint, vous pouvez/devez le faire pré-provisionner.

ℹ️ L’ip du CNO doit être dans le même VLAN que la vNic de management.

Activer l’hyperconvergence

PowerShell
Enable-ClusterS2D -Cim $CNO

Facile… ?

Dans un cas simple, ou les disques de cache sont by design plus rapide que ceux de stockage, c’est nickel.

Oui mais, moi j’ai de la thune, et je suis en full flash NVME…

Microsoft l’explique très bien, il faut forcer le model de disques que vous souhaitez réserver au cache.

PowerShell
Get-PhysicalDisk | Group Model -NoElement

Count Name
----- ----
    8 FABRIKAM NVME-1710
   16 CONTOSO NVME-1520

Then enter the following command, specifying the cache device model:

PowerShell
Enable-ClusterS2D -CacheDeviceModel "FABRIKAM NVME-1710"

Vous pouvez vérifier que les lecteurs souhaités sont utilisés pour la mise en cache en exécutant Get-PhysicalDisk dans PowerShell et en vérifiant que leur propriété Usage indique Journal

Création du volume de stockage

PowerShell
# Example: With 2 or 3 servers
# If your deployment has only two servers, Storage Spaces Direct will automatically use two-way mirroring
New-Volume -FriendlyName "Volume1" -AllocationUnitSize 65536 -FileSystem CSVFS_ReFS -StoragePoolFriendlyName S2D* -Size xTB
# Example: With 4+ servers
# 3-Way Mirroring
New-Volume -FriendlyName "Volume1" -AllocationUnitSize 65536 -FileSystem CSVFS_ReFS -StoragePoolFriendlyName S2D* -Size xTB -ResiliencySettingName Mirror
# 3-Way Parity
New-Volume -FriendlyName "Volume1" -AllocationUnitSize 65536 -FileSystem CSVFS_ReFS -StoragePoolFriendlyName S2D* -Size xTB -ResiliencySettingName Parity

⚠️ Notez que la taille d’unité d’allocation a 64KB est un best practice MS pour les besoins Hyper-V et particulièrement si vous renoncez au ReFS au profit du NTFS (avec une taille d’unité à 4KB le volume se limiterait à 16TB)

ℹ️ A mon sens: Utilisez une typologie de type Mirror pour de la production, réservez le Parity pour du stockage pure (Franchement ça rame !). Personnellement, je n’aime pas le tiering parce qu’il est difficile d’appréhender la ratio Perf/Capa.

Renommage du Virtual disk

A titre purement instructif, renommez les divers composants du CSV

PowerShell
Get-VirtualDisk Volume1 | Set-VirtualDisk -NewFriendlyName CSV01
(Get-ClusterSharedVolume -Name *Volume1*)[0].Name = 'Cluster Virtual Disk (CSV01)'
Set-Volume -FileSystemLabel Volume1 -NewFileSystemLabel CSV01

Avec ceci, vous allez obtenir:

  • Un point de montage dans C:\ÇlusterShardVolume nommé Volume1
  • Un disque virtuel nommé ‘CSV01’
  • Un Volume Cluster Partagé (CSV) nommé ‘Cluster Virtual Disk (CSV01)’
  • Un Volume « classique » dont le label est CSV01

Le quorum

Il existe plusieurs facon de gérer le Quorum. Ici je présente le File-Share Witness. (hébergé sur un File-Share Windows qui plus est…)

PowerShell
# don't forget to feed the variables below:
$CNO = [ClusterName]
$FsServer = [FileShareServerName]
$Path = [FileShareLocation]

$FsW = icm -Computername $FsServer -scriptblock {
    $Path = $Using:Path
    $dir = New-Item -Path $Path -Name $Using:CNO -ItemType Directory
    New-SmbShare -Name ($Using:CNO + '$') -Path $dir.FullName -FullAccess ($Using:CNO + '$') -ContinuouslyAvailable:$false -Description "File Share Witness Quorum for $Using:CNO"
    (Get-SmbShare –Name ($Using:CNO + '$')).PresetPathAcl | Set-Acl
}
FailoverClusters\Set-ClusterQuorum -NodeAndFileShareMajority "\\$($FsW.ScopeName)\$($FsW.Name)" -Cluster $CNO

Configuration avancée (optionnelle)

Optimisation SpacePort (résilience du stockage)

Certains fournisseurs (comme Dell) recommandent d’ajuster le délai d’expiration matériel du service SpacePort pour améliorer la tolérance aux ralentissements I/O temporaires sur les disques.

PowerShell
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\spaceport\Parameters -Name HwTimeout -Value 0x00002710

💡 Cette valeur hexadécimale 0x2710 correspond à 10 000 millisecondes (soit 10 secondes). Cela donne au service SpacePort plus de temps avant de considérer qu’un disque ou un canal I/O est défaillant.

📌 Contexte d’usage :

  • Améliore la résilience aux latences des disques virtuels (VHD/VHDX/VMDK)
  • Recommandé dans les clusters invités ou environnements fortement virtualisés
  • Prévient les failovers prématurés en cas de micro-coupures disque ou pics de latence

🔍 Variante Microsoft :

Microsoft recommande parfois une valeur plus haute, typiquement : 0x7530

Soit 30 000 ms (30 secondes) pour des environnements très sensibles aux I/O longues (réplication, snapshots, sous-provisionnement SSD, etc.).

⚠️ Ce paramètre doit être ajusté avec précaution, car il affecte la réactivité du système face à une panne réelle. À utiliser en connaissance de cause, et idéalement recommandé par le constructeur ou sur base d’observations (event logs, counters).

ℹ️ La valeur par défaut est 0x1770 ou 6sec

Renommer les réseaux de cluster

Pour plus de lisibilité, renommez les réseaux de cluster (visible dans la console failover cluster)

PowerShell
(Get-ClusterNetwork -Cluster $CNO | ? Role -eq ClusterAndClient).name = "Management"
(Get-ClusterNetwork -Cluster $CNO | ? Role -ne ClusterAndClient).name = "SMB"

Augmenter la taille de block de cache du cluster

D’expérience, cela améliore grandement les performances d’écritures sur le CSV. La valeur est purement arbitraire, je vous invite à faire des benches pour connaitre la valeur la plus adéquat.

PowerShell
#Configuring CSV Cache
(Get-Cluster).BlockCacheSize = 8192

Si je ne dis pas de bêtise, il est désactivé sur 2016, et à 1024 sur 2019.

AD: Trafic RPC limité à un port spécifique ?

ℹ️ Si votre configuration AD n’est pas concernée, passez cette étape.

PowerShell
# Create the rule to avoid 14050 vmms event 
$policy = New-Object -ComObject HNetCfg.FwPolicy2
$rule = $policy.ServiceRestriction.Rules | Where-Object { $_.RemotePorts -eq 5191}
If ([String]::IsNullorEmpty($rule)) {
    $rule = New-Object -com HNetCfg.FWRule
    $rule.Name = "Allow outbound traffic from service to TCP 5191"
    $rule.Direction = 2
    $rule.Protocol = 6
    $rule.Enabled = $true
    $rule.RemotePorts = "5191"
    $rule.ApplicationName = "$($env:systemdrive)\WINDOWS\system32\vmms.exe"
    $rule.ServiceName = "vmms"
    $policy.ServiceRestriction.Rules.Add($rule)
}

Pour plus d’info, consultez l’article MS concernant le Trafic RPC

Augmentation de la taille des journaux

ℹ️ facultatif

Augmenter leur taille permet plus de rétention. Par défaut le taux de rétention est trop petit et la congestion est fréquente.

PowerShell
# extend log max size
wevtutil set-log "Microsoft-Windows-SMBServer/Analytic" /enabled:false
wevtutil set-log "Microsoft-Windows-SMBServer/Analytic" /enabled:true /quiet:true /retention:true /maxsize:52428800

wevtutil set-log "Microsoft-Windows-Hyper-V-VmSwitch-Diagnostic" /enabled:false
wevtutil set-log "Microsoft-Windows-Hyper-V-VmSwitch-Diagnostic" /enabled:true /quiet:true /retention:true /maxsize:52428800

wevtutil set-log "Microsoft-Windows-Kernel-IoTrace/Diagnostic" /enabled:false
wevtutil set-log "Microsoft-Windows-Kernel-IoTrace/Diagnostic" /enabled:true /quiet:true /retention:true /maxsize:52428800

Bonus

S2D data re-balance.

@Cosmos Darwin, please considere all my appologies regarding what i’m about to do.

De quoi s’agit-il ?

C’est un script qu’il met arrivé d’exécuter comme ça, à brule-pourpoint, pour avoir une réponse rapide à une question simple: -« les données du pool sont-elles correctement réparties entre les disques ? »

Il arrive parfois, notemment après l’insertion de nouveaux disques au sein d’un cluster, que des disques ne soient pas utilisés pour acceuillir des données du StoragepPool à la même échelle que les autres.

Parfois même, c’est l’ensemble des disques d’un noeud qui sont sous-utilisés; Après une mise en maintenance par exemple.

D’où nous vient ce script ?

L’origine de ce script, je le tiens de Cosmos Darwin, un ingénieur confirmé, plus précisément de cette page.

Ce que l’on retiendra

J’ai réduit le script d’origine à 65 lignes pour avoir l’essentiel, et ajouté un fichier format.ps1xml à côté pour ceux qui ont besoin d’avoir les valeurs « humainement » lisibles.

Le résultat de la commande est pas loin de ça:


J’ai mis tout ça sur mon repo Git… C’est cadeau !

Cette publication était-elle utile ?

Cliquez sur une étoile pour l'évaluer !

Note moyenne 0 / 5. Nombre de votes : 0

Aucun vote pour le moment ! Soyez le premier à évaluer cette publication.

Laisser un commentaire