使用 PowerShell 和儲存空間直接存取效能歷程記錄編寫腳本
適用於︰Windows Server 2022、Windows Server 2019
在 Windows Server 2019 中, 儲存空間直接存取 記錄並儲存虛擬機器、伺服器、磁片磁碟機、磁片區、網路介面卡等的廣泛 效能歷程記錄 。 效能歷程記錄很容易在 PowerShell 中查詢和處理,因此您可以快速地從 原始資料 移至 實際解答 ,例如:
- 上周是否有任何 CPU 尖峰?
- 是否有任何實體磁片表現出異常延遲?
- 哪些 VM 目前會耗用最多的儲存體 IOPS?
- 我的網路頻寬是否飽和?
- 此磁片區何時會用盡可用空間?
- 在過去一個月中,哪些 VM 使用最多的記憶體?
Cmdlet Get-ClusterPerf
是針對腳本所建置。 它接受類似 Get-VM
或 Get-PhysicalDisk
管線的 Cmdlet 輸入來處理關聯,而且您可以將輸出管線傳送至 、 Where-Object
等 Sort-Object
公用程式 Cmdlet,並 Measure-Object
快速撰寫功能強大的查詢。
本主題提供並說明 6 個回答上述 6 個問題的範例腳本。 這些模式可讓您套用到尋找尖峰、尋找平均值、繪製趨勢線、執行極端值偵測等各種資料和時間範圍。 它們會以免費入門程式碼的形式提供,讓您複製、擴充和重複使用。
注意
為了簡潔起見,範例腳本會省略您可能預期高品質 PowerShell 程式碼的錯誤處理等事項。 它們主要是用於靈感和教育,而不是生產用途。
範例 1:CPU,我看見你!
此範例會 ClusterNode.Cpu.Usage
使用時間範圍中的 LastWeek
數列來顯示叢集中每部伺服器的最大(「高水位線」)、最小和平均 CPU 使用量。 它也會進行簡單的四分位數分析,以顯示過去 8 天內 CPU 使用量超過 25%、50% 和 75% 的時數。
螢幕擷取畫面
在下列螢幕擷取畫面中,我們看到 Server-02 上周出現無法解釋的尖峰:
運作方式
從 Get-ClusterPerf
管道順利輸出到內 Measure-Object
建 Cmdlet 中,我們只指定 Value
屬性。 透過它的 -Maximum
、 -Minimum
和 -Average
旗標, Measure-Object
我們幾乎免費提供前三個數據行。 若要進行四分位數分析,我們可以使用管線傳送至 Where-Object
和計算有多少值 -Gt
(大於) 25、50 或 75。 最後一個步驟是美化 和 Format-Hours
Format-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:引發、引發、延遲極端值
此範例會 PhysicalDisk.Latency.Average
使用時間範圍中的 LastHour
數列來尋找統計極端值,定義為每小時平均延遲超過 +3σ(三個標準差)高於母體平均值的磁片磁碟機。
重要
為了簡潔起見,此腳本不會針對低變異數實作保護,不會處理部分遺漏的資料,也不會區分模型或韌體等。請練習良好的判斷,不要只依賴此腳本來判斷是否要更換硬碟。 這裡僅供教育之用。
螢幕擷取畫面
在下列螢幕擷取畫面中,我們看到沒有極端值:
運作方式
首先,我們會檢查是否 PhysicalDisk.Iops.Total
一致 -Gt 1
地排除閒置或幾乎閒置的磁片磁碟機。 針對每個作用中的 HDD,我們會使用管線傳送時間 LastHour
範圍,其時間範圍由 10 秒間隔的 360 個測量組成,以 Measure-Object -Average
取得其過去一小時內的平均延遲。 這會設定我們的人口。
我們會實作 廣為人知的公式 ,以找出母體的平均 μ
和標準差 σ
。 對於每個作用中的 HDD,我們會比較其平均延遲與母體平均,並除以標準差。 我們會保留原始值,因此我們可以 Sort-Object
取得結果,但使用 Format-Latency
和 Format-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 秒即可即時取得新的度量。 此範例會 VHD.Iops.Total
從 MostRecent
時間範圍使用數列來識別最忙碌的虛擬機器,其中有些虛擬機器會耗用叢集中每個主機的最大儲存體 IOPS,並顯示其活動的讀取/寫入明細。
螢幕擷取畫面
在下列螢幕擷取畫面中,我們依儲存體活動看到前 10 部虛擬機器:
運作方式
與 不同的 Get-PhysicalDisk
是 Get-VM
,Cmdlet 不是叢集感知,它只會傳回本機伺服器上的 VM。 若要平行查詢每個伺服器,我們會在 中 Invoke-Command (Get-ClusterNode).Name { ... }
包裝呼叫。 針對每個 VM,我們會取得 VHD.Iops.Total
、 VHD.Iops.Read
和 VHD.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」
此範例會 NetAdapter.Bandwidth.Total
使用時間範圍中的 LastDay
數列來尋找網路飽和的跡象,定義為 > 理論最大頻寬的 90%。 對於叢集中的每個網路介面卡,它會比較過去一天觀察到的最高頻寬使用量與其所陳述的連結速度。
螢幕擷取畫面
在下列螢幕擷取畫面中,我們看到前一天有一個 Fabrikam NX-4 Pro #2 達到尖峰:
運作方式
我們會在每個伺服器上重複上述 Invoke-Command
的技巧, Get-NetAdapter
並將管線傳送至 Get-ClusterPerf
。 一路上,我們會擷取兩個相關屬性:其 LinkSpeed
字串,例如 「10 Gbps」,以及其原始 Speed
整數,例如 10000000000。 我們用來 Measure-Object
從最後一天取得平均值和尖峰(提醒:時間範圍中的每個 LastDay
測量都代表 5 分鐘),並將每個位元組乘以 8 位,以取得蘋果對蘋果的比較。
注意
某些廠商,如 Chelsio,在其網路介面卡 效能計數器中包含 遠端直接記憶體存取 (RDMA) 活動,因此包含在系列中 NetAdapter.Bandwidth.Total
。 其他人,如梅蘭諾克斯,可能不會。 如果您的廠商不這樣做,只要在此腳本版本中新增 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 年。 此範例會 Volume.Size.Available
使用時間範圍中的 LastYear
數列來判斷儲存體填滿的速率,並估計何時會滿。
螢幕擷取畫面
在下列螢幕擷取畫面中,我們看到 備份 磁片區每天新增約 15 GB:
以這個速度,它將在另一個42天內達到其容量。
運作方式
時間 LastYear
範圍每天有一個資料點。 雖然您只需要兩個點才能符合趨勢線,但實際上最好需要更多點,例如 14 天。 我們用來 Select-Object -Last 14
為範圍 [1, 14] 中的 x 設定 x 的陣列 (x, y) 點 。 透過這些點,我們實作簡單的 線性最小平方演算法 來尋找 $A
,並將 $B
最符合 y = ax + b 的線條參數化。 歡迎再次上高中。
將磁片 SizeRemaining
區的屬性除以趨勢(斜率 $A
)可讓我們粗略估計儲存成長率的天數,直到磁片區滿為止。 Format-Bytes
、 Format-Trend
和 Format-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 在主機之間移動多少次,您都不需要將不同機器的資料合併在一起。 此範例會 VM.Memory.Assigned
使用時間範圍中的 LastMonth
數列來識別過去 35 天內耗用最多記憶體的虛擬機器。
螢幕擷取畫面
在下列螢幕擷取畫面中,我們看到上個月記憶體使用量的前 10 部虛擬機器:
運作方式
我們會在每個伺服器上重複上述 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 基礎結構時的複雜問題。
其他參考
- 開始使用 Windows PowerShell
- 儲存空間直接存取概觀 \(部分機器翻譯\)
- 效能歷程記錄