分享方式:


分隔儲存空間直接存取中的磁片區配置

適用於︰Windows Server 2022、Windows Server 2019

Windows Server 2019 引進了手動分隔磁片區配置儲存空間直接存取的選項。 這樣做可以大幅提高特定條件下的容錯能力,但會強加一些額外的管理考慮和複雜度。 本主題說明其運作方式,並在 PowerShell 中提供範例。

重要

這項功能是 Windows Server 2019 的新功能。 Windows Server 2016 中無法使用。

必要條件

Green checkmark icon. 如果下列事項,請考慮使用此選項:

Red X icon. 如果下列事項,請勿使用此選項:

了解

檢閱:一般配置

使用一般三向鏡像時,磁片區分成許多小型「板狀」,這些「板狀」會複製三次,並平均分散到叢集中每部伺服器中的每個磁片磁碟機。 如需詳細資訊,請閱讀 此深入探討部落格

Diagram showing the volume being divided into three stacks of slabs and distributed evenly across every server.

此預設配置會將平行讀取和寫入最大化,進而提升效能,而且在簡單性方面很有吸引力:每部伺服器都同樣忙碌,每個磁片磁碟機都同樣完整,而且所有磁片區都會保持線上或離線。 如 這些範例 所示,每個磁片區都保證能存活最多兩個並行失敗。

不過,使用此配置時,磁片區無法倖存三個並行失敗。 如果三部伺服器一次失敗,或三部伺服器中的磁片磁碟機一次失敗,磁片區會變成無法存取,因為至少有一些板狀板配置給確切的三個磁片磁碟機或伺服器失敗。

在下列範例中,伺服器 1、3 和 5 同時失敗。 雖然許多板有倖存的複本,但有些沒有:

Diagram showing three of six servers highlighted in red, and the overall volume is red.

磁片區會離線並變成無法存取,直到伺服器復原為止。

新增:分隔配置

使用分隔配置時,您會指定要使用的伺服器子集(至少四個)。 磁片區分成三次複製的板狀,例如之前,但不會配置到每部伺服器, 而是將板配置給您指定的 伺服器子集。

例如,如果您有 8 個節點叢集(節點 1 到 8),則可以指定磁片區只位於節點 1、2、3、4 中的磁片區。

優點

使用範例配置時,磁片區很可能在三個並行失敗中倖存下來。 如果節點 1、2 和 6 關閉,則只有 2 個節點保留磁片區 3 個數據複本的節點已關閉,磁片區會保持上線狀態。

生存機率取決於伺服器數目和其他因素 – 請參閱 分析 以取得詳細資料。

缺點

分隔配置會強加一些新增的管理考慮和複雜性:

  1. 系統管理員負責分隔每個磁片區的配置,以平衡伺服器之間的儲存體使用率,並維護高生存機率,如最佳做法 一節所述

  2. 使用分隔配置,保留 相當於每部伺服器的一個容量磁片磁碟機(沒有最大值)。 這超過 一般配置的已發佈建議 ,最大值為四個容量磁片磁碟機總數。

  3. 如果伺服器失敗且需要取代,如移除伺服器及其磁片磁碟機中所述 ,系統管理員會負責更新受影響磁片區的分隔符號,方法是新增伺服器並移除失敗的磁片區 – 範例如下。

PowerShell 中的使用方式

您可以使用 New-Volume Cmdlet 在 儲存空間直接存取 中建立磁片區。

例如,若要建立一般三向鏡像磁片區:

New-Volume -FriendlyName "MyRegularVolume" -Size 100GB

建立磁片區並分隔其配置

