IIS Uygulama Havuzu Nedir ve Nasıl Yapılandırılır
Üretim ortamında IIS yönetiyorsanız, uygulama havuzlarını (Application Pool) iyi anlamadan sunucunuzu gerçekten yönetiyor sayılmazsınız. Bu yazıda uygulama havuzlarının ne işe yaradığını, nasıl yapılandırılacağını ve gerçek senaryolarda nasıl sorun giderileceğini aktaracağım. Teoriden çok, “bunu neden yapıyorum” sorusunun cevabına odaklanacağız.
Uygulama Havuzu Nedir, Neden Vardır?
IIS’in eski günlerini hatırlayanlar bilir: IIS 5.x döneminde tüm web uygulamaları aynı inetinfo.exe süreci içinde çalışırdı. Bir uygulama çöktüğünde sunucudaki her şey etkilenirdi. IIS 6.0 ile birlikte gelen uygulama havuzu konsepti, bu problemi köklü biçimde çözdü.
Uygulama havuzu, bir veya birden fazla web uygulamasını barındıran, kendi w3wp.exe (Worker Process) süreci olan izole bir çalışma ortamıdır. Her havuz birbirinden bağımsız çalışır. Havuz A çöktüğünde Havuz B’deki uygulamalar etkilenmez. Bu izolasyon hem güvenlik hem de kararlılık açısından kritiktir.
Bunu şöyle düşünün: Aynı sunucuda hem kurumsal ERP’nizi hem de basit bir tanıtım sitesini barındırıyorsunuz. ERP’niz bellek sızıntısı yapıp çöktüğünde, tanıtım siteniz ayakta kalmaya devam eder. Uygulama havuzları tam olarak bunu sağlar.
Uygulama Havuzu Mimarisi
IIS’in işleyişini anlamak için birkaç bileşeni bilmek gerekiyor:
- HTTP.sys: Kernel modda çalışan, HTTP isteklerini alan sürücü
- WAS (Windows Process Activation Service): Havuzları ve worker process’leri yöneten servis
- w3wp.exe: Asıl uygulama kodunu çalıştıran worker process
- Application Pool Identity: Worker process’in çalıştığı Windows hesabı
Bir istek geldiğinde HTTP.sys onu alır, WAS’a iletir, WAS ilgili havuzun worker process’ini başlatır (zaten çalışmıyorsa) ve isteği işlettirmek üzere oraya yönlendirir. Bu zinciri anlamak, sorun giderme süreçlerini çok kolaylaştırır.
PowerShell ile Uygulama Havuzu Yönetimi
IIS Manager arayüzü güzel ama gerçek üretim ortamında işleri scriptlerle hallediyorsunuz. IIS yönetimi için WebAdministration modülünü kullanıyoruz.
Önce modülü import edelim ve mevcut havuzları listeleyelim:
Import-Module WebAdministration
# Tüm uygulama havuzlarını listele
Get-ChildItem IIS:AppPools
# Belirli bir havuzun detaylarını gör
Get-Item "IIS:AppPoolsDefaultAppPool" | Select-Object *
Yeni bir uygulama havuzu oluşturmak:
# Temel havuz oluşturma
New-Item "IIS:AppPoolsUretimHavuzu"
# Ayarlarla birlikte oluşturma
New-Item "IIS:AppPoolsUretimHavuzu" | Set-ItemProperty -Name "managedRuntimeVersion" -Value "v4.0"
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" -Name "managedPipelineMode" -Value "Integrated"
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" -Name "startMode" -Value "AlwaysRunning"
.NET CLR Sürümü ve Pipeline Modu
İki kritik ayar var, bunları yanlış yapmak en sık karşılaşılan sorunların başında geliyor.
.NET CLR Sürümü
- v4.0: .NET Framework 4.x uygulamaları için
- v2.0: .NET Framework 2.0, 3.0, 3.5 uygulamaları için
- No Managed Code: PHP, Node.js (iisnode üzerinden) veya sadece statik içerik sunan siteler için
ASP.NET Core dikkat: ASP.NET Core uygulamaları “No Managed Code” seçeneğiyle çalışır çünkü .NET runtime’ı IIS dışında yönetilir. Bunu bilmeyenler saatlerce neden hata aldıklarını anlamaya çalışır.
Pipeline Modu
- Integrated: IIS ve ASP.NET pipeline’ları birleşik, daha performanslı. Modern uygulamalar için bu.
- Classic: Eski ISAPI uygulamaları için. 2024’te hâlâ Classic mode kullananlar var mı diye sormayın, var.
# ASP.NET Core uygulaması için havuz yapılandırması
$poolName = "AspNetCoreHavuzu"
New-Item "IIS:AppPools$poolName"
Set-ItemProperty "IIS:AppPools$poolName" -Name "managedRuntimeVersion" -Value ""
Set-ItemProperty "IIS:AppPools$poolName" -Name "managedPipelineMode" -Value "Integrated"
# Havuzu başlat
Start-WebAppPool -Name $poolName
Write-Host "$poolName havuzu oluşturuldu ve başlatıldı."
Process Recycling: Havuz Yeniden Başlatma Stratejileri
Bu konu yöneticilerin en çok gözden kaçırdığı ama üretimde can yakabilen ayarlardan biri. IIS varsayılan olarak her 1740 dakikada (29 saat) bir worker process’i yeniden başlatır. Neden? Bellek sızıntılarına, resource leak’lere karşı koruyucu bir önlem.
Ama bu varsayılan değer çoğu üretim ortamı için uygun değil. Sabah 3’te kimsenin bağlı olmadığı saatte recycle olmasını istersiniz, rastgele bir anda değil.
# Recycle ayarlarını yapılandır
$poolName = "UretimHavuzu"
# Zamana bağlı recycle'ı devre dışı bırak
Set-ItemProperty "IIS:AppPools$poolName" `
-Name "recycling.periodicRestart.time" `
-Value "00:00:00"
# Belirli saatte recycle için schedule ekle (gece 02:30)
$recycleTime = "02:30:00"
Clear-ItemProperty "IIS:AppPools$poolName" `
-Name "recycling.periodicRestart.schedule"
New-ItemProperty "IIS:AppPools$poolName" `
-Name "recycling.periodicRestart.schedule" `
-Value @{value=$recycleTime}
# Bellek limitini ayarla (MB cinsinden, 0 = limitsiz)
Set-ItemProperty "IIS:AppPools$poolName" `
-Name "recycling.periodicRestart.privateMemory" `
-Value 1048576 # 1 GB
Üretimde recycle’ı sadece belirli bir saatte yapmanızı öneririm. Hem log takibi kolaylaşır hem de kullanıcı trafiği düşükken olur.
Worker Process Sayısı: Web Garden Yapılandırması
Normalde bir uygulama havuzu tek bir w3wp.exe çalıştırır. Ama yüksek trafikli uygulamalarda birden fazla worker process kullanabilirsiniz. Buna Web Garden denir.
Dikkat: Web Garden her sorunun çözümü değil. Session state’i in-memory tutuyorsanız sorun yaratır çünkü farklı process’ler farklı session verisi tutar.
# Worker process sayısını artır (Web Garden)
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "processModel.maxProcesses" `
-Value 4
# Web Garden için session'ı SQL Server'a taşımanız gerekecek
# ya da StateServer kullanmanız
Uygulama Havuzu Kimliği (Identity) Yapılandırması
Güvenlik açısından en kritik ayarlardan biri bu. Worker process’in hangi Windows hesabıyla çalışacağını belirler.
Seçenekler:
- ApplicationPoolIdentity: Otomatik oluşturulan sanal hesap, önerilen
- NetworkService: Ağ kaynaklarına erişim gereken eski uygulamalar için
- LocalService: Kısıtlı yerel servis hesabı
- LocalSystem: Tam sistem yetkisi, kesinlikle önerilmez
- Custom Account: Özel domain veya yerel hesap
# ApplicationPoolIdentity (önerilen, varsayılan)
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "processModel.identityType" `
-Value "ApplicationPoolIdentity"
# Domain hesabıyla çalıştırma (veritabanı bağlantısı için domain hesabı gerekiyorsa)
$credential = Get-Credential
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "processModel.identityType" `
-Value "SpecificUser"
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "processModel.userName" `
-Value "DOMAINiis_servis_hesabi"
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "processModel.password" `
-Value "SifreGelirBuraya"
Üretimde genellikle şu senaryo ile karşılaşırsınız: Uygulama SQL Server’a Windows Authentication ile bağlanıyor ve domain hesabı kullanmak zorunda kalıyorsunuz. Bu durumda SpecificUser kaçınılmaz. Ama bu hesabın yetkilerini minimal tutmaya özen gösterin.
Idle Timeout ve Start Mode Ayarları
Varsayılan olarak IIS, 20 dakika boyunca istek almayan worker process’i kapatır. İlk istek geldiğinde process yeniden başlar ve “cold start” yaşanır. .NET uygulamalarında bu ilk istek bazen 5-10 saniye sürebilir. Kullanıcı “site bozuk” sanıp şikayet açar.
# Idle timeout'u devre dışı bırak
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "processModel.idleTimeout" `
-Value "00:00:00"
# AlwaysRunning modu - havuz her zaman çalışır, istek olmasa bile
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "startMode" `
-Value "AlwaysRunning"
# Recycle sonrası hemen yeni process başlat (Overlapped Recycling)
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "recycling.disallowOverlappingRotation" `
-Value $false
AlwaysRunning modu ile idleTimeout 0 kombinasyonu, kesintisiz çalışması gereken uygulamalar için standart yapılandırmam. Bellek tüketen bir ortamda değilseniz bunu kullanın.
Gerçek Dünya Senaryosu: Çöken Uygulama Havuzunu Tespit Etmek
Sabah 08:30, telefon çalmaya başladı. “Site açılmıyor.” Bunun bir uygulama havuzu problemi mi yoksa başka bir şey mi olduğunu nasıl anlarsınız?
# Tüm havuzların durumunu kontrol et
Get-WebConfiguration system.applicationHost/applicationPools/add |
Select-Object name,
@{n="State";e={(Get-WebAppPoolState $_.name).Value}} |
Format-Table -AutoSize
# Durmuş havuzları bul ve yeniden başlat
Get-ChildItem IIS:AppPools | Where-Object {$_.state -eq "Stopped"} | ForEach-Object {
Write-Host "$($_.name) havuzu durmuş, yeniden başlatılıyor..."
Start-WebAppPool -Name $_.name
Write-Host "$($_.name) başlatıldı."
}
Havuz neden durmuş? Event Viewer’a bakmanız gerekiyor. Uygulama havuzu hataları genellikle şu yerlerde görünür:
- Windows Logs > Application: Worker process crash logları
- Windows Logs > System: WAS ile ilgili hatalar
- Applications and Services Logs > Microsoft > Windows > WAS-ListenerAdapter: Bağlantı sorunları
Rapid Fail Protection devreye girmiş olabilir. 5 dakika içinde 5 kez crash yaşanırsa IIS havuzu otomatik olarak durdurur.
# Rapid Fail Protection ayarları
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "failure.rapidFailProtection" `
-Value $true
# 5 dakika içinde izin verilen maksimum crash sayısı
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "failure.maximumFailures" `
-Value 5
# Crash sonrası otomatik başlatma aksiyonu
Set-ItemProperty "IIS:AppPoolsUretimHavuzu" `
-Name "failure.loadBalancerCapabilities" `
-Value "HttpLevel"
Toplu Yapılandırma Scripti
Birden fazla sunucu yönetiyorsanız, her şeyi elle yapmak hem hataya açık hem de zaman kaybı. İşte standart bir üretim havuzu oluşturan script:
function New-ProductionAppPool {
param(
[string]$PoolName,
[string]$DotNetVersion = "v4.0",
[string]$PipelineMode = "Integrated",
[int]$MaxWorkers = 1,
[string]$RecycleTime = "03:00:00",
[int]$MemoryLimitMB = 0
)
if (Test-Path "IIS:AppPools$PoolName") {
Write-Warning "$PoolName zaten mevcut, atlanıyor."
return
}
New-Item "IIS:AppPools$PoolName" | Out-Null
# Temel ayarlar
Set-ItemProperty "IIS:AppPools$PoolName" -Name "managedRuntimeVersion" -Value $DotNetVersion
Set-ItemProperty "IIS:AppPools$PoolName" -Name "managedPipelineMode" -Value $PipelineMode
Set-ItemProperty "IIS:AppPools$PoolName" -Name "startMode" -Value "AlwaysRunning"
# Process ayarları
Set-ItemProperty "IIS:AppPools$PoolName" -Name "processModel.idleTimeout" -Value "00:00:00"
Set-ItemProperty "IIS:AppPools$PoolName" -Name "processModel.maxProcesses" -Value $MaxWorkers
Set-ItemProperty "IIS:AppPools$PoolName" -Name "processModel.identityType" -Value "ApplicationPoolIdentity"
# Recycle ayarları
Set-ItemProperty "IIS:AppPools$PoolName" -Name "recycling.periodicRestart.time" -Value "00:00:00"
Set-ItemProperty "IIS:AppPools$PoolName" -Name "recycling.disallowOverlappingRotation" -Value $false
if ($RecycleTime -ne "") {
New-ItemProperty "IIS:AppPools$PoolName" `
-Name "recycling.periodicRestart.schedule" `
-Value @{value=$RecycleTime}
}
if ($MemoryLimitMB -gt 0) {
Set-ItemProperty "IIS:AppPools$PoolName" `
-Name "recycling.periodicRestart.privateMemory" `
-Value ($MemoryLimitMB * 1024)
}
Write-Host "$PoolName başarıyla oluşturuldu." -ForegroundColor Green
}
# Kullanım örneği
New-ProductionAppPool -PoolName "ERP_Havuzu" -DotNetVersion "v4.0" -RecycleTime "03:00:00" -MemoryLimitMB 2048
New-ProductionAppPool -PoolName "API_Havuzu" -DotNetVersion "" -RecycleTime "03:30:00"
applicationHost.config Üzerinden Doğrudan Yapılandırma
Bazen GUI ve PowerShell yerine doğrudan XML yapılandırması gerekiyor, özellikle DevOps pipeline’larında. C:WindowsSystem32inetsrvconfigapplicationHost.config dosyası tüm IIS yapılandırmasını barındırır.
# applicationHost.config yedeği al (değişiklik yapmadan önce MUTLAKA)
$tarih = Get-Date -Format "yyyyMMdd_HHmmss"
Copy-Item "C:WindowsSystem32inetsrvconfigapplicationHost.config" `
"C:YedeklerapplicationHost_$tarih.config"
# appcmd ile havuz oluşturma (XML tabanlı yönetim için)
& "$env:SystemRootsystem32inetsrvappcmd.exe" add apppool `
/name:"YeniHavuz" `
/managedRuntimeVersion:"v4.0" `
/managedPipelineMode:"Integrated"
# appcmd ile havuz ayarlarını görüntüle
& "$env:SystemRootsystem32inetsrvappcmd.exe" list apppool "YeniHavuz" /config
Performans İzleme: Havuz Sağlığını Takip Etmek
Uygulama havuzlarını yapılandırmak bir başlangıç, izlemek ise sürekli iş. Performance Monitor’da takip etmeniz gereken temel sayaçlar:
- W3SVC_W3WP > Active Requests: Anlık aktif istek sayısı
- W3SVC_W3WP > % Time in GC: .NET garbage collection süresi, yüksekse bellek sorunu var
- Process > Private Bytes: Her w3wp.exe süreci için private bellek kullanımı
- W3SVC_W3WP > Requests / Sec: Saniyedeki istek sayısı
# Tüm w3wp.exe süreçlerini ve hangi havuza ait olduklarını listele
Get-Process w3wp | ForEach-Object {
$pid = $_.Id
$wmiQuery = Get-WmiObject -Query "SELECT * FROM Win32_Process WHERE ProcessId = $pid"
$cmd = $wmiQuery.CommandLine
[PSCustomObject]@{
PID = $pid
Bellek_MB = [math]::Round($_.WorkingSet64 / 1MB, 2)
CPU_San = [math]::Round($_.TotalProcessorTime.TotalSeconds, 2)
Komut = $cmd
}
} | Sort-Object Bellek_MB -Descending | Format-Table -AutoSize
Bu script ile hangi havuzun ne kadar bellek yediğini anlık olarak görebilirsiniz. Bellek sızıntısı olan havuzu tespit etmek artık çok daha kolay.
Güvenlik Sertleştirme Önerileri
Uygulama havuzlarını güvenli hale getirmek için uyguladığım standart önlemler:
- Her uygulamaya ayrı havuz: Tek havuzda birden fazla uygulama çalıştırmayın, izolasyon kaybedersiniz
- ApplicationPoolIdentity kullanın:
NetworkServiceveya özellikleLocalSystemkullanmayın - 32-bit devre dışı: Gerçekten gerekmiyorsa
Enable32BitAppOnWin64false bırakın - Hata sayfalarını özelleştirin:
detailedErrorsproduction’da kapalı olsun - Minimum yetkili dosya sistemi erişimi: Havuz kimliğine sadece ihtiyacı olan klasörlere okuma/yazma yetkisi verin
# Havuz kimliğine web root erişimi ver
$poolIdentity = "IIS AppPoolUretimHavuzu"
$webRoot = "C:inetpubwwwrootuygulamam"
$acl = Get-Acl $webRoot
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$poolIdentity,
"ReadAndExecute",
"ContainerInherit,ObjectInherit",
"None",
"Allow"
)
$acl.SetAccessRule($accessRule)
Set-Acl $webRoot $acl
Write-Host "Dosya sistemi yetkileri ayarlandı."
Sonuç
IIS uygulama havuzları, düzgün yapılandırıldığında web sunucunuzun hem güvenilirliğini hem de güvenliğini önemli ölçüde artırır. Özetlemek gerekirse, yeni bir uygulama deploy ettiğinizde kendinize şu soruları sorun: Bu uygulama için ayrı bir havuz oluşturmalı mıyım? Doğru .NET sürümü ve pipeline modu seçili mi? Idle timeout ve recycle ayarları üretim ortamına uygun mu? Havuz kimliği minimum yetkiyle mi çalışıyor?
Yıllar içinde IIS sorunlarının büyük çoğunluğunun yanlış yapılandırılmış uygulama havuzlarından kaynaklandığını gördüm. Performans sorunlarının yarısı idle timeout ve cold start kaynaklı, güvenlik olaylarının önemli bir kısmı da aşırı yetkili havuz kimlikleriyle ilgili. Yazıda paylaştığım scriptleri kendi ortamınıza uyarlayın, standart bir havuz yapılandırma şablonu oluşturun ve bunu tüm sunucularda tutarlı biçimde uygulayın. Tutarlılık, sistem yönetiminde her şeyden değerlidir.
