共用方式為


使用 PowerShell 和儲存空間直接存取效能歷程記錄編寫指令碼

在 Windows Server 2019 中,儲存空間直接存取會記錄,並儲存虛擬機器、伺服器、磁碟機、磁碟區、網路介面卡等大量 效能歷程記錄。 效能歷程記錄很容易在 PowerShell 中查詢和處理,因此您可以快速地將 原始資料 變成問題的 實際答案,例如:

  1. 上周是否有任何 CPU 尖峰?
  2. 是否有任何實體磁碟表現出異常延遲?
  3. 哪些 VM 目前耗用最多的儲存體 IOPS?
  4. 我的網路頻寬是否已飽和?
  5. 此磁碟區何時會用盡可用空間?
  6. 在過去一個月中,哪些 VM 使用最多的記憶體?

Get-ClusterPerf Cmdlet 是針對指令碼所建置。 它會接受來自管線 Get-VMGet-PhysicalDisk 等 Cmdlet 的輸入以處理關聯,而且您可以將輸出管線傳送至公用程式 Cmdlet,例如 Sort-ObjectWhere-ObjectMeasure-Object,以快速撰寫功能強大的查詢。

本主題會提供並說明回答上述 6 個問題的 6 個範例指令碼。 這提供了一個模式,讓您在各種資料和時間範圍套用,以尋找尖峰、尋找平均值、繪製趨勢線、執行極端值偵測等。 它們會以免費入門程式碼的形式提供,讓您複製、擴充和重複使用。

注意

為了簡潔起見,範例指令碼會省略您可能預期高品質 PowerShell 程式碼的錯誤處理等事項。 它們主要是用於靈感和教育,而不是生產用途。

範例 1: CPU,我看到你嘍!

此範例會使用 LastWeek 時間範圍內的 ClusterNode.Cpu.Usage 數列來顯示叢集中每部伺服器的最大值 (「高水位標記」)、最小值和平均 CPU 使用量。 它也會進行簡單的四分位數分析,以顯示過去 8 天內 CPU 使用量超過 25%、50% 和 75% 的時數。

螢幕擷取畫面

在下列螢幕擷取畫面中,我們看到 Server-02 上週出現無法解釋的尖峰:

顯示 Server-02 上周出現未解釋尖峰的螢幕快照。

運作方式

來自 Get-ClusterPerf 管線的輸出會順利管線傳送至內建的 Measure-Object Cmdlet,我們只指定 Value 屬性。 透過其 -Maximum-Minimum-Average 旗標, Measure-Object 幾乎免費提供前三欄。 若要進行四分位數分析,我們可以將管線傳送至 Where-Object ,並計算有幾個值 -Gt (大於) 25、50 或 75。 最後一個步驟是美化 Format-HoursFormat-Percent 協助程式函式 – 這當然是選擇性的。

指令碼

以下是指令碼:

Function Format-Hours {
    Param (
        $RawValue
    )
    # Weekly timeframe has frequency 15 minutes = 4 points per hour
    [Math]::Round($RawValue/4)
}

Function Format-Percent {
    Param (
        $RawValue
    )
    [String][Math]::Round($RawValue) + " " + "%"
}

$Output = Get-ClusterNode | ForEach-Object {
    $Data = $_ | Get-ClusterPerf -ClusterNodeSeriesName "ClusterNode.Cpu.Usage" -TimeFrame "LastWeek"

    $Measure = $Data | Measure-Object -Property Value -Minimum -Maximum -Average
    $Min = $Measure.Minimum
    $Max = $Measure.Maximum
    $Avg = $Measure.Average

    [PsCustomObject]@{
        "ClusterNode"    = $_.Name
        "MinCpuObserved" = Format-Percent $Min
        "MaxCpuObserved" = Format-Percent $Max
        "AvgCpuObserved" = Format-Percent $Avg
        "HrsOver25%"     = Format-Hours ($Data | Where-Object Value -Gt 25).Length
        "HrsOver50%"     = Format-Hours ($Data | Where-Object Value -Gt 50).Length
        "HrsOver75%"     = Format-Hours ($Data | Where-Object Value -Gt 75).Length
    }
}

