Windows’ta Servis İzleme ve Otomatik Yeniden Başlatma

Bir Cuma akşamı saat 23:00’da telefonun çalması ve “site çöktü, müşteriler ulaşamıyor” mesajını almak… Bunu yaşayan her sistem yöneticisi bilir, ilk kontrol ettikleri şey genellikle servislerdir. IIS durmuş, SQL Server Agent yeniden başlamamış ya da o kritik uygulama servisi sessiz sedasız ortadan kaybolmuş. Bu yazıda Windows ortamında servis izlemeyi ve otomatik yeniden başlatma mekanizmalarını, gerçek senaryolar üzerinden ele alacağız.

Windows Servis İzlemenin Temelleri

Windows’ta servisler, işletim sisteminin kalbinde çalışan bileşenlerdir. SCM (Service Control Manager) bu servisleri yönetir ama SCM’nin varsayılan davranışı her zaman ihtiyacımızı karşılamaz. Bir servis çöktüğünde SCM bunu fark eder, ancak ne yapacağını biz söylemezsek çoğu zaman sadece “çöktü” diye log’a yazar ve oturur.

Servis izleme konusunda iki temel yaklaşım vardır:

  • Reaktif izleme: Servis zaten düştükten sonra müdahale
  • Proaktif izleme: Servis düşmeden önce sinyalleri yakalamak

Çoğu ortamda sadece reaktif izleme yapılıyor. Bu yazıda her ikisini de nasıl yapılandıracağımızı göreceğiz.

SC Komutu ile Servis Kurtarma Ayarları

Windows’un yerleşik sc komutu, servislere hata kurtarma politikası tanımlamanın en hızlı yoludur. Pek çok sysadmin bu özelliği bilmesine rağmen production ortamlarında yapılandırmadan bırakır. Kötü bir alışkanlık.

Temel sözdizimi şu şekilde:

sc failure "ServisAdi" reset= 86400 actions= restart/5000/restart/10000/restart/30000

Bu komut şunu yapar: İlk çökmede 5 saniye bekleyip yeniden başlat, ikinci çökmede 10 saniye bekleyip yeniden başlat, üçüncü çökmede 30 saniye bekleyip yeniden başlat. reset= 86400 parametresi ise hata sayacını 24 saatte bir sıfırlar.

Gerçek bir örnek verelim. Bir müşteri ortamında Windows Server 2019 üzerinde çalışan bir .NET uygulaması servisi haftada birkaç kez memory leak nedeniyle çöküyordu. Kalıcı çözüme kadar köprü olarak şu yapılandırmayı kullandık:

sc failure "UygulamaServisi" reset= 3600 actions= restart/3000/restart/5000/run/10000
sc failureflag "UygulamaServisi" 1

failureflag 1 parametresi önemli: Bu olmadan, servis normal şekilde durdurulduğunda (yani exit code 0 ile) kurtarma aksiyonları tetiklenmez. Servis anormal çıkış yaptığında da tetiklenmesini istiyorsanız bu bayrağı 1 yapmanız gerekiyor.

Mevcut servisin kurtarma ayarlarını görmek için:

sc qfailure "ServisAdi"

PowerShell ile Servis İzleme Script’i

sc komutu tek bir servis için hızlı çözüm üretir ama onlarca sunucu ve servis yönetiyorsanız, merkezi bir izleme script’ine ihtiyacınız var. Aşağıdaki script, belirtilen servisleri izler, durmuş olanları yeniden başlatır ve Windows Event Log’a kayıt düşer.

# ServisIzleme.ps1
# Kritik servisleri izler ve otomatik yeniden baslatir

param(
    [string]$LogSource = "ServisIzleme",
    [string]$KonfigDosyasi = "C:Scriptskritik_servisler.txt"
)

$KritikServisler = @(
    "W3SVC",        # IIS
    "MSSQLSERVER",  # SQL Server
    "WSearch",      # Windows Search
    "Spooler"       # Print Spooler
)

# Event log kaynak yoksa olustur
if (-not [System.Diagnostics.EventLog]::SourceExists($LogSource)) {
    New-EventLog -LogName "Application" -Source $LogSource
}

