IIS’te Uygulama Crash Sorunlarını Çözme

Üretim ortamında IIS üzerinde çalışan bir uygulama çöktüğünde, ilk birkaç dakika gerçekten kritik. Telefon çalıyor, ekip Slack’te birbirini ping’liyor, müşteriler şikayet ediyor. Bu baskı altında doğru adımları atmak için önceden hazırlıklı olmak şart. Bu yazıda IIS uygulama crash sorunlarını sistematik bir şekilde nasıl çözeceğimizi, hangi araçları nerede kullanacağımızı ve gerçek senaryolarda nelerle karşılaşabileceğimizi ele alacağız.

IIS Crash Türlerini Anlamak

Her crash aynı değil. IIS ortamında üç temel crash kategorisiyle karşılaşırsınız:

Worker Process (w3wp.exe) çökmesi: Application Pool’un kendisi duruyor. Event Log’da genellikle “A worker process with process id of X serving application pool X has failed to respond to a ping” ya da benzer mesajlar görürsünüz.

Uygulama havuzu yeniden başlaması: Crash değil ama crash gibi görünüyor. Scheduled recycle, memory limit, request queue limit gibi nedenlerle pool yeniden başlıyor.

Uygulama içi exception: w3wp.exe ayakta duruyor ama uygulama 500 Internal Server Error döndürüyor. Bu genellikle unhandled exception veya konfigürasyon sorunudur.

Bu ayrımı net yapmadan troubleshooting yapmak, karanlıkta el yordamıyla ilerlemek gibidir.

Event Log ile İlk Triage

Crash olduğunda ilk durağım her zaman Event Viewer. Ama Windows Event Log’unu UI’dan tıklamak yerine PowerShell ile bakmak çok daha hızlı:

# Son 1 saatteki IIS ile ilgili kritik olayları listele
Get-EventLog -LogName Application -EntryType Error,Warning -Newest 50 |
    Where-Object { $_.Source -match "IIS|W3SVC|WAS|ASP.NET" } |
    Select-Object TimeGenerated, Source, EventID, Message |
    Format-List

Özellikle bakmanız gereken Event ID’ler:

  • 5002: Application Pool devre dışı bırakıldı (çok fazla crash sonrası)
  • 5011: Rapid fail protection devreye girdi
  • 1309: ASP.NET unhandled exception
  • 1000: Application Error (w3wp.exe crash)

WAS (Windows Activation Service) loglarına da bakmayı unutmayın:

# WAS loglarını kontrol et
Get-EventLog -LogName System -Source "WAS" -Newest 20 |
    Select-Object TimeGenerated, EventID, Message |
    Format-List

Failed Request Tracing (FREB) Kurulumu

FREB, IIS’in en güçlü diagnostic aracı ama çoğu sysadmin bunu ya bilmiyor ya da kurmakla uğraşmıyor. Üretimde bir crash yaşandıktan sonra “keşke FREB açık olsaydı” demek istemiyorsanız şimdiden aktif edin.

# FREB özelliğini Windows Feature olarak ekle
Install-WindowsFeature Web-Http-Tracing

# Belirli bir site için FREB'i PowerShell ile aktif et
Import-Module WebAdministration
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.applicationHost/sites/site[@name='MySite']/traceFailedRequestsLogging" `
    -name "enabled" -value "True"

Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.applicationHost/sites/site[@name='MySite']/traceFailedRequestsLogging" `
    -name "directory" -value "C:inetpublogsFailedReqLogFiles"

Web.config üzerinden de yapılandırabilirsiniz. 500 hataları için tüm içeriği yakalamak istiyorsanız:

<system.webServer>
  <tracing>
    <traceFailedRequests>
      <add path="*">
        <traceAreas>
          <add provider="ASP" verbosity="Verbose" />
          <add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
          <add provider="ISAPI Extension" verbosity="Verbose" />
          <add provider="WWW Server" areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module,Rewrite,FastCGI" verbosity="Verbose" />
        </traceAreas>
        <failureDefinitions timeTaken="00:00:30" statusCodes="500-599" />
      </add>
    </traceFailedRequests>
  </tracing>
</system.webServer>

Process Dump Alma: Crash Anını Yakalamak