$Output | Sort-Object ClusterNode | Format-Table

範例 2: 引發、引發、延遲極端值

此範例會使用 LastHour 時間範圍內的 PhysicalDisk.Latency.Average 數列來尋找統計極端值,定義為每小時平均延遲超過總體平均值超過 3σ(三個標準差) 的磁碟機。

重要

為了簡潔起見,此指令碼不會針對低變異數實作保護,不會處理部分遺漏的資料,也不會區分模型或韌體等。請運用良好的判斷,不要只依賴此指令碼來判斷是否要更換硬碟。 這裡僅供教育之用。

螢幕擷取畫面

在下列螢幕擷取畫面中,我們看到沒有極端值:

顯示沒有極端值的螢幕快照。

運作方式

首先,我們會檢查 PhysicalDisk.Iops.Total 是否一致地 -Gt 1,來排除閒置或幾乎閒置的磁碟機。 針對每個作用中的 HDD,我們會使用管線傳送其 LastHour 時間範圍,以 10 秒間隔進行 360 次度量,以 Measure-Object -Average 並在最後一小時內取得其平均延遲。 這會設定我們的人口。

我們會實作 廣為人知的公式 來找出總體的平均值 μ 和標準差 σ。 對於每個作用中的 HDD,我們會將其平均延遲與總體的平均值相比,並除以標準差。 我們會保留原始值,讓我們可以 Sort-Object 結果,但使用 Format-LatencyFormat-StandardDeviation 協助程式函式來美化我們將顯示的項目 – 這當然是選擇性的。

如果有任何磁碟機超過 3σ,我們會以紅色 Write-Host;如果沒有,則以綠色表示。

指令碼

以下是指令碼:

Function Format-Latency {
    Param (
        $RawValue
    )
    $i = 0 ; $Labels = ("s", "ms", "μs", "ns") # Petabits, just in case!
    Do { $RawValue *= 1000 ; $i++ } While ( $RawValue -Lt 1 )
    # Return
    [String][Math]::Round($RawValue, 2) + " " + $Labels[$i]
}

Function Format-StandardDeviation {
    Param (
        $RawValue
    )
    If ($RawValue -Gt 0) {
        $Sign = "+"
    }
    Else {
        $Sign = "-"
    }
    # Return
    $Sign + [String][Math]::Round([Math]::Abs($RawValue), 2) + "σ"
}

$HDD = Get-StorageSubSystem Cluster* | Get-PhysicalDisk | Where-Object MediaType -Eq HDD

$Output = $HDD | ForEach-Object {

    $Iops = $_ | Get-ClusterPerf -PhysicalDiskSeriesName "PhysicalDisk.Iops.Total" -TimeFrame "LastHour"
    $AvgIops = ($Iops | Measure-Object -Property Value -Average).Average

    If ($AvgIops -Gt 1) { # Exclude idle or nearly idle drives

        $Latency = $_ | Get-ClusterPerf -PhysicalDiskSeriesName "PhysicalDisk.Latency.Average" -TimeFrame "LastHour"
        $AvgLatency = ($Latency | Measure-Object -Property Value -Average).Average

        [PsCustomObject]@{
            "FriendlyName"  = $_.FriendlyName
            "SerialNumber"  = $_.SerialNumber
            "MediaType"     = $_.MediaType
            "AvgLatencyPopulation" = $null # Set below
            "AvgLatencyThisHDD"    = Format-Latency $AvgLatency
            "RawAvgLatencyThisHDD" = $AvgLatency
            "Deviation"            = $null # Set below
            "RawDeviation"         = $null # Set below
        }
    }
}

