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
PATHdeğişkenini tanımlayın. Cron’un PATH’i çok kısıtlıdır.
- Script kilitleme: Uzun süren scriptler örtüşmesin diye
flockkullanın.flock -n /tmp/saglik_raporu.lock /opt/scripts/sunucu_saglik_raporu.shşeklinde çağırabilirsiniz.
- Çıkış kodlarına dikkat:
set -ebazen beklenmedik yerlerde scripti durdurabili. Kritik olmayan komutlar için|| trueekleyin.
- Log rotasyonu:
/etc/logrotate.d/saglik_raporudosyası 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/Istanbulsatı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.