Crash sonrası en değerli şey memory dump. Ama crash anında dump almak için önceden konfigürasyon yapmak gerekiyor. Windows Error Reporting (WER) bunu otomatik yapabilir:

# Registry üzerinden WER dump konfigürasyonu
$WERPath = "HKLM:SOFTWAREMicrosoftWindowsWindows Error ReportingLocalDumpsw3wp.exe"
New-Item -Path $WERPath -Force
Set-ItemProperty -Path $WERPath -Name "DumpFolder" -Value "C:CrashDumps"
Set-ItemProperty -Path $WERPath -Name "DumpCount" -Value 10
Set-ItemProperty -Path $WERPath -Name "DumpType" -Value 2  # Full dump

# Dump klasörünü oluştur
New-Item -ItemType Directory -Path "C:CrashDumps" -Force

DumpType değerleri:

  • 0: Custom dump
  • 1: Mini dump
  • 2: Full dump (bellek analizi için bu şart)

Crash olmadan önce, yani sorun oluşurken dump almak istiyorsanız ProcDump kullanın:

# Uygulama havuzunun w3wp.exe process ID'sini bul
C:WindowsSystem32inetsrvappcmd.exe list wp

# ProcDump ile crash anında dump al (exception tabanlı)
procdump.exe -e 1 -f "" -ma -w w3wp.exe C:CrashDumps

# CPU %90'ı geçince dump al (hang senaryoları için)
procdump.exe -c 90 -s 10 -n 3 -ma <PID> C:CrashDumps

Application Pool Rapid Fail Protection

Bir Pool sürekli crash olup “disabled” durumuna düşüyorsa, hemen yeniden başlatmak yerine önce rapid fail protection ayarlarına bakın:

# Application Pool konfigürasyonunu görüntüle
Get-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.applicationHost/applicationPools/add[@name='MyAppPool']/failure" `
    -name "*"

# Rapid fail protection ayarını değiştir (test için geçici olarak devre dışı bırak)
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.applicationHost/applicationPools/add[@name='MyAppPool']/failure" `
    -name "rapidFailProtection" -value "False"

Uyarı: Rapid fail protection’ı devre dışı bırakmak production’da tehlikeli. Sadece root cause’u bulmak için geçici olarak yapın ve sorunu çözdükten sonra mutlaka geri açın.

Rapid fail protection parametreleri:

  • rapidFailProtectionInterval: Kaç dakikalık pencere içinde crash sayısını sayacağı (varsayılan 5 dakika)
  • rapidFailProtectionMaxCrashes: Bu pencerede kaç crash sonrası pool’u devre dışı bırakacağı (varsayılan 5)

Memory Leak ve Yüksek Bellek Kullanımı

Crash’lerin önemli bir kısmı aslında OutOfMemoryException’dan kaynaklanıyor. Application Pool’un memory limit’ini aşınca IIS pool’u recycle ediyor ve bu crash gibi görünüyor.

Önce mevcut durumu anlayalım:

