Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
No Windows Server 2019, Storage Spaces Direct registra e armazena um extenso histórico de desempenho para máquinas virtuais, servidores, unidades, volumes, adaptadores de rede e muito mais. O histórico de desempenho é fácil de consultar e processar no PowerShell para que você possa passar rapidamente de dados brutos para respostas reais a perguntas como:
- Houve algum pico de CPU na semana passada?
- Algum disco físico apresenta latência anormal?
- Quais VMs estão consumindo mais IOPS de armazenamento no momento?
- A largura de banda da minha rede está saturada?
- Quando é que este volume vai ficar sem espaço livre?
- No mês passado, quais VMs usaram mais memória?
O Get-ClusterPerf
cmdlet foi criado para scripts. Ele aceita entrada de cmdlets como Get-VM
ou Get-PhysicalDisk
pelo pipeline para lidar com a associação, e você pode canalizar sua saída para cmdlets de utilitários como Sort-Object
, Where-Object
e Measure-Object
para compor rapidamente consultas poderosas.
Este tópico fornece e explica 6 scripts de exemplo que respondem às 6 perguntas acima. Eles apresentam padrões que você pode aplicar para encontrar picos, encontrar médias, traçar linhas de tendência, executar deteção de outlier e muito mais, em uma variedade de dados e períodos de tempo. Eles são fornecidos como código inicial gratuito para você copiar, estender e reutilizar.
Observação
Por uma questão de brevidade, os scripts de exemplo omitem coisas como o tratamento de erros que você pode esperar de um código PowerShell de alta qualidade. Destinam-se principalmente à inspiração e educação e não ao uso da produção.
Amostra 1: CPU, eu vejo você!
Este exemplo usa a série ClusterNode.Cpu.Usage
do período de tempo LastWeek
para mostrar o uso máximo ("pico máximo"), mínimo e médio da CPU para cada servidor no cluster. Ele também faz uma análise de quartil simples para mostrar quantas horas de uso da CPU foi superior a 25%, 50%e 75% nos últimos 8 dias.
Captura de ecrã
Na captura de tela abaixo, vemos que o Server-02 teve um pico inexplicável na semana passada:
Como funciona
A saída de Get-ClusterPerf
encaixa-se bem no cmdlet interno Measure-Object
, bastando especificar a propriedade Value
. Com as suas -Maximum
, -Minimum
, e -Average
bandeiras, Measure-Object
dá-nos as três primeiras colunas quase gratuitamente. Para fazer a análise de quartis, podemos usar Where-Object
para filtrar e contar quantos valores eram -Gt
(maiores que) 25, 50 ou 75. O último passo é embelezar com as funções auxiliares Format-Hours
e Format-Percent
– certamente opcionais.
Guião
Aqui está o roteiro:
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
Amostra 2: Incêndio, incêndio, latência fora do normal
Esta amostra utiliza a série PhysicalDisk.Latency.Average
correspondente ao período de tempo LastHour
para procurar valores estatísticos anómalos, definidos como discos com uma latência média horária acima de +3σ (três desvios-padrão) acima da média da população.
Importante
Por uma questão de brevidade, este script não implementa salvaguardas contra baixa variância, não lida com dados parcialmente ausentes, não distingue por modelo ou firmware, etc. Por favor, exerça bom senso e não confie apenas neste script para determinar se deve substituir um disco rígido. É apresentado aqui apenas para fins educacionais.
Captura de ecrã
Na imagem abaixo, vemos que não há valores atípicos:
Como funciona
Primeiro, excluímos unidades ociosas ou quase ociosas verificando se PhysicalDisk.Iops.Total
está consistentemente -Gt 1
. Para cada HDD ativo, enviamos o seu LastHour
período de tempo, composto por 360 medições realizadas a cada 10 segundos, para Measure-Object -Average
com o objetivo de obter a latência média na hora anterior. Isso configura a nossa população.
Implementamos a fórmula amplamente conhecida para encontrar a média μ
e o desvio σ
padrão da população. Para cada HDD ativo, comparamos a sua latência média com a média da população e dividimos pelo desvio padrão. Mantemos os valores brutos, para que possamos Sort-Object
nossos resultados, mas usamos Format-Latency
e Format-StandardDeviation
funções auxiliares para embelezar o que iremos mostrar – certamente opcional.
Caso um disco exceda +3σ, indicamos em vermelho; caso contrário, em verde.
Guião
Aqui está o roteiro:
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."
}
Amostra 3: Vizinho barulhento? Isso é escrever!
O histórico de desempenho também pode responder a perguntas sobre o momento. Novas medições estão disponíveis em tempo real, a cada 10 segundos. Este exemplo utiliza a série VHD.Iops.Total
do intervalo de MostRecent
para identificar as máquinas virtuais mais activas (alguns podem dizer "mais barulhentas") que consomem mais IOPS de armazenamento, em todos os hosts do cluster, e apresentar a divisão de leitura/gravação da sua atividade.
Captura de ecrã
Na captura de tela abaixo, vemos as 10 principais máquinas virtuais por atividade de armazenamento:
Como funciona
Ao contrário de Get-PhysicalDisk
, o cmdlet Get-VM
não tem consciência de clusters – ele apenas retorna VMs no servidor local. Para consultar a partir de cada servidor em paralelo, envolvemos nossa chamada em Invoke-Command (Get-ClusterNode).Name { ... }
. Para cada VM, obtemos as medições VHD.Iops.Total
, VHD.Iops.Read
e VHD.Iops.Write
. Ao não especificar o -TimeFrame
parâmetro, obtemos o MostRecent
único ponto de dados para cada um.
Sugestão
Essas séries refletem a soma da atividade dessa VM em relação a todos os seus arquivos VHD/VHDX. Este é um exemplo em que o histórico de desempenho está sendo automaticamente agregado para nós. Para obter a divisão por VHD/VHDX, você pode canalizar um indivíduo Get-VHD
em Get-ClusterPerf
vez da VM.
Os resultados de cada servidor se reúnem como $Output
, que podemos Sort-Object
e depois Select-Object -First 10
. Observe que o Invoke-Command
decora os resultados com uma propriedade PsComputerName
que indica de onde eles vieram, e que podemos imprimir para determinar onde a VM está a ser executada.
Guião
Aqui está o roteiro:
$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
Amostra 4: Como dizem, "25-gig é o novo 10-gig"
Este exemplo usa a série NetAdapter.Bandwidth.Total
do período LastDay
para procurar sinais de saturação de rede, definida como >90% de largura de banda máxima teórica. Para cada adaptador de rede no cluster, ele compara o maior uso de largura de banda observado no último dia com sua velocidade de link declarada.
Captura de ecrã
Na imagem abaixo, vemos que um Fabrikam NX-4 Pro #2 atingiu o pico no último dia:
Como funciona
Repetimos o nosso truque mencionado acima em cada servidor e canalizamos para Invoke-Command
. Ao longo do caminho, capturamos duas propriedades relevantes: a sua LinkSpeed
string tal como "10 Gbps" e o seu inteiro bruto Speed
tal como 10000000000. Usamos Measure-Object
para obter a média e o pico do último dia (lembrete: cada medição no período de tempo LastDay
representa 5 minutos) e multiplicamos por 8 bits por byte para obter uma comparação direta.
Observação
Alguns fornecedores, como o Chelsio, incluem a atividade RDMA (acesso remoto direto à memória) em seus contadores de desempenho do adaptador de rede , por isso está incluída na NetAdapter.Bandwidth.Total
série. Outros, como Mellanox, não podem. Se o seu fornecedor não o fizer, basta adicionar a série NetAdapter.Bandwidth.RDMA.Total
na sua versão deste script.
Guião
Aqui está o roteiro:
$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
Amostra 5: Torne o armazenamento moderno novamente!
Para analisar as tendências macro, o histórico de desempenho é mantido por até 1 ano. Este exemplo usa a Volume.Size.Available
série do período de LastYear
tempo para determinar a taxa de enchimento do armazenamento e estimar quando ele estará cheio.
Captura de ecrã
Na imagem abaixo, vemos que o volume de backup está adicionando cerca de 15 GB por dia:
A este ritmo, atingirá a sua capacidade em mais 42 dias.
Como funciona
O LastYear
intervalo de tempo tem um ponto de dados por dia. Embora você precise estritamente de dois pontos para se encaixar em uma linha de tendência, na prática é melhor exigir mais, como 14 dias. Usamos Select-Object -Last 14
para configurar uma matriz de (x, y) pontos, para x no intervalo [1, 14]. Com estes pontos, implementamos o algoritmo de mínimos quadrados lineares simples para encontrar $A
e $B
que parametriza a linha de melhor ajuste y = ax + b. Bem-vindo ao ensino médio novamente.
Dividindo a propriedade do SizeRemaining
volume pela tendência (a inclinação $A
) nos permite estimar grosseiramente quantos dias, na taxa atual de crescimento do armazenamento, até que o volume esteja cheio. As funções auxiliares Format-Bytes
, Format-Trend
e Format-Days
embelezam a saída.
Importante
Esta estimativa é linear e baseia-se apenas nas 14 medições diárias mais recentes. Existem técnicas mais sofisticadas e precisas. Por favor, exerça bom senso e não confie apenas neste script para determinar se deve investir na expansão do seu armazenamento. É apresentado aqui apenas para fins educacionais.
Guião
Aqui está o roteiro:
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
Amostra 6: Consumo excessivo de memória, você pode correr, mas não podes esconder-te
Como o histórico de desempenho é coletado e armazenado centralmente para todo o cluster, você nunca precisa unir dados de máquinas diferentes, não importa quantas vezes as VMs se movam entre hosts. Este exemplo usa a VM.Memory.Assigned
série do período de LastMonth
tempo para identificar as máquinas virtuais que consomem mais memória nos últimos 35 dias.
Captura de ecrã
Na captura de tela abaixo, vemos as 10 principais máquinas virtuais por uso de memória no mês passado:
Como funciona
Repetimos o nosso Invoke-Command
truque, introduzido acima, em Get-VM
todos os servidores. Usamos Measure-Object -Average
para obter a média mensal para cada VM, seguido Sort-Object
por Select-Object -First 10
para obter a nossa tabela de classificação. (Ou talvez seja a nossa lista dos mais procurados ?)
Guião
Aqui está o roteiro:
$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
É isso! Espero que estas amostras o inspirem e o ajudem a começar. Com o histórico de desempenho do Storage Spaces Direct e o poderoso cmdlet amigável Get-ClusterPerf
para scripts, você tem o poder de perguntar – e responder! – perguntas complexas enquanto você gerencia e monitora sua infraestrutura do Windows Server 2019.