foreach ($ServisAdi in $KritikServisler) {
    $Servis = Get-Service -Name $ServisAdi -ErrorAction SilentlyContinue
    
    if ($null -eq $Servis) {
        Write-EventLog -LogName "Application" -Source $LogSource `
            -EventId 1002 -EntryType Warning `
            -Message "UYARI: $ServisAdi servisi bu sunucuda bulunamadi."
        continue
    }
    
    if ($Servis.Status -ne "Running") {
        $BaslamaPolitikasi = $Servis.StartType
        
        Write-EventLog -LogName "Application" -Source $LogSource `
            -EventId 1001 -EntryType Error `
            -Message "HATA: $ServisAdi servisi calismıyor (Durum: $($Servis.Status)). Yeniden baslatiliyor..."
        
        try {
            Start-Service -Name $ServisAdi -ErrorAction Stop
            Start-Sleep -Seconds 3
            
            $ServisYeni = Get-Service -Name $ServisAdi
            if ($ServisYeni.Status -eq "Running") {
                Write-EventLog -LogName "Application" -Source $LogSource `
                    -EventId 1000 -EntryType Information `
                    -Message "BASARILI: $ServisAdi servisi yeniden baslatildi."
            }
        }
        catch {
            Write-EventLog -LogName "Application" -Source $LogSource `
                -EventId 1003 -EntryType Error `
                -Message "KRITIK: $ServisAdi servisi baslatılamadi! Hata: $($_.Exception.Message)"
        }
    }
}

Bu script’i Task Scheduler ile her 5 dakikada bir çalıştırırsanız temel bir izleme mekanizmanız olur. Ama dikkat: Çok kısa aralıklarla çalıştırmak bazen servisin kendi kendine toparlanma fırsatını elinden alabilir. 3-5 dakika makul bir aralık.

Task Scheduler ile Otomatik Tetikleme

Script hazır, şimdi bunu otomatik çalıştırmamız gerekiyor. Grafik arayüz yerine PowerShell ile Task Scheduler görevi oluşturalım, böylece ortamdan ortama kopyalamak çok daha kolay:

$Tetikleyici = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Minutes 5) -Once -At (Get-Date)
$Eylem = New-ScheduledTaskAction -Execute "PowerShell.exe" `
    -Argument "-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File C:ScriptsServisIzleme.ps1"
$Ayarlar = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 2) `
    -MultipleInstances IgnoreNew
$Principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest

Register-ScheduledTask -TaskName "KritikServisIzleme" `
    -Trigger $Tetikleyici `
    -Action $Eylem `
    -Settings $Ayarlar `
    -Principal $Principal `
    -Description "Kritik servisleri her 5 dakikada kontrol eder"

-MultipleInstances IgnoreNew parametresine dikkat edin. Önceki çalışma bitmeden yenisi tetiklenirse, yeni görevi yok sayar. Yoksa bir servis başlatılamadığı durumda görevler birikmeye başlar.

NSSM ile Gelişmiş Servis Yönetimi

Bazen üçüncü parti bir uygulamayı Windows servisi olarak kaydetmeniz gerekir ama uygulamanın kendi installer’ı bunu yapmıyor. İşte burada NSSM (Non-Sucking Service Manager) devreye giriyor. Adı biraz sert ama gerçekten işe yarıyor.

NSSM’in en güzel özelliği, yönettiği uygulamalar için gelişmiş restart politikası tanımlayabilmesidir:

# NSSM ile servis kaydetme
nssm install "NodeUygulamasi" "C:Program Filesnodejsnode.exe"
nssm set "NodeUygulamasi" AppDirectory "C:AppsNodeApp"
nssm set "NodeUygulamasi" AppParameters "server.js"

# Otomatik yeniden baslatma ayarlari
nssm set "NodeUygulamasi" AppExit Default Restart
nssm set "NodeUygulamasi" AppRestartDelay 5000

# Log yonetimi
nssm set "NodeUygulamasi" AppStdout "C:LogsNodeAppstdout.log"
nssm set "NodeUygulamasi" AppStderr "C:LogsNodeAppstderr.log"
nssm set "NodeUygulamasi" AppRotateFiles 1
nssm set "NodeUygulamasi" AppRotateSeconds 86400

Log rotation özelliği özellikle önemli. Log’suz bırakan Node.js veya Python uygulamalarını servis olarak çalıştırırken, stdout ve stderr’ı dosyaya yönlendirmek sorun teşhisinde hayat kurtarır.

WMI Event Subscription ile Anlık Bildirim

Yukarıdaki polling tabanlı yaklaşımlar belirli aralıklarla kontrol yapar. Peki servis tam iki kontrol arasında düşerse ne olur? O 5 dakikalık pencerede sisteminiz servisiz çalışır. Daha anlık bir tepki için WMI Event Subscription kullanabilirsiniz.

# WMI Event Subscription - Servis durma aninda tetiklenir
$WQLSorgusu = "SELECT * FROM __InstanceModificationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Service' AND TargetInstance.Name = 'W3SVC' AND TargetInstance.State = 'Stopped'"

Register-WmiEvent -Query $WQLSorgusu -SourceIdentifier "IISServisDurdu" -Action {
    $EtkilenenServis = $Event.SourceEventArgs.NewEvent.TargetInstance
    
    Write-Host "$(Get-Date): $($EtkilenenServis.Name) servisi durdu! Yeniden baslatiliyor..."
    
    $Sonuc = Start-Service -Name $EtkilenenServis.Name -PassThru -ErrorAction SilentlyContinue
    
    if ($Sonuc.Status -eq "Running") {
        Write-Host "$(Get-Date): Servis basariyla yeniden baslatildi."
        Send-MailMessage -To "[email protected]" -From "[email protected]" `
            -Subject "UYARI: IIS Servisi Yeniden Baslatildi" `
            -Body "IIS servisi durdu ve otomatik olarak yeniden baslatildi. Sunucu: $env:COMPUTERNAME" `
            -SmtpServer "mail.sirket.com"
    }
}