# Tüm w3wp.exe process'lerinin bellek kullanımını görüntüle
Get-Process w3wp | Select-Object Id, ProcessName,
    @{N="Memory(MB)";E={[math]::Round($_.WorkingSet64/1MB,2)}},
    @{N="AppPool";E={(Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE ProcessId=$($_.Id)").CommandLine}} |
    Sort-Object "Memory(MB)" -Descending

Memory limit konfigürasyonu:

# Application Pool için bellek limiti ayarla (MB cinsinden)
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.applicationHost/applicationPools/add[@name='MyAppPool']/recycling/periodicRestart" `
    -name "memory" -value 1024000  # 1GB (KB cinsinden)

# Private memory limit (daha doğru ölçüm)
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.applicationHost/applicationPools/add[@name='MyAppPool']/recycling/periodicRestart" `
    -name "privateMemory" -value 1024000

Gerçek bir senaryodan: Bir müşteri uygulaması her gece 03:00’te crash oluyordu. Event log’a baktık, 1000 numaralı event, w3wp.exe crash. ProcDump ile dump aldık. WinDbg ile analiz edince büyük bir List koleksiyonunun belleği doldurup atmadığı ortaya çıktı. Sorun, gece çalışan bir batch job’ın veriyi sayfalamadan tek seferde çekmesiydi. Application Pool’u hemen private memory limit ile kısıtladık, sorunu bir gecede çözmedik ama en azından tüm sunucuyu çökertmekten kurtardık. Asıl fix koda gitti.

WinDbg ile Crash Dump Analizi

Dump almak bir şey, analiz etmek başka bir iş. WinDbg (Windows Debugger) ve SOS (Son of Strike) extension’ı .NET crash analizinin temel araçlarıdır.

# WinDbg ile dump dosyasını aç (Windbg Preview'dan çalıştır)
windbg.exe -z C:CrashDumpsw3wp.exe.1234.dmp

# WinDbg içinde SOS extension'ı yükle (.NET Framework için)
.loadby sos clr

# Exception zincirini göster
!analyze -v

# Tüm managed thread'lerin stack trace'ini listele
!threads
~*e!clrstack

# Heap'teki nesne dağılımını göster (memory leak analizi)
!dumpheap -stat

# En çok yer kaplayan tipleri göster
!dumpheap -stat -min 1000000

SOS yüklenmiyor veya hata veriyorsa, .NET Core / .NET 5+ uygulamalar için dotnet-dump kullanın. Çok daha kolay:

# dotnet-dump tool'u yükle
dotnet tool install --global dotnet-dump

# Çalışan process'ten dump al
dotnet-dump collect -p <PID> -o C:CrashDumpsapp.dmp

# Dump'ı analiz et
dotnet-dump analyze C:CrashDumpsapp.dmp

# Analiz modunda kullanışlı komutlar
> clrthreads
> clrstack
> dumpheap -stat
> gcroot <adres>

HTTP.sys Hataları ve Kernel Seviyesi Sorunlar

Bazen crash IIS seviyesinde değil, HTTP.sys kernel driver seviyesinde oluşur. Bu durumda netsh komutu ile HTTP.sys loglarına bakmanız gerekir:

# HTTP.sys error log'larını etkinleştir
netsh http set iplisten

# Mevcut HTTP.sys konfigürasyonunu göster
netsh http show servicestate

# HTTP.sys hata loglarını görüntüle
netsh http show errors

# HTTP request queue durumunu kontrol et
netsh http show requestq

HTTP.sys seviyesinde sıkça görülen durum kodları:

  • 503.2: Concurrent request limit aşıldı
  • 503.3: ASP.NET request queue doldu
  • 400: Malformed request, header boyutu gibi sorunlar

Request queue limitini ayarlamak için:

# Application Pool kuyruk uzunluğunu artır (varsayılan 1000)
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.applicationHost/applicationPools/add[@name='MyAppPool']" `
    -name "queueLength" -value 5000

Log Analizi ile Pattern Tespiti

IIS access log’ları crash pattern’larını bulmak için altın değerinde. Ama ham log dosyasını okumak yerine PowerShell ile analiz edin:

# IIS log dosyasından 500 hatalarını çek ve analiz et
$logPath = "C:inetpublogsLogFilesW3SVC1u_ex$(Get-Date -Format 'yyMMdd').log"

$errors500 = Get-Content $logPath |
    Where-Object { $_ -notmatch "^#" } |
    ConvertFrom-Csv -Delimiter " " -Header @(
        "date","time","s-ip","cs-method","cs-uri-stem","cs-uri-query",
        "s-port","cs-username","c-ip","cs-user-agent","cs-referer",
        "sc-status","sc-substatus","sc-win32-status","time-taken"
    ) |
    Where-Object { $_.("sc-status") -eq "500" }

# En çok 500 veren endpoint'leri listele
$errors500 | Group-Object "cs-uri-stem" |
    Sort-Object Count -Descending |
    Select-Object Count, Name |
    Select-Object -First 10

# Zaman bazlı dağılım (crash anını bul)
$errors500 | Group-Object time |
    Sort-Object Count -Descending |
    Select-Object Count, Name |
    Select-Object -First 10

Bu analiz gerçekten fark yaratabiliyor. Bir projede müşteri “uygulama crash oluyor” dedi. Log analizi yaptık, 500 hataların %80’i tek bir API endpoint’inden geliyordu ve tüm bu istekler belirli bir saatte yoğunlaşıyordu. Crash değil, ölçeklenme sorunuydu. Aynı endpoint’e giden isteklere cache ekleyerek sorunu çözdük.

Proaktif Monitoring ve Alerting

Crash olduktan sonra müdahale etmek yerine önceden haber almak istiyorsanız:

# Application Pool durumunu izleyen script
# Bunu Task Scheduler'a ekleyin, her 5 dakikada bir çalışsın

$appPools = Get-WebConfiguration system.applicationHost/applicationPools/add
$stoppedPools = @()

foreach ($pool in $appPools) {
    $state = (Get-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
        -filter "system.applicationHost/applicationPools/add[@name='$($pool.name)']" `
        -name "state").Value

    if ($state -eq "Stopped") {
        $stoppedPools += $pool.name

        # Event Log'a yaz
        Write-EventLog -LogName Application -Source "IISMonitor" `
            -EventId 9999 -EntryType Warning `
            -Message "Application Pool '$($pool.name)' stopped at $(Get-Date)"
    }
}

# E-posta bildirimi (SMTP sunucunuz varsa)
if ($stoppedPools.Count -gt 0) {
    $body = "Durdurulan Application Pool'lar:`n" + ($stoppedPools -join "`n")
    Send-MailMessage -To "[email protected]" -From "[email protected]" `
        -Subject "[ALARM] IIS Application Pool Durdu" `
        -Body $body -SmtpServer "smtp.sirket.com"
}

Bu script’i çalıştırmadan önce Event Source’u kaydedin:

New-EventLog -LogName Application -Source "IISMonitor"

Crash Sonrası Otomatik Kurtarma

Bazı durumlarda crash’in ardından pool’un kendiliğinden kalkmasını isteyebilirsiniz. IIS’in failure action’larını buna göre yapılandırın:

# Crash sonrası Application Pool'u otomatik başlatmak için action ayarla
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.applicationHost/applicationPools/add[@name='MyAppPool']/failure" `
    -name "loadBalancerCapabilities" -value "HttpLevel"

# İlk iki crash için restart, üçüncü için no action
$filter = "system.applicationHost/applicationPools/add[@name='MyAppPool']/failure"
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter $filter -name "autoShutdownExe" -value ""

# Crash action'larını konfigüre et
appcmd.exe set apppool "MyAppPool" /failure.rapidFailProtection:false
appcmd.exe set apppool "MyAppPool" /failure.loadBalancerCapabilities:HttpLevel

Debug Diagnostic Tool (DebugDiag) Kullanımı

DebugDiag, Microsoft’un ücretsiz aracı ve crash analizi için WinDbg’ye alternatif, çok daha kullanıcı dostu. Özellikle .NET memory leak ve crash analizi için DebugDiag Analysis bileşeni müthiş raporlar üretiyor.

DebugDiag kurulduktan sonra CLI ile de kullanabilirsiniz:

# DebugDiag ile crash rule oluştur (crash anında otomatik dump al)
"C:Program FilesDebugDiag 2DebugDiag.exe" /r crashrule /p w3wp.exe /o C:CrashDumps

# Mevcut dump'ı analiz et
"C:Program FilesDebugDiag 2DebugDiag.exe" /r analyze /d C:CrashDumpsw3wp.dmp /o C:CrashReports

Rapor HTML formatında çıkıyor ve exception call stack’ini, memory durumunu, thread durumlarını net bir şekilde gösteriyor. Müşteriye de gösterebilirsiniz, anlaşılabilir.

Sonuç

IIS crash sorunları, üzerinde düşünülmeden atılacak adımlarla çözülmez. Sistematik bir yaklaşım şart: önce Event Log ve FREB ile triage, sonra dump analizi, ardından log pattern tespiti. Her crash türünün kendine özgü çözüm yolu var.

Pratikte en çok zaman kazandıran şeyler şunlar: FREB’i üretimde her zaman aktif tutun, WER dump konfigürasyonunu deployment pipeline’ınızın bir parçası haline getirin, ve uygulama pool’larını monitör eden basit bir script’i task scheduler’a ekleyin. Bunları baştan kurarsanız, bir crash olduğunda “ne yapacağız” sorusuyla değil, “bu dump’ta ne görüyoruz” sorusuyla uğraşırsınız.

Gerçek sorun çözme becerisi, araçları bilmekten çok doğru sırayla kullanmaktan geçiyor. Bir dahaki crash’te paniğe kapılmadan bu adımları izlerseniz, çözüme çok daha hızlı ulaşırsınız.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir