Windows Server’da Kapasite Planlaması ve Raporlama
Kapasite planlaması deyince aklıma hep aynı sahne geliyor: Cuma akşamı 17:30, herkes çıkmak üzere, tam o sırada bir uygulama sunucusu disk dolduğu için çöküyor. Sonra bakıyorsun, son 3 aydır disk kullanımı düzenli olarak artıyormuş ama kimse izlememiş, kimse rapor almamış. İşte bu yazıyı tam da bu yüzden yazıyorum.
Windows Server ortamlarında kapasite planlaması, reaktif değil proaktif bir yaklaşım gerektiriyor. Sadece “şu an ne kadar dolmuş” sorusunu değil, “6 ay sonra nerede olacağız” sorusunu da yanıtlaması gerekiyor. Bu ikisi arasındaki fark, iyi bir sysadmin ile sürekli yangın söndüren birinin farkıdır.
Kapasite Planlamasının Temelleri
Önce şunu netleştirelim: Kapasite planlaması sadece disk ve RAM izlemek değil. CPU, ağ bant genişliği, IOPS, bağlantı havuzları, lisans kullanımı hepsi birer kapasite bileşeni. Ama pratikte çoğu ortamda en kritik üç alan şunlar: depolama, bellek ve işlemci. Bu üçünü doğru ölçemiyorsan geri kalanı zaten anlamsız.
Windows Server’da bu verileri toplamanın birkaç yolu var: Performance Monitor (Perfmon), PowerShell, WMI sorgularla ve üçüncü parti araçlar. Ben genellikle yerleşik araçlarla başlarım, çünkü her ortamda olmak zorunda olan araçlardır.
Performance Monitor ile Veri Toplayıcı Kümeleri
Perfmon’un GUI kısmı çoğu sysadmin’in bildiği yer, ama asıl güç “Data Collector Sets” kısmında. Buradan düzenli aralıklarla veri toplayıp .blg dosyalarına yazabilirsiniz. Sonra bu verileri analiz ederek trend çıkartabilirsiniz.
Logman aracı, komut satırından Data Collector Set oluşturmanızı sağlar:
logman create counter "KapasitePlan_Aylik" `
-c "Processor(_Total)% Processor Time" `
"MemoryAvailable MBytes" `
"LogicalDisk(C:)% Free Space" `
"LogicalDisk(C:)Avg. Disk Queue Length" `
"Network Interface(*)Bytes Total/sec" `
-si 00:05:00 `
-f csv `
-o "C:PerfLogsKapasiteRapor%computername%_perf.csv" `
-b 01/01/2025 00:00:00 `
-e 31/12/2025 23:59:59 `
-rf 30.00:00:00
Bu komut her 5 dakikada bir veri toplar ve 30 gün boyunca çalışır. CSV çıktısı aldığınızda Excel veya Power BI’a doğrudan aktarabilirsiniz.
PowerShell ile Anlık Kapasite Raporu
Anlık durum tespiti için aşağıdaki script temel ihtiyaçları karşılıyor. Birden fazla sunucuya aynı anda bağlanıp veri çekebilirsiniz:
$sunucular = @("SRV-APP01", "SRV-DB01", "SRV-WEB01", "SRV-FILE01")
$rapor = foreach ($sunucu in $sunucular) {
try {
$os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $sunucu -ErrorAction Stop
$cpu = Get-WmiObject -Class Win32_Processor -ComputerName $sunucu
$diskler = Get-WmiObject -Class Win32_LogicalDisk -ComputerName $sunucu -Filter "DriveType=3"
$bellek_toplam_gb = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2)
$bellek_bos_gb = [math]::Round($os.FreePhysicalMemory / 1MB, 2)
$bellek_kullanim = [math]::Round((($bellek_toplam_gb - $bellek_bos_gb) / $bellek_toplam_gb) * 100, 1)
foreach ($disk in $diskler) {
$disk_toplam_gb = [math]::Round($disk.Size / 1GB, 2)
$disk_bos_gb = [math]::Round($disk.FreeSpace / 1GB, 2)
$disk_kullanim = [math]::Round((($disk_toplam_gb - $disk_bos_gb) / $disk_toplam_gb) * 100, 1)
[PSCustomObject]@{
Sunucu = $sunucu
Disk = $disk.DeviceID
DiskToplamGB = $disk_toplam_gb
DiskBosGB = $disk_bos_gb
DiskKullanim = "$disk_kullanim%"
BellekToplamGB = $bellek_toplam_gb
BellekKullanim = "$bellek_kullanim%"
Tarih = (Get-Date -Format "yyyy-MM-dd HH:mm")
}
}
}
catch {
Write-Warning "$sunucu sunucusuna bagilanılamadı: $_"
}
}
$rapor | Export-Csv -Path "C:Raporlarkapasite_$(Get-Date -Format 'yyyyMMdd').csv" `
-NoTypeInformation -Encoding UTF8
$rapor | Format-Table -AutoSize
Bu script’i Task Scheduler’a ekleyip her sabah 07:00’de çalıştırırsanız, ofise geldiğinizde güncellemiş bir CSV dosyanız hazır olur.
Trend Analizi: Asıl İş Buradan Başlıyor
Anlık snapshot almak kolay, ama kapasite planlamasının can damarı geçmiş verilere bakarak gelecek tahmini yapmak. Bunun için düzenli veri toplamak ve bunu analiz etmek gerekiyor.
Aşağıdaki script, geçmiş CSV’leri okuyup belirli bir metriğin zaman içindeki değişimini çıkarıyor:
$veri_klasoru = "C:Raporlar"
$hedef_sunucu = "SRV-DB01"
$hedef_disk = "C:"
$tum_veriler = Get-ChildItem -Path $veri_klasoru -Filter "kapasite_*.csv" |
Sort-Object Name |
ForEach-Object {
Import-Csv $_.FullName -Encoding UTF8
}
$filtrelenmis = $tum_veriler |
Where-Object { $_.Sunucu -eq $hedef_sunucu -and $_.Disk -eq $hedef_disk } |
Select-Object Tarih,
@{N="DiskKullanim_Yuzde"; E={ [double]($_.DiskKullanim -replace '%','') }},
DiskBosGB
$filtrelenmis | Sort-Object Tarih |
Format-Table -AutoSize
# Basit lineer trend hesabı
$veri_sayisi = $filtrelenmis.Count
if ($veri_sayisi -ge 7) {
$ilk_deger = ($filtrelenmis | Sort-Object Tarih | Select-Object -First 1).DiskKullanim_Yuzde
$son_deger = ($filtrelenmis | Sort-Object Tarih | Select-Object -Last 1).DiskKullanim_Yuzde
$gunluk_artis = ($son_deger - $ilk_deger) / $veri_sayisi
$kalan_kapasite = 100 - $son_deger
$dolu_olacak_gun = [math]::Round($kalan_kapasite / $gunluk_artis)
Write-Host "`n=== TREND ANALİZİ ===" -ForegroundColor Cyan
Write-Host "Sunucu : $hedef_sunucu" -ForegroundColor White
Write-Host "Disk : $hedef_disk" -ForegroundColor White
Write-Host "Günlük artış : $([math]::Round($gunluk_artis, 2))%" -ForegroundColor Yellow
Write-Host "Tahmini dolma: $dolu_olacak_gun gün sonra" -ForegroundColor Red
}
Bu basit lineer model, düzensiz büyüme gösteren sistemler için yanıltıcı olabilir. Ama %80 doğrulukla “3 ay içinde sıkıntı yaşarsın” diyebilmek bile büyük değer.
SQL Server Logları ve Windows Event Log’dan Kapasite Sinyalleri
Bir veritabanı sunucusunda kapasite problemleri çoğu zaman disk dolmadan önce başka yerlerden sinyal verir. Transaction log büyümesi, TempDB şişmesi, bunlar birer uyarı işareti. Ama Windows Event Log’da da önemli kapasite olayları var:
# Son 7 günde disk doluluğuna dair Event ID'leri tara
$kritik_eventler = Get-WinEvent -ComputerName "SRV-DB01" `
-FilterHashtable @{
LogName = 'System'
Id = @(2013, 2004, 7, 4226)
StartTime = (Get-Date).AddDays(-7)
} -ErrorAction SilentlyContinue
# Event ID referansı:
# 2013: Disk doldu uyarısı
# 2004: Bellek baskısı (Working Set trimming)
# 7 : Hizmet başlatma hatası (kaynak yetersizliği)
if ($kritik_eventler) {
Write-Host "Son 7 günde $($kritik_eventler.Count) kritik kapasite eventi bulundu:" -ForegroundColor Red
$kritik_eventler | Select-Object TimeCreated, Id, Message |
Sort-Object TimeCreated -Descending |
Format-List
} else {
Write-Host "Son 7 günde kritik kapasite eventi yok." -ForegroundColor Green
}
HTML Rapor Oluşturma
Kapasite verilerini yöneticilere veya müşterilere sunmak için HTML rapor şart. Bakımı kolay, e-posta ile gönderilebilir ve özelleştirme esnekliği var:
function New-KapasiteRaporu {
param (
[string[]]$Sunucular,
[string]$CiktiDosyasi = "C:Raporlarkapasite_raporu.html"
)
$html_header = @"
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>Kapasite Raporu - $(Get-Date -Format 'dd.MM.yyyy')</title>
<style>
body { font-family: Segoe UI, sans-serif; font-size: 13px; background: #f5f5f5; }
h2 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 5px; }
.blok { background: white; border-radius: 6px; padding: 16px; margin: 10px 0;
box-shadow: 0 1px 4px rgba(0,0,0,.1); }
.kritik { color: #e74c3c; font-weight: bold; }
.uyari { color: #f39c12; font-weight: bold; }
.iyi { color: #27ae60; }
</style>
</head>
<body>
<div class="blok">
<h2>Sunucu Kapasite Raporu</h2>
<p>Rapor tarihi: <strong>$(Get-Date -Format 'dd.MM.yyyy HH:mm')</strong></p>
"@
$html_body = ""
foreach ($sunucu in $Sunucular) {
try {
$os = Get-WmiObject Win32_OperatingSystem -ComputerName $sunucu -EA Stop
$diskler = Get-WmiObject Win32_LogicalDisk -ComputerName $sunucu -Filter "DriveType=3"
$bellek_pct = [math]::Round((1 - ($os.FreePhysicalMemory / $os.TotalVisibleMemorySize)) * 100, 1)
$html_body += "<div class='blok'><h3>$sunucu</h3>"
$html_body += "<p>Bellek Kullanımı: <span class='$(if($bellek_pct -gt 90){"kritik"} elseif($bellek_pct -gt 75){"uyari"} else {"iyi"})'>$bellek_pct%</span></p>"
foreach ($disk in $diskler) {
$pct = [math]::Round((1 - ($disk.FreeSpace / $disk.Size)) * 100, 1)
$bos = [math]::Round($disk.FreeSpace / 1GB, 1)
$sinif = if ($pct -gt 90) { "kritik" } elseif ($pct -gt 75) { "uyari" } else { "iyi" }
$html_body += "<p>Disk $($disk.DeviceID): <span class='$sinif'>$pct% dolu</span> | Boş: ${bos} GB</p>"
}
$html_body += "</div>"
}
catch {
$html_body += "<div class='blok'><p class='kritik'>$sunucu : Bağlantı hatası</p></div>"
}
}
$html_footer = "</div></body></html>"
$html_header + $html_body + $html_footer | Out-File $CiktiDosyasi -Encoding UTF8
Write-Host "Rapor oluşturuldu: $CiktiDosyasi" -ForegroundColor Green
}
New-KapasiteRaporu -Sunucular @("SRV-APP01","SRV-DB01","SRV-WEB01")
E-posta ile Otomatik Kapasite Uyarısı
Raporu oluşturmak yetmez, ilgililere ulaşması gerekiyor. Aşağıdaki yapı, disk %85’in üzerine çıktığında otomatik e-posta gönderiyor:
$esik_yuzde = 85
$smtp_sunucu = "mail.sirket.local"
$gonderen = "[email protected]"
$alicilar = @("[email protected]", "[email protected]")
$sunucular = @("SRV-APP01","SRV-DB01","SRV-WEB01","SRV-FILE01")
$uyarilar = @()
foreach ($sunucu in $sunucular) {
try {
$diskler = Get-WmiObject Win32_LogicalDisk -ComputerName $sunucu `
-Filter "DriveType=3" -ErrorAction Stop
foreach ($disk in $diskler) {
$pct = [math]::Round((1 - ($disk.FreeSpace / $disk.Size)) * 100, 1)
if ($pct -ge $esik_yuzde) {
$uyarilar += "UYARI: $sunucu - $($disk.DeviceID) diski $pct% dolu " +
"($([math]::Round($disk.FreeSpace/1GB,1)) GB boş)"
}
}
}
catch {
$uyarilar += "HATA: $sunucu sunucusuna ulaşılamadı."
}
}
if ($uyarilar.Count -gt 0) {
$mesaj_govde = "Otomatik Kapasite Uyarısı`n`nTarih: $(Get-Date -Format 'dd.MM.yyyy HH:mm')`n`n"
$mesaj_govde += $uyarilar -join "`n"
$mesaj_govde += "`n`nLütfen ilgili sunucuları kontrol ediniz."
Send-MailMessage `
-From $gonderen `
-To $alicilar `
-Subject "[KAPASİTE UYARISI] Disk Eşiği Aşıldı - $(Get-Date -Format 'dd.MM.yyyy')" `
-Body $mesaj_govde `
-SmtpServer $smtp_sunucu `
-Encoding UTF8
Write-Host "$($uyarilar.Count) uyarı e-posta ile gönderildi." -ForegroundColor Yellow
}
Büyüme Projeksiyonu ve Kapasite Karar Mekanizması
Veri toplamak ve raporlamak işin teknik kısmı. Ama bütün bunların bir yönetim kararına dönüşmesi için net bir dil lazım. Yöneticiler “disk %78 dolu” yerine “X ay sonra problem yaşayacağız, şimdiden Y TB alırsak Z ay kazanırız” duymak istiyor.
Bunun için kapasite projeksiyonunu şu parametreler üzerinden kurabilirsiniz:
Mevcut kullanım yüzdesi: Anlık ölçüm değil, son 30 günün ortalamasını alın. Ani bir yedekleme işlemi nedeniyle o gün disk dolmuş olabilir.
Büyüme hızı: Haftalık veya aylık bazda hesaplayın. Bazı sistemler mevsimsel büyüme gösterir, e-ticaret siteleri yılın belirli dönemlerinde patlar, muhasebe sistemleri dönem kapanışlarında.
Emniyet tamponu: Hiçbir zaman bir diski %95’e kadar doldurmayın. %80 planlama sınırı, %90 alarm sınırı, %95 kriz sınırı şeklinde çalışmak güvenli bir yaklaşım. NTFS’de sistem dosyaları için rezerv alan, SQL Server log operasyonları için alan, anlık yedeklemeler için alan hep gerekiyor.
Temin süresi: Yeni disk alanı veya sunucu eklemek ne kadar sürüyor? Satın alma, onay süreçleri, kurulum. Bu süreyi geriye sayarak planlama yapın. 3 aylık temin süresi varsa, 3 ay dolmadan önce süreci başlatmak gerekiyor.
Scheduled Task ile Otomasyonu Tamamlamak
Bütün bu script’leri tek seferlik değil düzenli çalışacak şekilde ayarlamak gerekiyor. Task Scheduler’a PowerShell script kaydetmek için:
$eylem = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NonInteractive -NoProfile -ExecutionPolicy Bypass -File C:Scriptskapasite_rapor.ps1"
$tetikleyici_gunluk = New-ScheduledTaskTrigger -Daily -At "07:00"
$tetikleyici_haftalik = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At "08:00"
$ayarlar = New-ScheduledTaskSettingsSet `
-ExecutionTimeLimit (New-TimeSpan -Minutes 30) `
-RestartCount 2 `
-RestartInterval (New-TimeSpan -Minutes 5)
Register-ScheduledTask `
-TaskName "KapasitePlanlamaGunluk" `
-Action $eylem `
-Trigger $tetikleyici_gunluk `
-Settings $ayarlar `
-RunLevel Highest `
-User "DOMAINsvc_monitoring" `
-Description "Günlük kapasite raporu ve uyarı gönderimi"
Write-Host "Zamanlanmış görev oluşturuldu." -ForegroundColor Green
Servis hesabı kullanmak önemli. Kişisel kullanıcı hesabıyla çalışan tasklar, kullanıcı şifre değişikliğinde veya hesap kilitlendiğinde sessizce çalışmayı bırakır ve haber bile vermez.
Sonuç
Windows Server ortamında kapasite planlaması; performans verisi toplamak, saklamak, analiz etmek ve bunu eyleme dönüştürmek zincirinden oluşuyor. Bu yazıda anlattığım yöntemler sıfır bütçeyle, yerleşik araçlarla uygulanabilir. Ek bir lisans veya araç gerekmez.
Başlangıç için minimum uygulanabilir yaklaşım şu şekilde olabilir: Her gün sabah çalışan bir PowerShell script’i, her hafta Pazartesi gönderilen bir HTML e-posta raporu ve kritik eşikler aşıldığında anlık uyarı. Bu üçü bile sizi o Cuma akşamı sürprizinden büyük ölçüde korur.
Daha uzun vadede ise topladığınız verileri Power BI’a bağlayıp görselleştirmek, anomali tespiti için basit istatistiksel modeller kurmak, farklı iş yükleri için ayrı büyüme profilleri çıkarmak değer katacak adımlar. Ama her şeyden önce veri toplamaya başlamak lazım. Geçmişi ölçmeden geleceği tahmin edemezsiniz.