WITHIN 5 ifadesi, WMI’ın her 5 saniyede bir durumu kontrol edeceği anlamına gelir. Bu değeri düşürdükçe sistem yükü artar, yükselttikçe tepki süresi uzar. Kritiklik seviyesine göre ayarlayın.

Uzaktan Çoklu Sunucu İzleme

Tek sunucu yönetmiyorsunuzdur büyük ihtimalle. 10, 20, belki 50 sunucu… Her birine ayrı ayrı bağlanıp kontrol etmek hem verimsiz hem de hata yapmaya açık. PowerShell Remoting ile çoklu sunucu izleme:

# Coklu sunucu servis kontrol scripti
$SunucuListesi = @("WEB01", "WEB02", "APP01", "APP02", "DB01")

$KontrolEdilecekServisler = @{
    "WEB01" = @("W3SVC", "WAS")
    "WEB02" = @("W3SVC", "WAS")  
    "APP01" = @("UygulamaServisi", "MessageQueue")
    "APP02" = @("UygulamaServisi", "MessageQueue")
    "DB01"  = @("MSSQLSERVER", "SQLSERVERAGENT")
}

$Sonuclar = Invoke-Command -ComputerName $SunucuListesi -ScriptBlock {
    param($KontrolListesi)
    
    $SunucuAdi = $env:COMPUTERNAME
    $Rapor = @()
    
    foreach ($ServisAdi in $KontrolListesi[$SunucuAdi]) {
        $Servis = Get-Service -Name $ServisAdi -ErrorAction SilentlyContinue
        $Rapor += [PSCustomObject]@{
            Sunucu  = $SunucuAdi
            Servis  = $ServisAdi
            Durum   = if ($Servis) { $Servis.Status.ToString() } else { "BULUNAMADI" }
            Zaman   = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        }
    }
    return $Rapor
    
} -ArgumentList $KontrolEdilecekServisler

# Durumu calismayan servisleri filtrele
$SorunluServisler = $Sonuclar | Where-Object { $_.Durum -ne "Running" }

if ($SorunluServisler.Count -gt 0) {
    Write-Host "=== SORUNLU SERVISLER ===" -ForegroundColor Red
    $SorunluServisler | Format-Table -AutoSize
}

Bu script’i merkezi bir management sunucusundan çalıştırırsanız, tüm ortamı tek bakışta görürsünüz.

Windows Event Log’ları ile Entegrasyon

Servis izlemenin en sık atlanan kısmı: Event Log korelasyonu. Bir servis düşmeden önce genellikle Event Log’da uyarı sinyalleri vardır. Özellikle System ve Application log’larını izlemek, reaktif müdahale yerine proaktif müdahale şansı verir.

# Son 1 saatteki servis ile ilgili kritik eventleri cek
$BaslangicZamani = (Get-Date).AddHours(-1)

$ServisEventleri = Get-WinEvent -FilterHashtable @{
    LogName   = 'System'
    Id        = @(7031, 7034, 7036, 7040)
    StartTime = $BaslangicZamani
} -ErrorAction SilentlyContinue