If ($Output.Length -Ge 3) { # Minimum population requirement

    # Find mean μ and standard deviation σ
    $μ = ($Output | Measure-Object -Property RawAvgLatencyThisHDD -Average).Average
    $d = $Output | ForEach-Object { ($_.RawAvgLatencyThisHDD - $μ) * ($_.RawAvgLatencyThisHDD - $μ) }
    $σ = [Math]::Sqrt(($d | Measure-Object -Sum).Sum / $Output.Length)

    $FoundOutlier = $False

    $Output | ForEach-Object {
        $Deviation = ($_.RawAvgLatencyThisHDD - $μ) / $σ
        $_.AvgLatencyPopulation = Format-Latency $μ
        $_.Deviation = Format-StandardDeviation $Deviation
        $_.RawDeviation = $Deviation
        # If distribution is Normal, expect >99% within 3σ
        If ($Deviation -Gt 3) {
            $FoundOutlier = $True
        }
    }

    If ($FoundOutlier) {
        Write-Host -BackgroundColor Black -ForegroundColor Red "Oh no! There's an HDD significantly slower than the others."
    }
    Else {
        Write-Host -BackgroundColor Black -ForegroundColor Green "Good news! No outlier found."
    }

    $Output | Sort-Object RawDeviation -Descending | Format-Table FriendlyName, SerialNumber, MediaType, AvgLatencyPopulation, AvgLatencyThisHDD, Deviation

}
Else {
    Write-Warning "There aren't enough active drives to look for outliers right now."
}

範例 3: 吵雜的鄰居? 這就是寫入!

效能歷程記錄也可以回答與 目前 相關的問題。 每 10 秒即可取得即時的新度量。 此範例會使用 MostRecent 時間範圍中的 VHD.Iops.Total 系列來識別在所有叢集的主機中,耗用最多儲存體 IOPS 的最忙碌 (有些人會說「最吵的」) 虛擬機器,並顯示其活動的讀取/寫入明細。

螢幕擷取畫面

在下列螢幕擷取畫面中,我們會看到依儲存體活動排列前 10 部的虛擬機器:

顯示記憶體活動前10個虛擬機的螢幕快照。

運作方式

不同於 Get-PhysicalDiskGet-VM Cmdlet 不會感知叢集,它只會在本機伺服器上傳回 VM。 若要平行查詢每部伺服器,我們會在 Invoke-Command (Get-ClusterNode).Name { ... } 中包裝呼叫。 針對每個 VM,我們會取得 VHD.Iops.TotalVHD.Iops.ReadVHD.Iops.Write 度量。 透過不指定 -TimeFrame 參數,每個參數我們可以獲得 MostRecent 個單一資料點。

提示

這些系列會反映此 VM 活動的所有 VHD/VHDX 檔案的總和。 這是自動匯總效能歷程記錄的範例。 若要取得每個 VHD/VHDX 明細,您可以將個別 Get-VHD 管線傳送至 Get-ClusterPerf,而不是 VM。

每部伺服器的結果會聚集在一起作爲 $Output,我們可以 Sort-Object ,然後 Select-Object -First 10。 請注意,Invoke-Command 會使用 PsComputerName 屬性裝飾結果,指出其來自何處,我們可以列印以知道 VM 的執行位置。

指令碼

以下是指令碼:

$Output = Invoke-Command (Get-ClusterNode).Name {
    Function Format-Iops {
        Param (
            $RawValue
        )
        $i = 0 ; $Labels = (" ", "K", "M", "B", "T") # Thousands, millions, billions, trillions...
        Do { if($RawValue -Gt 1000){$RawValue /= 1000 ; $i++ } } While ( $RawValue -Gt 1000 )
        # Return
        [String][Math]::Round($RawValue) + " " + $Labels[$i]
    }

    Get-VM | ForEach-Object {
        $IopsTotal = $_ | Get-ClusterPerf -VMSeriesName "VHD.Iops.Total"
        $IopsRead  = $_ | Get-ClusterPerf -VMSeriesName "VHD.Iops.Read"
        $IopsWrite = $_ | Get-ClusterPerf -VMSeriesName "VHD.Iops.Write"
        [PsCustomObject]@{
            "VM" = $_.Name
            "IopsTotal" = Format-Iops $IopsTotal.Value
            "IopsRead"  = Format-Iops $IopsRead.Value
            "IopsWrite" = Format-Iops $IopsWrite.Value
            "RawIopsTotal" = $IopsTotal.Value # For sorting...
        }
    }
}

