Udostępnij za pomocą


Tworzenie skryptów w PowerShell i historia wydajności Storage Spaces Direct

W systemie Windows Server 2019 rekordy bezpośrednich miejsc do magazynowania i przechowuje obszerną historię wydajności maszyn wirtualnych, serwerów, dysków, woluminów, kart sieciowych i innych. Historia wydajności jest łatwa do wykonywania zapytań i przetwarzania w programie PowerShell, dzięki czemu można szybko przejść z danych pierwotnych do rzeczywistych odpowiedzi na pytania, takie jak:

  1. Czy były jakieś skoki użycia procesora w zeszłym tygodniu?
  2. Czy jakikolwiek dysk fizyczny wykazuje nieprawidłowe opóźnienie?
  3. Które maszyny wirtualne zużywają obecnie najwięcej operacji we/wy na sekundę dyskowych?
  4. Czy moja przepustowość sieci jest nasycona?
  5. Kiedy ten wolumin wyczerpie przestrzeń dyskową?
  6. W ubiegłym miesiącu, które maszyny wirtualne używały największej ilości pamięci?

Get-ClusterPerf cmdlet jest zaprojektowany do obsługi skryptów. Akceptuje dane wejściowe z poleceń cmdlet, takich jak Get-VM lub Get-PhysicalDisk za pomocą potoku do obsługi skojarzenia, i można przekazać jego dane wyjściowe do poleceń cmdlet użytkowych, takich jak Sort-Object, Where-Object i Measure-Object, aby szybko tworzyć zaawansowane zapytania.

W tym temacie opisano 6 przykładowych skryptów, które odpowiadają na 6 powyższych pytań. Przedstawiają wzorce, które można zastosować do znajdowania szczytów, znajdowania średnich, kreślenia linii trendu, wykrywania odstającego i nie tylko w różnych przedziałach czasu i danych. Są one udostępniane jako bezpłatny kod początkowy do kopiowania, rozszerzania i ponownego użycia.

Note

W przypadku zwięzłości przykładowe skrypty pomijają takie rzeczy jak obsługa błędów, których można oczekiwać od kodu programu PowerShell wysokiej jakości. Są one przeznaczone głównie do inspiracji i edukacji, a nie do użytku produkcyjnego.

Przykład 1: CPU, widzę cię!

W tym przykładzie użyto serii z okresu ClusterNode.Cpu.Usage, aby pokazać maksymalny ("wysoki poziom"), minimalny i średnie użycie procesora dla każdego serwera w klastrze. Wykonuje również prostą analizę kwartylu, aby pokazać, ile godzin użycia procesora przekraczało 25%, 50%i 75% w ciągu ostatnich 8 dni.

Screenshot

Na poniższym zrzucie ekranu widać, że serwer-02 miał niewyjaśniony skok w zeszłym tygodniu:

Zrzut ekranu pokazujący, że serwer-02 miał niewyjaśniony skok w zeszłym tygodniu.

Jak to działa

Wynik z Get-ClusterPerf można płynnie przekazać do wbudowanego polecenia Measure-Object cmdlet, wystarczy określić właściwość Value. Dzięki flagom -Maximum, -Minimum i -Average, Measure-Object daje nam pierwsze trzy kolumny prawie za darmo. Aby przeprowadzić analizę kwartylu, możemy przekazać dane do Where-Object i zliczyć liczbę wartości -Gt (większych niż) 25, 50 lub 75. Ostatnim krokiem jest upiększenie za pomocą funkcji Format-Hours i Format-Percent pomocniczych – z pewnością opcjonalnych.

Script

Oto skrypt:

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

Przykład 2. Ogień, pożar, odstanie opóźnienia

W tym przykładzie użyto serii z przedziału czasowego PhysicalDisk.Latency.Average, aby wyszukać wartości odstające statystyczne, zdefiniowane jako dyski, które mają średnie opóźnienie godzinowe przekraczające +3σ (trzy odchylenia standardowe) powyżej średniej populacji.

Important

Dla zwięzłości, ten skrypt nie implementuje zabezpieczeń przed niską wariancją, nie obsługuje częściowych brakujących danych, nie rozróżnia modelu ani oprogramowania układowego itp. Należy użyć rozsądku i nie polegać wyłącznie na tym skrypcie, aby określić, czy zastąpić dysk twardy. Jest on przedstawiony tylko w celach edukacyjnych.

Screenshot

Na poniższym zrzucie ekranu widać, że nie ma wartości odstających:

Zrzut ekranu przedstawiający brak wartości odstających.

Jak to działa