foreach ($Event in $ServisEventleri) {
    $RenkSecimi = switch ($Event.Id) {
        7031 { "Red" }     # Servis beklenmedik sekilde sonlandi
        7034 { "Red" }     # Servis beklenmedik sekilde sonlandi (baska format)
        7036 { "Yellow" }  # Servis durumu degisti
        7040 { "Cyan" }    # Servis baslangic tipi degistirildi
    }
    
    Write-Host "[$($Event.TimeCreated)] ID:$($Event.Id) - $($Event.Message.Substring(0, [Math]::Min(100, $Event.Message.Length)))" `
        -ForegroundColor $RenkSecimi
}

Event ID’leri ezberlemeye gerek yok ama şunları mutlaka tanıyın:

  • 7031: Servis beklenmedik şekilde sonlandı, kurtarma aksiyonu tetiklendi
  • 7034: Servis beklenmedik şekilde sonlandı
  • 7036: Servisin durumu değişti (başladı/durdu)
  • 7009: Servis başlama zaman aşımına uğradı
  • 7023: Servis hata vererek sonlandı

Bu event’leri bir SIEM veya merkezi log sistemine göndermek, uzun vadeli trend analizi açısından çok değerlidir.

Pratik Senaryo: IIS Uygulama Havuzu İzleme

Bir web ortamında sadece W3SVC servisini izlemek yetmez. IIS Application Pool’ları da düşebilir. Bunlar servis değil, IIS içi yapılar olduğu için sc ile doğrudan izleyemezsiniz. Bunun için şu yaklaşımı kullanabilirsiniz:

# IIS Application Pool izleme ve yeniden baslatma
Import-Module WebAdministration

$UygulamaHavuzlari = Get-ChildItem IIS:AppPools

foreach ($Havuz in $UygulamaHavuzlari) {
    $HavuzDurum = (Get-WebConfigurationProperty -pspath "machine/webroot/apphost" `
        -filter "system.applicationHost/applicationPools/add[@name='$($Havuz.Name)']" `
        -name "state").Value
    
    if ($HavuzDurum -ne "Started") {
        Write-Host "$(Get-Date): $($Havuz.Name) havuzu durmuş! Başlatılıyor..." -ForegroundColor Yellow
        
        try {
            Start-WebAppPool -Name $Havuz.Name
            Write-Host "$(Get-Date): $($Havuz.Name) başarıyla yeniden başlatıldı." -ForegroundColor Green
            
            Write-EventLog -LogName "Application" -Source "IISHavuzIzleme" `
                -EventId 2001 -EntryType Warning `
                -Message "Application Pool yeniden baslatildi: $($Havuz.Name)"
        }
        catch {
            Write-Host "HATA: $($Havuz.Name) başlatılamadı - $($_.Exception.Message)" -ForegroundColor Red
        }
    }
}

Bu script’i çalıştırmadan önce Event Log kaynağını oluşturmayı unutmayın:

New-EventLog -LogName "Application" -Source "IISHavuzIzleme"

Dikkat Edilmesi Gereken Tuzaklar

Otomatik yeniden başlatma mekanizmalarını kurarken birkaç önemli noktayı göz önünde bulundurun.

Sonsuz döngü riski: Bir servis sürekli çöküp başlıyorsa, otomatik yeniden başlatma bu döngüyü sonsuza kadar sürdürebilir. SCM’nin reset parametresi bu riski azaltır ama tamamen ortadan kaldırmaz. Belirli bir sayıdan sonra yeniden başlatma yerine “bildirim gönder ve bekle” aksiyonu almayı düşünün.

Bağımlı servisler: Bazı servisler başlamak için başka servislere ihtiyaç duyar. W3SVC başlamadan WAS başlamaz. Yeniden başlatma sırasını yanlış kurarsanız servis yine başlamaz ama neden başlamadığını anlamak için zaman kaybedersiniz.

Kaynak tüketimi izleme: Memory leak yaşayan bir servis yeniden başlasa bile aynı problemi yaşamaya devam edecektir. Otomatik yeniden başlatmayı geçici bir çözüm olarak kullanırken, asıl problemi araştırmayı ihmal etmeyin.

Bakım pencereleri: Gece yarısı yamalar uygulandığında servisler kasıtlı olarak durdurulur. Eğer izleme script’iniz bu sırada çalışırsa, bakım sırasında durdurulmuş bir servisi yeniden başlatmaya çalışabilir. Script’lerinize maintenance mode desteği eklemek küçük ama kritik bir detay.

Sonuç

Windows’ta servis izleme ve otomatik yeniden başlatma, tek bir araç veya yöntemle çözülecek bir mesele değildir. SCM’nin yerleşik kurtarma mekanizmaları temel koruma sağlar, PowerShell script’leri esneklik ve merkezi yönetim kazandırır, WMI Event Subscription anlık tepki verir, NSSM ise üçüncü parti uygulamaları düzgünce sarmalamaya yarar.

İdeal bir ortamda bu katmanları üst üste koymak en sağlıklı yaklaşımdır: SCM kurtarma aksiyonları her serviste yapılandırılmış, merkezi izleme script’i her 5 dakikada çalışıyor, kritik servisler için WMI event subscription aktif, tüm event’ler merkezi log sistemine akıyor.

Ama bunların hepsinden önce şunu yapın: Hangi servislerin gerçekten kritik olduğunu belirleyin. Her şeyi izlemeye çalışmak, hiçbir şeyi düzgün izlememekle eşdeğerdir. Ortamınız için kritik olan 5-10 servisi iyi izlemek, 50 servisi yarım yamalak izlemekten çok daha değerlidir. O Cuma gecesi telefon sizi uyandırdığında, hazırlıklı olmanın farkını anlarsınız.

Bir yanıt yazın

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