Cron ile Otomatik Sunucu Sağlık Raporu Oluşturma

Sunucuların sağlıklı çalışıp çalışmadığını takip etmek, bir sysadmin’in en temel sorumluluklarından biri. Ama her sabah SSH açıp disk kullanımına, CPU yüküne, servis durumlarına tek tek bakmak hem zaman kaybı hem de hata payı yüksek bir yöntem. İşte tam bu noktada cron devreye giriyor. Düzgün kurgulanmış bir cron job ile sunucundan her gün düzenli sağlık raporu alabilir, sorunları henüz küçükken fark edebilirsiniz.

Bu yazıda sıfırdan bir sunucu sağlık raporu sistemi kuracağız. Bash script yazımından cron zamanlamasına, e-posta bildiriminden log arşivlemeye kadar her adımı gerçek dünya senaryolarıyla ele alacağız.

Neden Otomatik Sağlık Raporu?

Geçen yıl bir müşterimin sunucusunda disk doluluk sorununu ancak uygulama hata vermeye başladığında fark ettik. Oysa iki hafta önceden “/var” partition’ı %85’i geçmişti. Bunu zamanında görebilseydik, temizlik için bolca zamanımız olurdu. Bu tür olaylar yaşandıktan sonra insan otomasyonun değerini daha iyi anlıyor.

Otomatik sağlık raporu şu faydaları sağlar:

  • Proaktif izleme: Sorunları kullanıcılar fark etmeden önce görürsünüz
  • Trend analizi: Zaman içinde disk ve RAM kullanımının nasıl arttığını takip edebilirsiniz
  • Dokümantasyon: Arşivlenmiş raporlar sorun analizinde çok işe yarar
  • Zaman tasarrufu: Manuel kontrol yerine sabah kahvenizi içerken raporu okursunuz

Temel Script Yapısı

Her şey sağlam bir Bash scriptiyle başlıyor. /opt/scripts/ dizini altında çalışacağız çünkü bu konum sistem betikleri için de facto standart haline gelmiştir.

#!/bin/bash
# sunucu_saglik_raporu.sh
# Sunucu sağlık durumunu toplayıp raporlar

set -euo pipefail

# Değişkenler
HOSTNAME=$(hostname -f)
TARIH=$(date '+%Y-%m-%d %H:%M:%S')
TARIH_DOSYA=$(date '+%Y%m%d_%H%M')
RAPOR_DIZIN="/var/log/saglik_raporlari"
RAPOR_DOSYA="${RAPOR_DIZIN}/rapor_${TARIH_DOSYA}.txt"
MAIL_ALICI="[email protected]"
KIRMIZI_ESIK_DISK=85
SARI_ESIK_DISK=70
KIRMIZI_ESIK_RAM=90
KIRMIZI_ESIK_CPU=80

# Rapor dizinini oluştur
mkdir -p "$RAPOR_DIZIN"

echo "============================================" > "$RAPOR_DOSYA"
echo "  SUNUCU SAĞLIK RAPORU" >> "$RAPOR_DOSYA"
echo "  Sunucu: $HOSTNAME" >> "$RAPOR_DOSYA"
echo "  Tarih: $TARIH" >> "$RAPOR_DOSYA"
echo "============================================" >> "$RAPOR_DOSYA"

set -euo pipefail satırına dikkat edin. Bu üç seçenek birlikte scriptin daha güvenli çalışmasını sağlar. -e hata durumunda durur, -u tanımsız değişken kullanımını engeller, -o pipefail ise pipe içindeki komutların hatalarını yakalar.

Sistem Bilgilerini Toplama

Uptime ve Yük Ortalaması