Najpierw wykluczamy bezczynne lub prawie bezczynne dyski, sprawdzając, czy PhysicalDisk.Iops.Total jest stale -Gt 1. Dla każdego aktywnego dysku twardego tworzymy jego LastHour przedział czasu, składający się z 360 pomiarów w odstępach 10 sekundowych, do Measure-Object -Average, aby uzyskać średnie opóźnienie w ciągu ostatniej godziny. Dzięki temu przygotujemy naszą populację.

Implementujemy powszechnie znaną formułę , aby znaleźć średnią μ i odchylenie σ standardowe populacji. Dla każdego aktywnego dysku twardego porównujemy średnie opóźnienie z średnią populacji i dzielimy przez odchylenie standardowe. Zachowujemy nieprzetworzone wartości, aby móc Sort-Object nasze wyniki, ale używamy funkcji pomocniczych Format-Latency i Format-StandardDeviation do upiększenia tego, co pokażemy – co jest oczywiście opcjonalne.

Jeśli jakikolwiek dysk jest większy niż +3σ, oznaczamy to w Write-Host kolorze czerwonym; jeśli nie, oznaczamy to na zielono.

Script

Oto skrypt:

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."
}

Przykład 3: Hałaśliwy sąsiad? To pisze!

Historia wydajności może również odpowiedzieć na pytania dotyczące tej pory. Nowe pomiary są dostępne w czasie rzeczywistym co 10 sekund. W tym przykładzie użyto serii z przedziału VHD.Iops.TotalMostRecent, aby zidentyfikować najbardziej obciążone (niektóre mogą powiedzieć "najbardziej hałaśliwe") maszyny wirtualne korzystające z największej liczby operacji IOPS magazynu na każdym hoście w klastrze oraz przedstawić podział ich operacji odczytu/zapisu.

Screenshot

Na poniższym zrzucie ekranu widzimy najlepsze 10 maszyn wirtualnych według aktywności pamięci masowej.

Zrzut ekranu przedstawiający 10 najlepszych maszyn wirtualnych według aktywności magazynowania.

Jak to działa

W przeciwieństwie do Get-PhysicalDisk polecenia Get-VM cmdlet nie jest świadome klastra — zwraca tylko maszyny wirtualne na serwerze lokalnym. Aby wykonywać zapytania z każdego serwera równolegle, opakowujemy nasze wywołanie w pliku Invoke-Command (Get-ClusterNode).Name { ... }. Dla każdej maszyny wirtualnej uzyskujemy miary VHD.Iops.Total, VHD.Iops.Readi VHD.Iops.Write . Nie określając parametru -TimeFrame , otrzymujemy MostRecent pojedynczy punkt danych dla każdego z nich.

Tip

Te serie odzwierciedlają sumę działania tej maszyny wirtualnej na wszystkie pliki VHD/VHDX. Jest to przykład, w którym historia wydajności jest automatycznie agregowana dla nas. Aby uzyskać podział na dysk VHD/VHDX, możesz przekierować pojedynczy Get-VHD do Get-ClusterPerf zamiast do maszyny wirtualnej.

Wyniki z każdego serwera są łączone jako $Output, co pozwala nam Sort-Object, a następnie Select-Object -First 10. Zwróć uwagę, że Invoke-Command dodaje do wyników właściwość PsComputerName, wskazującą, skąd pochodzą. Możemy ją wydrukować, aby dowiedzieć się, gdzie działa maszyna wirtualna.

Script

Oto skrypt:

$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

Przykład 4: Jak mówią, "25-gig to nowe 10-gig"

W tym przykładzie NetAdapter.Bandwidth.Total użyto serii z przedziału LastDay czasu, aby wyszukać oznaki nasycenia sieci, zdefiniowane jako >90% teoretycznie maksymalnej przepustowości. Dla każdej karty sieciowej w klastrze porównuje największe obserwowane użycie przepustowości w ciągu ostatniego dnia z podaną szybkością połączenia.

Screenshot

Na poniższym zrzucie ekranu widzimy, że Fabrikam NX-4 Pro #2 osiągnął szczyt w ciągu ostatniego dnia:

Zrzut ekranu pokazujący, że Fabrikam NX-4 Pro #2 osiągnął szczyt popularności w ciągu ostatniego dnia.

Jak to działa