$Output | Sort-Object RawIopsTotal -Descending | Select-Object -First 10 | Format-Table PsComputerName, VM, IopsTotal, IopsRead, IopsWrite

範例 4: 正如他們所說,「25 gig 是新的 10 gig」

此範例會使用 LastDay 時間範圍內的 NetAdapter.Bandwidth.Total 數列來尋找網路飽和的跡象,定義為理論最大頻寬的 >90%。 對於叢集中的每個網路介面卡,它會比較過去一天觀察到的最高頻寬使用量和其陳述的連結速度。

螢幕擷取畫面

在下列螢幕擷取畫面中,我們看到前一天有一個 Fabrikam NX-4 Pro #2 達到尖峰:

顯示 Fabrikam NX-4 Pro #2 在過去一天達到尖峰的螢幕快照。

運作方式

我們重複上述的 Invoke-Command 技巧,在每個伺服器上 Get-NetAdapter,並管線傳送至 Get-ClusterPerf。 一路上,我們會擷取兩個相關的屬性: 其 LinkSpeed 字串,例如 「10 Gbps」,以及其原始 Speed 整數,例如 10000000000。 我們會使用 Measure-Object 從前一天取得平均值和尖峰值 (提醒: LastDay 時間範圍中的每個度量代表 5 分鐘),並乘以每位元組 8 個位元來取得 apples-to-apples 比較。

注意

某些廠商,例如 Chelsio,會在其 網路介面卡 效能計數器中包含遠端直接記憶體存取 (RDMA) 活動,因此它會包含在 NetAdapter.Bandwidth.Total 系列中。 其他的廠商,例如 Mellanox,則可能不會這麽做。 如果您的廠商沒有這麽做,只要在此指令碼您的版本中新增 NetAdapter.Bandwidth.RDMA.Total 系列即可。

指令碼

以下是指令碼:

$Output = Invoke-Command (Get-ClusterNode).Name {

    Function Format-BitsPerSec {
        Param (
            $RawValue
        )
        $i = 0 ; $Labels = ("bps", "kbps", "Mbps", "Gbps", "Tbps", "Pbps") # Petabits, just in case!
        Do { $RawValue /= 1000 ; $i++ } While ( $RawValue -Gt 1000 )
        # Return
        [String][Math]::Round($RawValue) + " " + $Labels[$i]
    }

    Get-NetAdapter | ForEach-Object {

        $Inbound = $_ | Get-ClusterPerf -NetAdapterSeriesName "NetAdapter.Bandwidth.Inbound" -TimeFrame "LastDay"
        $Outbound = $_ | Get-ClusterPerf -NetAdapterSeriesName "NetAdapter.Bandwidth.Outbound" -TimeFrame "LastDay"

        If ($Inbound -Or $Outbound) {

            $InterfaceDescription = $_.InterfaceDescription
            $LinkSpeed = $_.LinkSpeed

            $MeasureInbound = $Inbound | Measure-Object -Property Value -Maximum
            $MaxInbound = $MeasureInbound.Maximum * 8 # Multiply to bits/sec

            $MeasureOutbound = $Outbound | Measure-Object -Property Value -Maximum
            $MaxOutbound = $MeasureOutbound.Maximum * 8 # Multiply to bits/sec

            $Saturated = $False

            # Speed property is Int, e.g. 10000000000
            If (($MaxInbound -Gt (0.90 * $_.Speed)) -Or ($MaxOutbound -Gt (0.90 * $_.Speed))) {
                $Saturated = $True
                Write-Warning "In the last day, adapter '$InterfaceDescription' on server '$Env:ComputerName' exceeded 90% of its '$LinkSpeed' theoretical maximum bandwidth. In general, network saturation leads to higher latency and diminished reliability. Not good!"
            }

            [PsCustomObject]@{
                "NetAdapter"  = $InterfaceDescription
                "LinkSpeed"   = $LinkSpeed
                "MaxInbound"  = Format-BitsPerSec $MaxInbound
                "MaxOutbound" = Format-BitsPerSec $MaxOutbound
                "Saturated"   = $Saturated
            }
        }
    }
}

