PowerShell ile Windows Performans Verisi Toplama
Üretim ortamında bir Windows sunucusu aniden yavaşladığında, elinizin altında doğru araçlar yoksa sorunun kaynağını bulmak gerçek bir kabuса dönüşebilir. Görev Yöneticisi’ni açıp “bir şeyler yüksek” demek yetmez, bu bilgiyi kayıt altına almanız, geçmişle karşılaştırmanız ve anlamlı raporlar üretmeniz gerekir. İşte tam bu noktada PowerShell, Windows sunucu yöneticilerinin en güçlü silahı haline gelir.
Bu yazıda sizi teorik bir PowerShell dersinden geçirmeyeceğim. Gerçek ortamlarda karşılaştığım senaryolara, üretimde kullandığım scriptlere ve birkaç acı tecrübeden çıkardığım derslere odaklanacağım. Hazırsanız başlayalım.
Temel Performans Verisi Toplama: Get-Counter ile Başlamak
PowerShell’in yerleşik Get-Counter cmdlet’i, Windows Performance Monitor’ün sunduğu her şeye komut satırından erişmenizi sağlar. Ancak çoğu kişinin bilmediği şey, bu cmdlet’in ne kadar esnek olduğudur.
Önce basit bir CPU kullanım sorgusuyla başlayalım:
# Anlık CPU kullanımını al
Get-Counter 'Processor(_Total)% Processor Time'
# Belirli aralıklarla sürekli örnekleme
Get-Counter 'Processor(_Total)% Processor Time' -SampleInterval 5 -MaxSamples 12
Bu komut 5 saniye aralıklarla 12 örnek alır, yani 1 dakikalık bir CPU profili çıkarır. Peki ya birden fazla sayacı aynı anda izlemek istiyorsanız?
# CPU, RAM ve Disk I/O'yu birlikte izle
$sayaclar = @(
'Processor(_Total)% Processor Time',
'MemoryAvailable MBytes',
'PhysicalDisk(_Total)Disk Reads/sec',
'PhysicalDisk(_Total)Disk Writes/sec',
'Network Interface(*)Bytes Total/sec'
)
Get-Counter -Counter $sayaclar -SampleInterval 10 -MaxSamples 6 |
ForEach-Object {
$_.CounterSamples | ForEach-Object {
[PSCustomObject]@{
Zaman = $_.Timestamp
Sayac = $_.Path
Deger = [Math]::Round($_.CookedValue, 2)
}
}
} | Format-Table -AutoSize
Bunu çalıştırdığınızda anlık olarak güzel bir tablo göreceksiniz. Ama verileri ekrana değil bir dosyaya kaydetmek istiyorsanız, Export-Csv ile kolayca yapabilirsiniz.
WMI ve CIM: Daha Derin Sistem Bilgisi
Get-Counter anlık performans sayaçları için mükemmeldir, ancak donanım bilgisi, kurulu servisler veya proses detayları için WMI/CIM sorguları çok daha güçlüdür. Modern PowerShell’de Get-CimInstance tercih edilmeli, eski Get-WmiObject yerine.
# CPU detayları ve mevcut yük
$cpu = Get-CimInstance -ClassName Win32_Processor
$os = Get-CimInstance -ClassName Win32_OperatingSystem
$toplamRam = [Math]::Round($os.TotalVisibleMemorySize / 1MB, 2)
$kullanilanRam = [Math]::Round(($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / 1MB, 2)
$bosRam = [Math]::Round($os.FreePhysicalMemory / 1MB, 2)
$ramYuzdesi = [Math]::Round(($kullanilanRam / $toplamRam) * 100, 1)
Write-Host "=== SISTEM OZETI ===" -ForegroundColor Cyan
Write-Host "Sunucu : $($env:COMPUTERNAME)"
Write-Host "CPU : $($cpu.Name)"
Write-Host "Cekirdek: $($cpu.NumberOfCores) fiziksel / $($cpu.NumberOfLogicalProcessors) mantiksal"
Write-Host "CPU Yuk : $($cpu.LoadPercentage)%"
Write-Host "RAM : $kullanilanRam GB / $toplamRam GB ($ramYuzdesi%)" -ForegroundColor $(if ($ramYuzdesi -gt 85) {'Red'} else {'Green'})
Write-Host "Bos RAM : $bosRam GB"
Bu script özellikle sabah kontrol rutinlerinde işe yarar. RAM kullanımı yüzde 85’i geçtiğinde otomatik olarak kırmızı renkte uyarı verir.
Disk Performansı: Sadece Doluluk Değil, I/O da Önemli
Sistem yöneticilerinin yaptığı en yaygın hatalardan biri disk izlemeyi sadece doluluk yüzdesiyle sınırlamak. Bir disk yüzde 40 dolu olsa bile yüksek I/O latency nedeniyle sistemin canını yakabilir. İşte kapsamlı bir disk analiz scripti:
# Disk doluluk ve I/O analizi
function Get-DiskPerformans {
param(
[string]$Sunucu = $env:COMPUTERNAME,
[int] $EsikYuzdesi = 80
)
# Disk doluluk bilgisi
$diskler = Get-CimInstance -ComputerName $Sunucu -ClassName Win32_LogicalDisk -Filter "DriveType=3"
foreach ($disk in $diskler) {
$toplamGB = [Math]::Round($disk.Size / 1GB, 1)
$bosGB = [Math]::Round($disk.FreeSpace / 1GB, 1)
$kullanilanGB = $toplamGB - $bosGB
$kullanim = [Math]::Round(($kullanilanGB / $toplamGB) * 100, 1)
$renk = switch ($true) {
($kullanim -ge 90) { 'Red' }
($kullanim -ge $EsikYuzdesi) { 'Yellow' }
default { 'Green' }
}
Write-Host ("[{0}] Toplam: {1}GB | Kullanilan: {2}GB | Bos: {3}GB | Doluluk: {4}%" -f
$disk.DeviceID, $toplamGB, $kullanilanGB, $bosGB, $kullanim) -ForegroundColor $renk
}
# Disk I/O sayaclari
Write-Host "`n--- DISK I/O (5 saniye ortalama) ---" -ForegroundColor Cyan
$ioSayaclar = @(
'PhysicalDisk(*)Avg. Disk sec/Read',
'PhysicalDisk(*)Avg. Disk sec/Write',
'PhysicalDisk(*)Disk Transfers/sec'
)
Get-Counter -Counter $ioSayaclar -SampleInterval 5 -MaxSamples 1 |
Select-Object -ExpandProperty CounterSamples |
Where-Object { $_.InstanceName -ne '_total' } |
ForEach-Object {
Write-Host (" {0}: {1}" -f $_.Path.Split('')[-1], [Math]::Round($_.CookedValue, 4))
}
}
Get-DiskPerformans -EsikYuzdesi 75
Proses Bazlı Kaynak Tüketimi: Suçluyu Bulmak
Sunucu yavaşladığında ilk yapmanız gereken şey hangi prosesin kaynakları tükettiğini tespit etmek. Windows’un Get-Process cmdlet’i oldukça yetersiz kalır bu konuda. Aşağıdaki yaklaşım çok daha bilgi verici:
# CPU ve RAM'i en cok tuketenleri bul
function Get-TopProsesler {
param(
[int]$TopN = 10,
[ValidateSet('CPU','RAM','Ikisi')]
[string]$SiralayaGore = 'CPU'
)
$prosesler = Get-Process | Select-Object `
Name,
Id,
@{N='CPU_Saniye'; E={[Math]::Round($_.CPU, 1)}},
@{N='RAM_MB'; E={[Math]::Round($_.WorkingSet64 / 1MB, 1)}},
@{N='Handle'; E={$_.HandleCount}},
@{N='Thread'; E={$_.Threads.Count}},
@{N='BaslangicZamani'; E={
try { $_.StartTime.ToString('dd.MM.yyyy HH:mm') }
catch { 'N/A' }
}}
switch ($SiralayaGore) {
'CPU' { $prosesler | Sort-Object CPU_Saniye -Descending | Select-Object -First $TopN }
'RAM' { $prosesler | Sort-Object RAM_MB -Descending | Select-Object -First $TopN }
'Ikisi'{
Write-Host "=== CPU Bazli ===" -ForegroundColor Yellow
$prosesler | Sort-Object CPU_Saniye -Descending | Select-Object -First $TopN | Format-Table
Write-Host "=== RAM Bazli ===" -ForegroundColor Yellow
$prosesler | Sort-Object RAM_MB -Descending | Select-Object -First $TopN | Format-Table
}
}
}
Get-TopProsesler -TopN 10 -SiralayaGore 'Ikisi'
Geçen yıl bir müşterinin IIS sunucusunda haftalık memory leak sorunu yaşıyorduk. Bu script sayesinde w3wp.exe prosesinin her Salı gecesi 4 GB’ı aşan RAM tüketimine ulaştığını belgeledik. Pattern netleşince sorun kaynağı bir uygulama havuzu yapılandırmasıydı.
Ağ Performansı İzleme
Ağ sorunları genellikle en zor tespit edilenlerdir çünkü anlık bir ekran görüntüsü yeterli olmaz, trend verisi gerekir.
# Network adapter istatistikleri
function Get-AgPerformans {
$adaptorler = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration |
Where-Object { $_.IPEnabled -eq $true }
$agSayaclar = Get-Counter 'Network Interface(*)*' -SampleInterval 3 -MaxSamples 3 |
Select-Object -ExpandProperty CounterSamples |
Group-Object InstanceName
foreach ($adaptor in $adaptorler) {
Write-Host "`nAdaptor: $($adaptor.Description)" -ForegroundColor Cyan
Write-Host "IP : $($adaptor.IPAddress -join ', ')"
# Ilgili sayaclari goster
$agSayaclar | Where-Object {
$_.Name -like "*$($adaptor.Description.Substring(0, [Math]::Min(20, $adaptor.Description.Length)))*"
} | ForEach-Object {
$ortalama = ($_.Group | Measure-Object -Property CookedValue -Average).Average
$sayacAdi = $_.Group[0].Path.Split('')[-1]
if ($ortalama -gt 0) {
Write-Host (" {0,-40}: {1:N2}" -f $sayacAdi, $ortalama)
}
}
}
}
Get-AgPerformans
Scheduled Task ile Otomatik Performans Kayıt Sistemi
Anlık sorgular güzel, ama asıl güç düzenli veri toplamaktan gelir. Aşağıdaki script, belirli aralıklarla performans verisini CSV dosyasına kaydeden tam otomatik bir sistem kurar:
# Performans veri toplama ve kayit scripti
# C:ScriptsPerfCollector.ps1 olarak kaydedin
param(
[string]$CiktiDizini = "C:PerfLogs",
[int] $OrneklemeSuresi = 60, # Dakika
[int] $OrneklemAraligi = 30 # Saniye
)
# Cikti dizini yoksa olustur
if (-not (Test-Path $CiktiDizini)) {
New-Item -ItemType Directory -Path $CiktiDizini -Force | Out-Null
}
$dosyaAdi = Join-Path $CiktiDizini ("Perf_{0}_{1}.csv" -f $env:COMPUTERNAME, (Get-Date -Format 'yyyyMMdd_HHmm'))
$maxOrnek = [Math]::Ceiling($OrneklemeSuresi * 60 / $OrneklemAraligi)
$sayaclar = @(
'Processor(_Total)% Processor Time',
'MemoryAvailable MBytes',
'MemoryPages/sec',
'PhysicalDisk(_Total)Avg. Disk sec/Read',
'PhysicalDisk(_Total)Avg. Disk sec/Write',
'PhysicalDisk(_Total)Disk Transfers/sec',
'Network Interface(*)Bytes Total/sec',
'SystemProcessor Queue Length'
)
Write-Host "Veri toplama basliyor: $dosyaAdi" -ForegroundColor Green
Write-Host "Sure: $OrneklemeSuresi dk | Aralik: $OrneklemAraligi sn | Toplam ornek: $maxOrnek"
$baslik = "Zaman,CPU_Yuzde,RAM_BOS_MB,Sayfa_Hatalari,Disk_Okuma_Latency,Disk_Yazma_Latency,Disk_IOPS,Ag_Bytes,IslemciKuyrugu"
$baslik | Out-File $dosyaAdi -Encoding UTF8
Get-Counter -Counter $sayaclar -SampleInterval $OrneklemAraligi -MaxSamples $maxOrnek |
ForEach-Object {
$ornekler = $_.CounterSamples
$zaman = $ornekler[0].Timestamp.ToString('yyyy-MM-dd HH:mm:ss')
$degerler = $ornekler | Group-Object {
$_.Path -replace '\\[^\]+', ''
} | ForEach-Object { ($_.Group | Measure-Object -Property CookedValue -Average).Average }
$satir = $zaman + ',' + ($degerler | ForEach-Object { [Math]::Round($_, 4) } | Join-String -Separator ',')
$satir | Out-File $dosyaAdi -Append -Encoding UTF8
Write-Host "[$zaman] Kaydedildi" -ForegroundColor Gray
}
Write-Host "Tamamlandi: $dosyaAdi" -ForegroundColor Green
Bu scripti Scheduled Task olarak kaydetmek için:
# Her sabah 08:00'de calistir, 1 saatlik veri topla
$eylem = New-ScheduledTaskAction -Execute 'PowerShell.exe' `
-Argument '-NonInteractive -File "C:ScriptsPerfCollector.ps1" -OrneklemeSuresi 60'
$tetikleyici = New-ScheduledTaskTrigger -Daily -At '08:00'
$ayarlar = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Hours 2) `
-RestartCount 2 -RestartInterval (New-TimeSpan -Minutes 5)
Register-ScheduledTask -TaskName 'PerfCollector_Sabah' `
-Action $eylem -Trigger $tetikleyici -Settings $ayarlar `
-RunLevel Highest -User 'SYSTEM'
Write-Host "Zamanlanmis gorev olusturuldu." -ForegroundColor Green
Çoklu Sunucu İzleme: Merkezi Yaklaşım
Tek sunucu güzel, ama ortamınızda 20-30 sunucu varsa ne yapacaksınız? PowerShell Remoting bu işi kolaylaştırır:
# Birden fazla sunucudan anlık ozet al
function Get-CokluSunucuOzet {
param(
[string[]]$Sunucular,
[PSCredential]$Kimlik
)
$sonuclar = Invoke-Command -ComputerName $Sunucular -Credential $Kimlik -ScriptBlock {
$os = Get-CimInstance Win32_OperatingSystem
$cpu = Get-CimInstance Win32_Processor
$ramToplam = [Math]::Round($os.TotalVisibleMemorySize / 1MB, 1)
$ramBos = [Math]::Round($os.FreePhysicalMemory / 1MB, 1)
$ramKullanim = [Math]::Round((($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / $os.TotalVisibleMemorySize) * 100, 1)
[PSCustomObject]@{
Sunucu = $env:COMPUTERNAME
CPU_Yuzdesi = $cpu.LoadPercentage
RAM_Toplam = $ramToplam
RAM_Bos = $ramBos
RAM_Yuzde = $ramKullanim
Son_Boot = $os.LastBootUpTime.ToString('dd.MM.yyyy HH:mm')
Uptime_Gun = ([DateTime]::Now - $os.LastBootUpTime).Days
Durum = if ($cpu.LoadPercentage -gt 90 -or $ramKullanim -gt 90) {'KRITIK'} elseif ($cpu.LoadPercentage -gt 70 -or $ramKullanim -gt 75) {'UYARI'} else {'NORMAL'}
}
} -ErrorAction SilentlyContinue
$sonuclar | Sort-Object CPU_Yuzdesi -Descending | ForEach-Object {
$renk = switch ($_.Durum) {
'KRITIK' { 'Red' }
'UYARI' { 'Yellow' }
default { 'Green' }
}
Write-Host ("[{0,-15}] CPU: {1,3}% | RAM: {2,4}GB/{3,4}GB ({4,5}%) | Uptime: {5} gun | {6}" -f
$_.Sunucu, $_.CPU_Yuzdesi, ($_.RAM_Toplam - $_.RAM_Bos), $_.RAM_Toplam, $_.RAM_Yuzde, $_.Uptime_Gun, $_.Durum) `
-ForegroundColor $renk
}
}
# Kullanim
$sunucuListesi = @('WebSrv01', 'WebSrv02', 'DbSrv01', 'AppSrv01')
$kimlik = Get-Credential -Message "Sunucu erisim bilgileri"
Get-CokluSunucuOzet -Sunucular $sunucuListesi -Kimlik $kimlik
Otomatik Uyarı ve Email Bildirimi
Veri toplamak yeterli değil, kritik eşikler aşıldığında haberdar olmanız gerekir:
# Esik tabanli uyari sistemi
function Test-PerformansEsikleri {
param(
[int]$CPU_Esik = 90,
[int]$RAM_Esik = 85,
[int]$Disk_Esik = 80,
[string]$EmailKimden = "[email protected]",
[string]$EmailKime = "[email protected]",
[string]$SMTPSunucu = "smtp.sirket.com"
)
$uyarilar = @()
$sunucu = $env:COMPUTERNAME
$zaman = Get-Date -Format 'dd.MM.yyyy HH:mm:ss'
# CPU kontrolu
$cpuDeger = (Get-Counter 'Processor(_Total)% Processor Time' -SampleInterval 5 -MaxSamples 3 |
Select-Object -ExpandProperty CounterSamples |
Measure-Object -Property CookedValue -Average).Average
$cpuDeger = [Math]::Round($cpuDeger, 1)
if ($cpuDeger -gt $CPU_Esik) {
$uyarilar += "[KRITIK] CPU kullanimi: $cpuDeger% (Esik: $CPU_Esik%)"
}
# RAM kontrolu
$os = Get-CimInstance Win32_OperatingSystem
$ramYuzde = [Math]::Round((($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / $os.TotalVisibleMemorySize) * 100, 1)
if ($ramYuzde -gt $RAM_Esik) {
$bosGB = [Math]::Round($os.FreePhysicalMemory / 1MB, 1)
$uyarilar += "[KRITIK] RAM kullanimi: $ramYuzde% (Bos: $bosGB GB)"
}
# Disk doluluk kontrolu
Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object {
$doluluk = [Math]::Round((($_.Size - $_.FreeSpace) / $_.Size) * 100, 1)
if ($doluluk -gt $Disk_Esik) {
$bosGB = [Math]::Round($_.FreeSpace / 1GB, 1)
$uyarilar += "[UYARI] $($_.DeviceID) dolulugu: $doluluk% (Bos: $bosGB GB)"
}
}
if ($uyarilar.Count -gt 0) {
$govde = @"
Sunucu: $sunucu
Zaman : $zaman
Asagidaki esikler asildi:
$($uyarilar | ForEach-Object { "- $_" } | Out-String)
Bu mesaj otomatik olarak uretilmistir.
"@
Send-MailMessage -From $EmailKimden -To $EmailKime `
-Subject "[$sunucu] Performans Uyarisi - $($uyarilar.Count) sorun tespit edildi" `
-Body $govde -SmtpServer $SMTPSunucu -Encoding UTF8
Write-Host "Uyari emaili gonderildi: $($uyarilar.Count) sorun" -ForegroundColor Red
$uyarilar | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow }
} else {
Write-Host "[$zaman] Tum metrikler normal sinirlar icinde." -ForegroundColor Green
}
}
# Her 15 dakikada bir calistir
Test-PerformansEsikleri -CPU_Esik 90 -RAM_Esik 85 -Disk_Esik 80
Pratik Ipuçları ve Dikkat Edilmesi Gerekenler
Birkaç yıllık deneyimden damıttığım bazı notlar:
- Get-Counter performance overhead’i: Çok sayıda sayacı çok sık sorgulamak sunucunuza yük bindirir. Üretim saatlerinde örnekleme aralığını 30 saniyenin altına düşürmeyin.
- WinRM yapılandırması: Uzak sunucuları sorgulamak için
Enable-PSRemotingve uygun firewall kuralları şart. Bunu önceden test edin, kriz anında yapmaya çalışmayın. - Credential yönetimi: Scriptlerinizde asla plain-text parola kullanmayın.
Get-Credential, Windows Credential Manager veya Secret Management modülünü tercih edin. - Log rotasyonu: CSV dosyalarınız zamanla şişer. Otomatik temizleme için
Get-ChildItem | Where-Object {$_.CreationTime -lt (Get-Date).AddDays(-30)} | Remove-Itemgibi bir temizlik görevi ekleyin. - PowerShell sürüm uyumluluğu:
Join-Stringgibi cmdlet’ler PS 6.2+ gerektiriyor. Eski sunucularda$array -join ','alternatifini kullanın. - Sayaç yolu yerelleştirmesi: Türkçe dil paketi yüklü Windows sunucularda sayaç yolları Türkçe gelebilir. Güvenlik için
Get-Counter -ListSet *ile mevcut sayaçları doğrulayın.
Sonuç
PowerShell ile Windows performans izleme, pahalı lisanslara gerek kalmadan kurumsal düzeyde bir gözlemlenebilirlik altyapısı kurmanızı sağlar. Bu yazıda anlık veri toplamadan zamanlanmış kayıt sistemine, tek sunucudan çoklu ortam izlemeye kadar bir dizi pratik senaryo paylaştım.
Asıl mesaj şu: Veri toplamaya bugün başlayın. Bir sorun yaşamadan önce baseline oluşturmanız, o kriz anında size çok şey kazandırır. “Normalde CPU bu saatte yüzde kaçtaydı?” sorusunun cevabını verebilmek, sorun tespitini saatlerden dakikalara indirir.
Scriptleri doğrudan kopyalamak yerine kendi ortamınıza göre uyarlayın. Her sunucu farklıdır, her uygulama farklı bir profil çizer. PowerShell size ham gücü veriyor, bu gücü doğru yönlendirmek sizin işiniz.