Powtarzamy naszą Invoke-Command sztuczkę z powyższego na każdym serwerze i przekierować do Get-NetAdapter. Po drodze pobieramy dwie istotne właściwości: ciąg taki jak LinkSpeed "10 Gb/s" i nieprzetworzoną liczbę całkowitą Speed taką jak 10000000000. Używamy Measure-Object, aby uzyskać średnią i wartość szczytową z ostatniego dnia (przypomnienie: każda miara w przedziale czasu LastDay reprezentuje 5 minut) i pomnożyć przez 8 bitów na bajt, aby uzyskać bezpośrednie porównanie.

Note

Niektórzy dostawcy, tacy jak Chelsio, obejmują aktywność dostępu do pamięci zdalnej (RDMA) w licznikach wydajności karty sieciowej , więc jest uwzględniona w NetAdapter.Bandwidth.Total serii. Inni, tacy jak Mellanox, mogą nie. Jeśli dostawca tego nie zrobi, po prostu dodaj serię NetAdapter.Bandwidth.RDMA.Total w swojej wersji tego skryptu.

Script

Oto skrypt:

$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

Przykład 5: Spraw, aby magazyn był znów modny!

Aby przyjrzeć się trendom makr, historia wydajności jest przechowywana przez maksymalnie 1 rok. W tym przykładzie użyto serii z przedziału czasowego Volume.Size.Available, aby określić szybkość wypełniania się pamięci masowej i oszacować, kiedy będzie pełna.

Screenshot

Na poniższym zrzucie ekranu widać, że wolumin kopii zapasowej dodaje około 15 GB dziennie:

Zrzut ekranu pokazujący, że wolumin kopii zapasowej dodaje około 15 GB dziennie.

W tym tempie osiągnie swoją pojemność w ciągu kolejnych 42 dni.

Jak to działa

Przedział LastYear czasu zawiera jeden punkt danych dziennie. Mimo że do dopasowania linii trendu potrzebujesz tylko dwóch punktów, w praktyce lepiej jest mieć więcej danych, na przykład z około 14 dni. Służymy Select-Object -Last 14 do konfigurowania tablicy punktów (x, y) dla x w zakresie [1, 14]. Za pomocą tych punktów implementujemy prosty liniowy algorytm najmniejszych kwadratów w celu znalezienia $A i $B parametryzacji linii najlepiej dopasowanej y = ax + b. Zapraszamy do liceum po raz kolejny.

Podzielenie właściwości woluminu SizeRemaining według trendu (nachylenie $A) pozwala nam surowo oszacować, ile dni, przy bieżącym tempie wzrostu magazynu, dopóki wolumin nie zostanie zapełniony. Funkcje Format-Bytespomocnicze , Format-Trendi Format-Days upiększające dane wyjściowe.

Important

To oszacowanie jest liniowe i oparte tylko na najnowszych 14 codziennych pomiarach. Istnieją bardziej zaawansowane i dokładne techniki. Proszę używać zdrowego rozsądku i nie polegać wyłącznie na tym skrypcie, aby zdecydować, czy inwestować w rozszerzenie przechowywania. Jest on przedstawiony tylko w celach edukacyjnych.

Script

Oto skrypt:


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

Przykład 6: Pożeracz pamięci, możesz uciekać, ale nie możesz się ukryć

Ponieważ historia wydajności jest zbierana i przechowywana centralnie dla całego klastra, nigdy nie trzeba łączyć danych z różnych maszyn, bez względu na to, ile razy maszyny wirtualne są przenoszone między hostami. W tym przykładzie VM.Memory.Assigned użyto serii z przedziału LastMonth czasu, aby zidentyfikować maszyny wirtualne zużywające największą ilość pamięci w ciągu ostatnich 35 dni.

Screenshot

Na poniższym zrzucie ekranu zobaczymy 10 pierwszych maszyn wirtualnych według użycia pamięci w zeszłym miesiącu:

Zrzut ekranu programu PowerShell

Jak to działa

Powtarzamy naszą Invoke-Command sztuczkę, przedstawioną powyżej, na Get-VM każdym serwerze. Użyjemy Measure-Object -Average do uzyskania średniej miesięcznej dla każdej maszyny wirtualnej, a następnie za pomocą Sort-Object oraz Select-Object -First 10 uzyskamy nasz ranking. (A może to nasza lista najbardziej poszukiwanych ?)

Script

Oto skrypt:

$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

To wszystko! Miejmy nadzieję, że te przykłady cię zainspirują i pomogą Ci rozpocząć pracę. Dzięki historii wydajności Storage Spaces Direct i zaawansowanemu, przyjaznemu poleceniu cmdlet Get-ClusterPerf, możesz zadawać pytania — i odpowiadać na nie! — złożone pytania dotyczące zarządzania infrastrukturą systemu Windows Server 2019 i monitorowania ich.

Dodatkowe referencje