若要建立三向鏡像磁片區,並分隔其配置:

  1. 首先,將叢集中的伺服器指派給 變數 $Servers

    $Servers = Get-StorageFaultDomain -Type StorageScaleUnit | Sort FriendlyName
    

    提示

    在儲存空間直接存取中,「儲存體縮放單位」一詞是指連結至一部伺服器的所有原始儲存體,包括直接連結的磁片磁碟機和具有磁片磁碟機的直接連結外部主機殼。 在此內容中,它與 「伺服器」相同。

  2. 指定要與新 -StorageFaultDomainsToUse 參數搭配使用的伺服器,並將 索引編制成 $Servers 。 例如,若要將配置分隔至第一部、第二部、第三部和第四部伺服器(索引 0、1、2 和 3):

    New-Volume -FriendlyName "MyVolume" -Size 100GB -StorageFaultDomainsToUse $Servers[0,1,2,3]
    

請參閱分隔配置

若要查看 MyVolume 的配置方式 ,請使用 Get-VirtualDiskFootprintBySSU.ps1 附錄 中的 腳本:

PS C:\> .\Get-VirtualDiskFootprintBySSU.ps1

VirtualDiskFriendlyName TotalFootprint Server1 Server2 Server3 Server4 Server5 Server6
----------------------- -------------- ------- ------- ------- ------- ------- -------
MyVolume                300 GB         100 GB  100 GB  100 GB  100 GB  0       0

請注意,只有 Server1、Server2、Server3 和 Server4 包含 MyVolume 板。

變更分隔的配置

使用新的 Add-StorageFaultDomainRemove-StorageFaultDomain Cmdlet 來變更配置分隔的方式。

例如,若要將 MyVolume 移至 一部伺服器:

  1. 指定第五部伺服器 可以 儲存 MyVolume 板狀:

    Get-VirtualDisk MyVolume | Add-StorageFaultDomain -StorageFaultDomains $Servers[4]
    
  2. 指定第一部伺服器 無法 儲存 MyVolume 板狀:

    Get-VirtualDisk MyVolume | Remove-StorageFaultDomain -StorageFaultDomains $Servers[0]
    
  3. 重新平衡儲存集區,讓變更生效:

    Get-StoragePool S2D* | Optimize-StoragePool
    

您可以使用 來監視重新平衡 Get-StorageJob 的進度。

完成之後,請再次確認 Get-VirtualDiskFootprintBySSU.ps1 MyVolume 已移動。

PS C:\> .\Get-VirtualDiskFootprintBySSU.ps1

VirtualDiskFriendlyName TotalFootprint Server1 Server2 Server3 Server4 Server5 Server6
----------------------- -------------- ------- ------- ------- ------- ------- -------
MyVolume                300 GB         0       100 GB  100 GB  100 GB  100 GB  0

請注意,Server1 不再包含 MyVolume 板狀 ,而是 Server5。

最佳作法

以下是使用分隔磁片區配置時要遵循的最佳做法:

選擇四部伺服器

將每個三向鏡像磁片區分隔為四部伺服器,而非更多伺服器。

平衡儲存體

平衡配置給每部伺服器的儲存體數量,並考慮磁片區大小。

交錯分隔的配置磁片區

若要最大化容錯能力,請讓每個磁片區的配置成為唯一的,這表示它不會與另一個磁片區共用 其所有 伺服器(有些重迭沒問題)。

例如,在八個節點系統上:磁片區 1:伺服器 1、2、3、4 磁片區 2:伺服器 5、6、7、8 磁片區 3:伺服器 3、4、5、6 磁片區 4:伺服器 1、2、7、8

分析

本節會衍生磁片區保持線上且可存取的數學機率(或同等的,即維持在線上且可存取的整體儲存體的預期分數),做為失敗數目和叢集大小的函式。

注意

本節是選擇性閱讀。 如果您渴望看到數學,請閱讀! 但如果沒有,別擔心: PowerShell 中的使用量和 最佳做法 是您需要成功實作分隔配置。

最多兩個失敗永遠沒問題

不論其配置為何,每一個三向鏡像磁片區最多可以同時存留兩次失敗。 如果兩個磁片磁碟機失敗,或兩部伺服器失敗,或兩部伺服器失敗,則每一個三向鏡像磁片區都會保持線上且可存取,即使有一般配置也一樣。

超過一半的叢集失敗永遠沒問題