$Output | Sort-Object PsComputerName, InterfaceDescription | Format-Table PsComputerName, NetAdapter, LinkSpeed, MaxInbound, MaxOutbound, Saturated

範例 5: 再次讓儲存體變得時尚!

爲了查看宏趨勢,效能歷程記錄會保留最多 1 年。 此範例會使用 LastYear 時間範圍內的 Volume.Size.Available 數列來判斷儲存體填滿的速率,以及估計何時會滿。

螢幕擷取畫面

在下列螢幕擷取畫面中,我們看到 備份 磁碟區每天會新增約 15 GB:

顯示備份磁碟區每天新增約 15 GB 的螢幕快照。

以這個速度,它將在 42 天以後達到其容量。

運作方式

LastYear 時間範圍每天有一個資料點。 雖然您只需要兩個點以符合趨勢線,但實際上最好需要更多點,例如 14 天。 我們會使用 Select-Object -Last 14 來設定範圍 [1, 14] 中 x(x, y) 點陣列。 透過這些點,我們會實作簡單的 線性最小平方演算法 以尋找參數化最符合 y = ax + b 線的 $A$B。 歡迎再次上高中。

將磁碟區的 SizeRemaining 屬性除以趨勢 (斜率 $A) 可讓我們粗略估計天數 (以目前儲存體成長的速率),直到磁碟區滿了為止。 Format-BytesFormat-TrendFormat-Days 協助程式函式會美化輸出。

重要

此估計值是線性的,且僅以最近 14 天的量測為基礎。 更複雜且精確的技術存在。 請運用良好的判斷,不要只依賴此指令碼來判斷是否要投資擴充儲存體。 這裡僅供教育之用。

指令碼

以下是指令碼:


Function Format-Bytes {
    Param (
        $RawValue
    )
    $i = 0 ; $Labels = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
    Do { $RawValue /= 1024 ; $i++ } While ( $RawValue -Gt 1024 )
    # Return
    [String][Math]::Round($RawValue) + " " + $Labels[$i]
}

Function Format-Trend {
    Param (
        $RawValue
    )
    If ($RawValue -Eq 0) {
        "0"
    }
    Else {
        If ($RawValue -Gt 0) {
            $Sign = "+"
        }
        Else {
            $Sign = "-"
        }
        # Return
        $Sign + $(Format-Bytes ([Math]::Abs($RawValue))) + "/day"
    }
}

Function Format-Days {
    Param (
        $RawValue
    )
    [Math]::Round($RawValue)
}

$CSV = Get-Volume | Where-Object FileSystem -Like "*CSV*"