# Uptime ve yük bilgisi
toplam_uptime_bilgisi() {
    echo "" >> "$RAPOR_DOSYA"
    echo "--- SISTEM UPTIME ---" >> "$RAPOR_DOSYA"
    uptime >> "$RAPOR_DOSYA"
    
    # Yük ortalamasını ayrıştır
    LOAD_1=$(uptime | awk '{print $(NF-2)}' | tr -d ',')
    LOAD_5=$(uptime | awk '{print $(NF-1)}' | tr -d ',')
    LOAD_15=$(uptime | awk '{print $NF}')
    CPU_CORE=$(nproc)
    
    echo "CPU Çekirdek Sayısı: $CPU_CORE" >> "$RAPOR_DOSYA"
    echo "Yük Ortalaması (1/5/15 dk): $LOAD_1 / $LOAD_5 / $LOAD_15" >> "$RAPOR_DOSYA"
    
    # Normalize edilmiş yük kontrolü
    LOAD_NORMALIZED=$(echo "$LOAD_1 $CPU_CORE" | awk '{printf "%.0f", ($1/$2)*100}')
    
    if [ "$LOAD_NORMALIZED" -gt "$KIRMIZI_ESIK_CPU" ]; then
        echo "[UYARI] Yük ortalaması kritik seviyede: %${LOAD_NORMALIZED}" >> "$RAPOR_DOSYA"
        UYARI_VAR=1
    fi
}

Disk Kullanımı Kontrolü

Disk kontrolü en kritik parça. Birden fazla partition olan sunucularda her birini tek tek kontrol etmek gerekiyor.