相反地,在極端情況下,叢集中超過一半的伺服器或磁片磁碟機會一次失敗,仲裁會遺失 而且每一個三向鏡像磁片區都會離線且無法存取,而不論其配置為何。

介於兩者之間呢?

如果一次發生三或多個失敗,但至少有一半的伺服器和磁片磁碟機仍在啟動中,則具有分隔配置的磁片區可能會保持線上且可存取,視哪些伺服器發生失敗而定。

常見問題集

我可以分隔一些磁片區,但不能分隔其他磁片區嗎?

是。 您可以選擇個別磁片區是否要分隔配置。

分隔配置會變更磁片磁碟機更換的運作方式嗎?

否,這與一般配置相同。

其他參考

附錄

此腳本可協助您查看磁片區的配置方式。

若要如上述所述使用,請複製/貼上並儲存為 Get-VirtualDiskFootprintBySSU.ps1

Function ConvertTo-PrettyCapacity {
    Param (
        [Parameter(
            Mandatory = $True,
            ValueFromPipeline = $True
            )
        ]
    [Int64]$Bytes,
    [Int64]$RoundTo = 0
    )
    If ($Bytes -Gt 0) {
        $Base = 1024
        $Labels = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
        $Order = [Math]::Floor( [Math]::Log($Bytes, $Base) )
        $Rounded = [Math]::Round($Bytes/( [Math]::Pow($Base, $Order) ), $RoundTo)
        [String]($Rounded) + " " + $Labels[$Order]
    }
    Else {
        "0"
    }
    Return
}