$Output = $CSV | ForEach-Object {

    $N = 14 # Require 14 days of history

    $Data = $_ | Get-ClusterPerf -VolumeSeriesName "Volume.Size.Available" -TimeFrame "LastYear" | Sort-Object Time | Select-Object -Last $N

    If ($Data.Length -Ge $N) {

        # Last N days as (x, y) points
        $PointsXY = @()
        1..$N | ForEach-Object {
            $PointsXY += [PsCustomObject]@{ "X" = $_ ; "Y" = $Data[$_-1].Value }
        }

        # Linear (y = ax + b) least squares algorithm
        $MeanX = ($PointsXY | Measure-Object -Property X -Average).Average
        $MeanY = ($PointsXY | Measure-Object -Property Y -Average).Average
        $XX = $PointsXY | ForEach-Object { $_.X * $_.X }
        $XY = $PointsXY | ForEach-Object { $_.X * $_.Y }
        $SSXX = ($XX | Measure-Object -Sum).Sum - $N * $MeanX * $MeanX
        $SSXY = ($XY | Measure-Object -Sum).Sum - $N * $MeanX * $MeanY
        $A = ($SSXY / $SSXX)
        $B = ($MeanY - $A * $MeanX)
        $RawTrend = -$A # Flip to get daily increase in Used (vs decrease in Remaining)
        $Trend = Format-Trend $RawTrend

        If ($RawTrend -Gt 0) {
            $DaysToFull = Format-Days ($_.SizeRemaining / $RawTrend)
        }
        Else {
            $DaysToFull = "-"
        }
    }
    Else {
        $Trend = "InsufficientHistory"
        $DaysToFull = "-"
    }

    [PsCustomObject]@{
        "Volume"     = $_.FileSystemLabel
        "Size"       = Format-Bytes ($_.Size)
        "Used"       = Format-Bytes ($_.Size - $_.SizeRemaining)
        "Trend"      = $Trend
        "DaysToFull" = $DaysToFull
    }
}

$Output | Format-Table

範例 6: 記憶體佔用,您跑得掉,但躲不了

因為效能歷程記錄會針對整個叢集集中收集並儲存,所以不論 VM 在主機之間移動多少次,您都不需要將不同機器的資料合併在一起。 此範例會使用 LastMonth 時間範圍中的 VM.Memory.Assigned 數系來識別在過去 35 天內耗用最多記憶體的虛擬機器。

螢幕擷取畫面

在下列螢幕擷取畫面中,我們會看到依儲存體使用量排列前 10 部的虛擬機器:

PowerShell 的螢幕快照

運作方式

我們重複上述的 Invoke-Command 技巧,在每個伺服器上 Get-VM。 我們會使用 Measure-Object -Average 取得每個 VM 的每月平均值,然後 Sort-Object ,接著是 Select-Object -First 10 以取得我們的排行榜。 (或者可能是我們的 最高通緝 清單?)

指令碼

以下是指令碼:

$Output = Invoke-Command (Get-ClusterNode).Name {
    Function Format-Bytes {
        Param (
            $RawValue
        )
        $i = 0 ; $Labels = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
        Do { if( $RawValue -Gt 1024 ){ $RawValue /= 1024 ; $i++ } } While ( $RawValue -Gt 1024 )
        # Return
        [String][Math]::Round($RawValue) + " " + $Labels[$i]
    }

    Get-VM | ForEach-Object {
        $Data = $_ | Get-ClusterPerf -VMSeriesName "VM.Memory.Assigned" -TimeFrame "LastMonth"
        If ($Data) {
            $AvgMemoryUsage = ($Data | Measure-Object -Property Value -Average).Average
            [PsCustomObject]@{
                "VM" = $_.Name
                "AvgMemoryUsage" = Format-Bytes $AvgMemoryUsage.Value
                "RawAvgMemoryUsage" = $AvgMemoryUsage.Value # For sorting...
            }
        }
    }
}

$Output | Sort-Object RawAvgMemoryUsage -Descending | Select-Object -First 10 | Format-Table PsComputerName, VM, AvgMemoryUsage

介紹完畢 希望這些範例能激勵您,並協助您開始使用。 透過儲存空間直接存取效能歷程記錄和功能強大的指令碼易記 Get-ClusterPerf Cmdlet,您可以提問和查看答案! – 管理及監視 Windows Server 2019 基礎結構時的複雜問題。

其他參考