# Disk kullanım kontrolü
disk_kontrolu() {
    echo "" >> "$RAPOR_DOSYA"
    echo "--- DISK KULLANIMI ---" >> "$RAPOR_DOSYA"
    df -h --output=source,size,used,avail,pcent,target | grep -v "tmpfs|udev|overlay" >> "$RAPOR_DOSYA"
    
    echo "" >> "$RAPOR_DOSYA"
    echo "Kritik Disk Uyarıları:" >> "$RAPOR_DOSYA"
    
    # Her partition için kontrol
    df -h --output=pcent,target | grep -v "Use%|tmpfs|udev" | while read -r YUZDE MONTAJ; do
        YUZDE_SAYI=${YUZDE//%/}
        
        if [ "$YUZDE_SAYI" -gt "$KIRMIZI_ESIK_DISK" ]; then
            echo "[KRITIK] $MONTAJ -> %$YUZDE_SAYI dolu!" >> "$RAPOR_DOSYA"
            UYARI_VAR=1
        elif [ "$YUZDE_SAYI" -gt "$SARI_ESIK_DISK" ]; then
            echo "[DIKKAT] $MONTAJ -> %$YUZDE_SAYI dolu" >> "$RAPOR_DOSYA"
        fi
    done
    
    echo "" >> "$RAPOR_DOSYA"
    echo "En Büyük 10 Dizin (/var altında):" >> "$RAPOR_DOSYA"
    du -sh /var/* 2>/dev/null | sort -rh | head -10 >> "$RAPOR_DOSYA"
}

RAM ve Swap Kullanımı

# Bellek kullanım kontrolü
bellek_kontrolu() {
    echo "" >> "$RAPOR_DOSYA"
    echo "--- BELLEK KULLANIMI ---" >> "$RAPOR_DOSYA"
    free -h >> "$RAPOR_DOSYA"
    
    # RAM yüzdesini hesapla
    RAM_TOPLAM=$(free | awk '/^Mem:/{print $2}')
    RAM_KULLANAN=$(free | awk '/^Mem:/{print $3}')
    RAM_YUZDE=$(echo "$RAM_KULLANAN $RAM_TOPLAM" | awk '{printf "%.0f", ($1/$2)*100}')
    
    echo "" >> "$RAPOR_DOSYA"
    echo "RAM Kullanım Yüzdesi: %$RAM_YUZDE" >> "$RAPOR_DOSYA"
    
    if [ "$RAM_YUZDE" -gt "$KIRMIZI_ESIK_RAM" ]; then
        echo "[KRITIK] RAM kullanımı çok yüksek: %$RAM_YUZDE" >> "$RAPOR_DOSYA"
        UYARI_VAR=1
    fi
    
    # Swap kontrolü
    SWAP_TOPLAM=$(free | awk '/^Swap:/{print $2}')
    if [ "$SWAP_TOPLAM" -gt 0 ]; then
        SWAP_KULLANAN=$(free | awk '/^Swap:/{print $3}')
        SWAP_YUZDE=$(echo "$SWAP_KULLANAN $SWAP_TOPLAM" | awk '{printf "%.0f", ($1/$2)*100}')
        echo "Swap Kullanım Yüzdesi: %$SWAP_YUZDE" >> "$RAPOR_DOSYA"
        
        if [ "$SWAP_YUZDE" -gt 50 ]; then
            echo "[DIKKAT] Swap kullanımı yüksek: %$SWAP_YUZDE - RAM yetersiz kalıyor olabilir" >> "$RAPOR_DOSYA"
            UYARI_VAR=1
        fi
    fi
}

Servis Durumu Kontrolü

Kritik servislerin çalışıp çalışmadığını kontrol etmek raporun en önemli bölümlerinden biri. Bu listeyi kendi ortamınıza göre özelleştirmeniz gerekiyor.

# Kritik servis kontrolü
servis_kontrolu() {
    echo "" >> "$RAPOR_DOSYA"
    echo "--- KRITIK SERVIS DURUMLARI ---" >> "$RAPOR_DOSYA"
    
    # Kontrol edilecek servisler listesi
    SERVISLER=(
        "nginx"
        "mysql"
        "postgresql"
        "redis"
        "docker"
        "ssh"
        "fail2ban"
        "ufw"
    )
    
    CALISAN=0
    DURMUS=0
    
    for SERVIS in "${SERVISLER[@]}"; do
        # Servisin sistemde var olup olmadığını kontrol et
        if systemctl list-units --full -all | grep -q "${SERVIS}.service"; then
            DURUM=$(systemctl is-active "$SERVIS" 2>/dev/null)
            
            if [ "$DURUM" = "active" ]; then
                echo "[OK]    $SERVIS -> Çalışıyor" >> "$RAPOR_DOSYA"
                ((CALISAN++))
            else
                echo "[HATA]  $SERVIS -> DURMUS! (Durum: $DURUM)" >> "$RAPOR_DOSYA"
                ((DURMUS++))
                UYARI_VAR=1
                
                # Son 5 log satırını ekle
                echo "        Son loglar:" >> "$RAPOR_DOSYA"
                journalctl -u "$SERVIS" -n 5 --no-pager 2>/dev/null | sed 's/^/        /' >> "$RAPOR_DOSYA"
            fi
        fi
    done
    
    echo "" >> "$RAPOR_DOSYA"
    echo "Özet: $CALISAN servis aktif, $DURMUS servis durmuş" >> "$RAPOR_DOSYA"
}

Güvenlik Kontrolleri

Güvenlik durumunu da rapora eklemek iyi bir pratik. Özellikle başarısız SSH denemeleri ve son giriş yapan kullanıcılar önemli bilgiler içerir.

# Güvenlik durumu kontrolü
guvenlik_kontrolu() {
    echo "" >> "$RAPOR_DOSYA"
    echo "--- GUVENLIK DURUMU ---" >> "$RAPOR_DOSYA"
    
    # Son 24 saatteki başarısız SSH denemeleri
    BASARISIZ_SSH=$(journalctl _SYSTEMD_UNIT=ssh.service --since "24 hours ago" 2>/dev/null | 
        grep -c "Failed password" || echo "0")
    echo "Son 24 saatte başarısız SSH denemesi: $BASARISIZ_SSH" >> "$RAPOR_DOSYA"
    
    if [ "$BASARISIZ_SSH" -gt 100 ]; then
        echo "[UYARI] Yüksek sayıda başarısız SSH girişi tespit edildi!" >> "$RAPOR_DOSYA"
        UYARI_VAR=1
    fi
    
    # En çok deneme yapan IP'ler
    echo "" >> "$RAPOR_DOSYA"
    echo "En çok deneme yapan IP'ler (Top 5):" >> "$RAPOR_DOSYA"
    journalctl _SYSTEMD_UNIT=ssh.service --since "24 hours ago" 2>/dev/null | 
        grep "Failed password" | 
        awk '{print $(NF-3)}' | 
        sort | uniq -c | sort -rn | head -5 >> "$RAPOR_DOSYA" || echo "Veri alınamadı" >> "$RAPOR_DOSYA"
    
    # Son başarılı girişler
    echo "" >> "$RAPOR_DOSYA"
    echo "Son 5 başarılı giriş:" >> "$RAPOR_DOSYA"
    last -n 5 | head -5 >> "$RAPOR_DOSYA"
    
    # Bekleyen sistem güncellemeleri
    echo "" >> "$RAPOR_DOSYA"
    echo "Bekleyen Güncellemeler:" >> "$RAPOR_DOSYA"
    if command -v apt-get &> /dev/null; then
        GUNCELLEME=$(apt-get -s upgrade 2>/dev/null | grep -c "^Inst" || echo "0")
        echo "Bekleyen paket güncellemesi: $GUNCELLEME" >> "$RAPOR_DOSYA"
        
        GUVENLIK=$(apt-get -s upgrade 2>/dev/null | grep -i "security" | grep "^Inst" | wc -l || echo "0")
        if [ "$GUVENLIK" -gt 0 ]; then
            echo "[DIKKAT] $GUVENLIK adet güvenlik güncellemesi bekliyor!" >> "$RAPOR_DOSYA"
            UYARI_VAR=1
        fi
    fi
}

Ağ Bağlantı Kontrolü

# Ağ ve bağlantı durumu
ag_kontrolu() {
    echo "" >> "$RAPOR_DOSYA"
    echo "--- AG DURUMU ---" >> "$RAPOR_DOSYA"
    
    # Açık portlar ve dinleyen servisler
    echo "Dinleyen portlar (TCP):" >> "$RAPOR_DOSYA"
    ss -tlnp | grep LISTEN >> "$RAPOR_DOSYA"
    
    # Dış bağlantı testi
    echo "" >> "$RAPOR_DOSYA"
    if ping -c 1 -W 3 8.8.8.8 &>/dev/null; then
        echo "[OK] Dış ağ bağlantısı mevcut" >> "$RAPOR_DOSYA"
    else
        echo "[HATA] Dış ağ bağlantısı YOK!" >> "$RAPOR_DOSYA"
        UYARI_VAR=1
    fi
    
    # Aktif bağlantı sayısı
    BAGLANTI_SAYISI=$(ss -tn | grep ESTAB | wc -l)
    echo "Aktif TCP bağlantı sayısı: $BAGLANTI_SAYISI" >> "$RAPOR_DOSYA"
    
    if [ "$BAGLANTI_SAYISI" -gt 1000 ]; then
        echo "[UYARI] Çok yüksek sayıda aktif bağlantı: $BAGLANTI_SAYISI" >> "$RAPOR_DOSYA"
        UYARI_VAR=1
    fi
}

E-posta Bildirimi

Raporu oluşturmak yetmez, o raporun size ulaşması gerekir. mailutils veya msmtp kurulu olmalı.

# E-posta gönderme fonksiyonu
mail_gonder() {
    local KONU_PREFIX="[Sunucu Raporu]"
    
    if [ "${UYARI_VAR:-0}" -eq 1 ]; then
        KONU_PREFIX="[UYARI] [Sunucu Raporu]"
    fi
    
    KONU="$KONU_PREFIX $HOSTNAME - $(date '+%d.%m.%Y')"
    
    # mail komutu varsa kullan
    if command -v mail &>/dev/null; then
        mail -s "$KONU" 
             -a "From: [email protected]" 
             "$MAIL_ALICI" < "$RAPOR_DOSYA"
        echo "Rapor e-posta ile gönderildi: $MAIL_ALICI"
    # sendmail varsa
    elif command -v sendmail &>/dev/null; then
        {
            echo "To: $MAIL_ALICI"
            echo "Subject: $KONU"
            echo "Content-Type: text/plain; charset=UTF-8"
            echo ""
            cat "$RAPOR_DOSYA"
        } | sendmail -t
    else
        echo "Uyarı: Mail göndermek için uygun araç bulunamadı. Rapor: $RAPOR_DOSYA"
    fi
}

Rapor Arşivleme ve Temizleme

Raporları sonsuza kadar saklamak disk dolmasına yol açar. Eski raporları otomatik temizlemek şart.

# Eski raporları temizle
rapor_temizle() {
    echo "" >> "$RAPOR_DOSYA"
    echo "--- RAPOR ARSIVI ---" >> "$RAPOR_DOSYA"
    
    # 30 günden eski raporları sil
    SILINEN=$(find "$RAPOR_DIZIN" -name "rapor_*.txt" -mtime +30 -delete -print | wc -l)
    echo "Silinen eski rapor sayısı: $SILINEN" >> "$RAPOR_DOSYA"
    
    # Mevcut rapor sayısı
    MEVCUT=$(find "$RAPOR_DIZIN" -name "rapor_*.txt" | wc -l)
    echo "Arşivdeki rapor sayısı: $MEVCUT" >> "$RAPOR_DOSYA"
    
    # Raporları gzip ile sıkıştır (1 günden eskiler)
    find "$RAPOR_DIZIN" -name "rapor_*.txt" -mtime +1 -exec gzip {} ; 2>/dev/null
}

Her Şeyi Bir Araya Getirme

Tüm fonksiyonları bir ana akışta birleştirin:

# Ana akış
main() {
    UYARI_VAR=0
    
    echo "Sağlık raporu oluşturuluyor: $RAPOR_DOSYA"
    
    toplam_uptime_bilgisi
    disk_kontrolu
    bellek_kontrolu
    servis_kontrolu
    guvenlik_kontrolu
    ag_kontrolu
    rapor_temizle
    
    # Raporu sonlandır
    echo "" >> "$RAPOR_DOSYA"
    echo "============================================" >> "$RAPOR_DOSYA"
    echo "Rapor tamamlandı: $(date '+%Y-%m-%d %H:%M:%S')" >> "$RAPOR_DOSYA"
    echo "Uyarı durumu: ${UYARI_VAR}" >> "$RAPOR_DOSYA"
    echo "============================================" >> "$RAPOR_DOSYA"
    
    # E-posta gönder
    mail_gonder
    
    echo "Rapor hazır: $RAPOR_DOSYA"
    
    # Uyarı varsa çıkış kodunu 1 yap (izleme araçları için)
    exit "${UYARI_VAR:-0}"
}

main "$@"

Cron Job Ayarları

Script hazır, şimdi zamanlamayı kuralım. crontab -e ile root kullanıcısının cron tablosunu düzenleyin.

# Crontab ayarları - crontab -e ile düzenleyin

# Her sabah 07:00'de günlük rapor
0 7 * * * /opt/scripts/sunucu_saglik_raporu.sh >> /var/log/saglik_raporlari/cron.log 2>&1

# Her 6 saatte bir hızlı disk kontrolü (sadece kritik uyarı için)
0 */6 * * * /opt/scripts/hizli_disk_kontrol.sh >> /var/log/saglik_raporlari/cron.log 2>&1

# Pazar günleri saat 08:00'de haftalık özet raporu
0 8 * * 0 /opt/scripts/haftalik_ozet_raporu.sh >> /var/log/saglik_raporlari/cron.log 2>&1

# Her ayın 1'inde aylık rapor
0 9 1 * * /opt/scripts/aylik_rapor.sh >> /var/log/saglik_raporlari/cron.log 2>&1

Script dosyasına çalıştırma izni vermeyi unutmayın:

chmod +x /opt/scripts/sunucu_saglik_raporu.sh
chown root:root /opt/scripts/sunucu_saglik_raporu.sh

Cron job’ın düzgün çalışıp çalışmadığını test etmek için:

# Scripti manuel çalıştır
/opt/scripts/sunucu_saglik_raporu.sh

# Cron ortamında nasıl çalışacağını simüle et
env -i HOME=/root SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 
    /opt/scripts/sunucu_saglik_raporu.sh

# Cron loglarını izle
tail -f /var/log/syslog | grep CRON

Cron job’larında sık yapılan hata, scriptin terminal ortamında çalışmasına rağmen cron ortamında PATH değişkeni eksik olduğu için komutları bulamamasıdır. Script başına PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin eklemek bu sorunu çözer.

Çoklu Sunucu Yönetimi

Birden fazla sunucunuz varsa raporları merkezi bir yerde toplamak çok daha pratik. Ana yönetim sunucusundan diğer sunuculara SSH ile bağlanıp raporları çekebilirsiniz.

#!/bin/bash
# merkezi_rapor_toplama.sh
# Tüm sunuculardan raporları toplar

SUNUCULAR=(
    "web-01.sirket.com"
    "db-01.sirket.com"
    "cache-01.sirket.com"
)

MERKEZI_RAPOR_DIZIN="/var/log/merkezi_raporlar/$(date '+%Y%m%d')"
mkdir -p "$MERKEZI_RAPOR_DIZIN"

for SUNUCU in "${SUNUCULAR[@]}"; do
    echo "Rapor alınıyor: $SUNUCU"
    
    # Uzak sunucuda scripti çalıştır ve çıktıyı yerel dosyaya kaydet
    ssh -o ConnectTimeout=10 
        -o StrictHostKeyChecking=no 
        -i /root/.ssh/monitoring_key 
        "root@$SUNUCU" 
        "/opt/scripts/sunucu_saglik_raporu.sh 2>&1" 
        > "${MERKEZI_RAPOR_DIZIN}/${SUNUCU}_rapor.txt" 2>&1 || 
        echo "[HATA] $SUNUCU'ya bağlanılamadı!" > "${MERKEZI_RAPOR_DIZIN}/${SUNUCU}_rapor.txt"
done

echo "Tüm raporlar toplandı: $MERKEZI_RAPOR_DIZIN"

Pratik İpuçları ve Yaygın Hatalar

Yıllarca bu tür scriptler kullandıktan sonra edindiğim birkaç kritik not:

  • Cron ortamında PATH eksikliği: Scriptlerin en başına mutlaka PATH değişkenini tanımlayın. Cron’un PATH’i çok kısıtlıdır.
  • Script kilitleme: Uzun süren scriptler örtüşmesin diye flock kullanın. flock -n /tmp/saglik_raporu.lock /opt/scripts/sunucu_saglik_raporu.sh şeklinde çağırabilirsiniz.
  • Çıkış kodlarına dikkat: set -e bazen beklenmedik yerlerde scripti durdurabili. Kritik olmayan komutlar için || true ekleyin.
  • Log rotasyonu: /etc/logrotate.d/saglik_raporu dosyası oluşturarak logrotate ile entegre edin. Aksi halde cron.log şişer.
  • Test ortamı: Production’da ilk çalıştırmadan önce mutlaka bir test sunucusunda deneyin.
  • Timezone sorunu: Cron’un hangi timezone’da çalıştığını kontrol edin. CRON_TZ=Europe/Istanbul satırını crontab’ın başına ekleyin.
  • E-posta spam filtresi: Otomatik e-postalar bazen spam’e düşer. SPF ve DKIM ayarlarınızı kontrol edin ya da UYARI durumlarında farklı konu satırı kullanın.

Sonuç

Birkaç saatlik emekle kurduğunuz bu sistem, size aylar boyunca değerli zaman kazandırır ve daha da önemlisi, gece yarısı “disk doldu, uygulama çöktü” telefonlarından kurtarır. Buradaki script iskeletini kendi ortamınıza göre özelleştirin, izlemek istediğiniz servisleri ve eşik değerlerini ayarlayın.

Başlangıç için basit tutun. Önce disk ve servis kontrolüyle başlayın, sistemi tanıdıkça güvenlik kontrolleri ve trend analizini ekleyin. Her şeyi bir anda yapmaya çalışmak yerine kademeli geliştirmek, hem öğrenme açısından hem de stabil çalışan bir sistem elde etmek açısından çok daha sağlıklı bir yaklaşım.

Son olarak, bu scripti version control’e (Git) almayı ihmal etmeyin. Bir değişiklik sonrası script bozulduğunda “önceki hali nasıldı?” sorusunun cevabını Git’te bulursunuz, hafızanızda değil.

Yorum yapın