Function Get-VirtualDiskFootprintByStorageFaultDomain {

    ################################################
    ### Step 1: Gather Configuration Information ###
    ################################################

    Write-Progress -Activity "Get-VirtualDiskFootprintByStorageFaultDomain" -CurrentOperation "Gathering configuration information..." -Status "Step 1/4" -PercentComplete 00

    $ErrorCannotGetCluster = "Cannot proceed because 'Get-Cluster' failed."
    $ErrorNotS2DEnabled = "Cannot proceed because the cluster is not running Storage Spaces Direct."
    $ErrorCannotGetClusterNode = "Cannot proceed because 'Get-ClusterNode' failed."
    $ErrorClusterNodeDown = "Cannot proceed because one or more cluster nodes is not Up."
    $ErrorCannotGetStoragePool = "Cannot proceed because 'Get-StoragePool' failed."
    $ErrorPhysicalDiskFaultDomainAwareness = "Cannot proceed because the storage pool is set to 'PhysicalDisk' fault domain awareness. This cmdlet only supports 'StorageScaleUnit', 'StorageChassis', or 'StorageRack' fault domain awareness."

    Try  {
        $GetCluster = Get-Cluster -ErrorAction Stop
    }
    Catch {
        throw $ErrorCannotGetCluster
    }

    If ($GetCluster.S2DEnabled -Ne 1) {
        throw $ErrorNotS2DEnabled
    }

    Try  {
        $GetClusterNode = Get-ClusterNode -ErrorAction Stop
    }
    Catch {
        throw $ErrorCannotGetClusterNode
    }

    If ($GetClusterNode | Where State -Ne Up) {
        throw $ErrorClusterNodeDown
    }

    Try {
        $GetStoragePool = Get-StoragePool -IsPrimordial $False -ErrorAction Stop
    }
    Catch {
        throw $ErrorCannotGetStoragePool
    }

    If ($GetStoragePool.FaultDomainAwarenessDefault -Eq "PhysicalDisk") {
        throw $ErrorPhysicalDiskFaultDomainAwareness
    }

    ###########################################################
    ### Step 2: Create SfdList[] and PhysicalDiskToSfdMap{} ###
    ###########################################################

    Write-Progress -Activity "Get-VirtualDiskFootprintByStorageFaultDomain" -CurrentOperation "Analyzing physical disk information..." -Status "Step 2/4" -PercentComplete 25

    $SfdList = Get-StorageFaultDomain -Type ($GetStoragePool.FaultDomainAwarenessDefault) | Sort FriendlyName # StorageScaleUnit, StorageChassis, or StorageRack

    $PhysicalDiskToSfdMap = @{} # Map of PhysicalDisk.UniqueId -> StorageFaultDomain.FriendlyName
    $SfdList | ForEach {
        $StorageFaultDomain = $_
        $_ | Get-StorageFaultDomain -Type PhysicalDisk | ForEach {
            $PhysicalDiskToSfdMap[$_.UniqueId] = $StorageFaultDomain.FriendlyName
        }
    }

    ##################################################################################################
    ### Step 3: Create VirtualDisk.FriendlyName -> { StorageFaultDomain.FriendlyName -> Size } Map ###
    ##################################################################################################

    Write-Progress -Activity "Get-VirtualDiskFootprintByStorageFaultDomain" -CurrentOperation "Analyzing virtual disk information..." -Status "Step 3/4" -PercentComplete 50

    $GetVirtualDisk = Get-VirtualDisk | Sort FriendlyName

    $VirtualDiskMap = @{}

    $GetVirtualDisk | ForEach {
        # Map of PhysicalDisk.UniqueId -> Size for THIS virtual disk
        $PhysicalDiskToSizeMap = @{}
        $_ | Get-PhysicalExtent | ForEach {
            $PhysicalDiskToSizeMap[$_.PhysicalDiskUniqueId] += $_.Size
        }
        # Map of StorageFaultDomain.FriendlyName -> Size for THIS virtual disk
        $SfdToSizeMap = @{}
        $PhysicalDiskToSizeMap.keys | ForEach {
            $SfdToSizeMap[$PhysicalDiskToSfdMap[$_]] += $PhysicalDiskToSizeMap[$_]
        }
        # Store
        $VirtualDiskMap[$_.FriendlyName] = $SfdToSizeMap
    }

    #########################
    ### Step 4: Write-Out ###
    #########################

    Write-Progress -Activity "Get-VirtualDiskFootprintByStorageFaultDomain" -CurrentOperation "Formatting output..." -Status "Step 4/4" -PercentComplete 75

    $Output = $GetVirtualDisk | ForEach {
        $Row = [PsCustomObject]@{}

        $VirtualDiskFriendlyName = $_.FriendlyName
        $Row | Add-Member -MemberType NoteProperty "VirtualDiskFriendlyName" $VirtualDiskFriendlyName

        $TotalFootprint = $_.FootprintOnPool | ConvertTo-PrettyCapacity
        $Row | Add-Member -MemberType NoteProperty "TotalFootprint" $TotalFootprint

        $SfdList | ForEach {
            $Size = $VirtualDiskMap[$VirtualDiskFriendlyName][$_.FriendlyName] | ConvertTo-PrettyCapacity
            $Row | Add-Member -MemberType NoteProperty $_.FriendlyName $Size
        }

        $Row
    }

    # Calculate width, in characters, required to Format-Table
    $RequiredWindowWidth = ("TotalFootprint").length + 1 + ("VirtualDiskFriendlyName").length + 1
    $SfdList | ForEach {
        $RequiredWindowWidth += $_.FriendlyName.Length + 1
    }

    $ActualWindowWidth = (Get-Host).UI.RawUI.WindowSize.Width

    If (!($ActualWindowWidth)) {
        # Cannot get window width, probably ISE, Format-List
        Write-Warning "Could not determine window width. For the best experience, use a Powershell window instead of ISE"
        $Output | Format-Table
    }
    ElseIf ($ActualWindowWidth -Lt $RequiredWindowWidth) {
        # Narrower window, Format-List
        Write-Warning "For the best experience, try making your PowerShell window at least $RequiredWindowWidth characters wide. Current width is $ActualWindowWidth characters."
        $Output | Format-List
    }
    Else {
        # Wider window, Format-Table
        $Output | Format-Table
    }
}

Get-VirtualDiskFootprintByStorageFaultDomain