Restic ile Yedekleme Sağlık Kontrolü ve Doğrulama
Yedekleme aldığını sanmak ile yedeklemenin gerçekten çalışır durumda olduğunu bilmek arasında dağlar kadar fark var. Onlarca sistem yöneticisiyle konuştuğumda gördüm ki çoğu kişi yedekleme scriptini çalıştırıp “tamam, yedek alınıyor” diye içi rahat ediyor. Ta ki felakete uğrayana kadar. Restic bu konuda oldukça güçlü araçlar sunuyor ve bugün bu araçları nasıl kullanacağımızı, otomatik sağlık kontrollerini nasıl kuracağımızı ve yedeklerimizin gerçekten restore edilebilir olduğunu nasıl doğrulayacağımızı derinlemesine inceleyeceğiz.
Restic Repository Sağlığı Neden Kritik?
Restic, yedeklerinizi bir repository içinde blok bazlı (content-addressable) olarak saklar. Bu mimari harika bir veri tekilleştirme sağlarken aynı zamanda repository bütünlüğünün korunması kritik hale gelir. Ağ kesintileri, donanım hataları, storage tarafındaki sorunlar veya yarım kalan işlemler repository’yi bozabilir.
Peki bu bozulma ne zaman fark edilir? Çoğu zaman tam da restore etmeye çalıştığınız o kritik anda. Bu yüzden proaktif doğrulama yapmak, sysadmin’in birinci önceliği olmalı.
Temel check Komutu
Restic’in check komutu repository’nin yapısal bütünlüğünü doğrular. En basit haliyle:
restic -r /mnt/backup/myrepo check
Bu komut ne yapar?
- Repository’nin index yapısını kontrol eder
- Pack dosyalarının varlığını doğrular
- Snapshot referanslarının tutarlılığını inceler
- Orphan (sahipsiz) veri bloklarını tespit eder
Çıktı şöyle görünür:
using temporary cache in /tmp/restic-check-cache-123456789
repository 1a2b3c4d opened successfully, password is correct
created new cache in /tmp/restic-check-cache-123456789
[0:00] 100.00% 1 / 1 index files loaded
no errors were found
Eğer bir sorun varsa şöyle bir çıktı alabilirsiniz:
pack 4a5b6c7d: does not exist
error: repository contains errors
Bu noktada panik yapmadan önce sorunun ne olduğunu anlamanız gerekiyor.
Veri Bloklarını da Doğrulayan Derinlemesine Kontrol
Varsayılan check komutu sadece metadata’yı doğrular, asıl veri bloklarını okumaz. Gerçek bir sağlık kontrolü için --read-data flag’ini kullanmanız gerekir:
restic -r /mnt/backup/myrepo check --read-data
Bu komut repository’deki her pack dosyasını okur ve checksum’ları doğrular. Küçük bir repository’de dakikalar içinde biter ama büyük repository’lerde saatler alabilir. Bu yüzden genellikle haftalık veya aylık olarak çalıştırılır.
Büyük repository’ler için daha akıllıca bir yaklaşım olan rastgele örnekleme özelliğini kullanabilirsiniz:
# Repository'nin %10'unu rastgele örnekleyerek kontrol et
restic -r /mnt/backup/myrepo check --read-data-subset=10%
Bu yaklaşım özellikle uzak depolama (S3, Backblaze B2, sftp) kullanırken hem bant genişliğini hem de maliyeti kontrol altında tutmanızı sağlar.
Belirli bir boyut veya pack sayısı da belirtebilirsiniz:
# İlk 5 pack dosyasını kontrol et
restic -r /mnt/backup/myrepo check --read-data-subset=5/50
Remote Repository ile Sağlık Kontrolü
Rclone üzerinden uzak bir depolamaya bağlıyken sağlık kontrolü yapmak biraz farklı bir konfigürasyon gerektiriyor. Önce bir örnek senaryo kuralım: S3-uyumlu bir depolamada (örneğin Wasabi veya Backblaze B2) restic repository’niz var.
# Environment variable'larla S3 bağlantısı
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export RESTIC_REPOSITORY="s3:s3.wasabisys.com/mybucket/backups"
export RESTIC_PASSWORD="your-repo-password"
# Metadata kontrolü
restic check
# Rastgele %5 veri okuma ile kontrol
restic check --read-data-subset=5%
Rclone backend kullanıyorsanız:
export RESTIC_REPOSITORY="rclone:myremote:backup-bucket/restic-repo"
export RESTIC_PASSWORD_FILE="/etc/restic/password"
restic check --read-data-subset=15%
Çıkış kodlarına dikkat edin. Restic başarılı kontrol için 0, hata durumunda 1 döndürür. Bu, scriptleme açısından son derece kullanışlıdır.
Snapshot Doğrulama ve Detaylı İnceleme
Sağlıklı bir repository var ama snapshot’larınız gerçekten restore edilebilir mi? Bunu anlamak için birkaç farklı yaklaşım var.
İlk olarak snapshot’larınızı listeleyin ve son snapshot’ın zamanını kontrol edin:
restic snapshots --json | python3 -c "
import json, sys
from datetime import datetime, timezone
data = json.load(sys.stdin)
if not data:
print('CRITICAL: No snapshots found!')
sys.exit(2)
latest = max(data, key=lambda x: x['time'])
snap_time = datetime.fromisoformat(latest['time'].replace('Z', '+00:00'))
now = datetime.now(timezone.utc)
hours_ago = (now - snap_time).total_seconds() / 3600
print(f'Latest snapshot: {latest["id"][:8]}')
print(f'Time: {snap_time}')
print(f'Hours ago: {hours_ago:.1f}')
if hours_ago > 25:
print('WARNING: Last backup is older than 25 hours!')
sys.exit(1)
else:
print('OK: Recent backup exists')
"
Bu script aynı zamanda Nagios/Icinga tipi monitoring sistemleriyle entegre edilebilir çünkü uygun çıkış kodları kullanıyor.
Gerçek Dünya Restore Testi
Yedeklerinizi test etmeden “yedek alıyorum” demek, sigortasız araba sürmekle aynı şey. Ayda en az bir kere kısmi restore testi yapmanızı kesinlikle tavsiye ederim.
#!/bin/bash
# restore_test.sh - Kısmi restore testi
REPO="rclone:wasabi-backup:mycompany/restic"
PASSWORD_FILE="/etc/restic/password"
TEST_DIR="/tmp/restic-restore-test"
LOG_FILE="/var/log/restic-restore-test.log"
echo "=== Restore Test Başlıyor: $(date) ===" >> "$LOG_FILE"
# Geçici dizini temizle
rm -rf "$TEST_DIR"
mkdir -p "$TEST_DIR"
# Son snapshot'ı al
LATEST_SNAP=$(restic -r "$REPO" --password-file "$PASSWORD_FILE"
snapshots --json --last | python3 -c
"import json,sys; d=json.load(sys.stdin); print(d[-1]['id'] if d else '')")
if [ -z "$LATEST_SNAP" ]; then
echo "HATA: Snapshot bulunamadi" >> "$LOG_FILE"
exit 1
fi
echo "Test edilecek snapshot: $LATEST_SNAP" >> "$LOG_FILE"
# Sadece kritik bir dizini restore et (örneğin /etc/nginx)
restic -r "$REPO" --password-file "$PASSWORD_FILE"
restore "$LATEST_SNAP"
--include "/etc/nginx"
--target "$TEST_DIR" 2>> "$LOG_FILE"
RESTORE_EXIT=$?
if [ $RESTORE_EXIT -eq 0 ]; then
# Restore edilen dosyaların varlığını kontrol et
if [ -d "$TEST_DIR/etc/nginx" ]; then
FILE_COUNT=$(find "$TEST_DIR/etc/nginx" -type f | wc -l)
echo "BASARILI: $FILE_COUNT dosya restore edildi" >> "$LOG_FILE"
# Test dizinini temizle
rm -rf "$TEST_DIR"
exit 0
else
echo "HATA: Restore klasoru beklenen yapıda degil" >> "$LOG_FILE"
exit 1
fi
else
echo "HATA: Restore islemi basarisiz (exit: $RESTORE_EXIT)" >> "$LOG_FILE"
exit 1
fi
Otomatik Sağlık Kontrol Scripti
Bütün kontrolleri bir araya getiren, e-posta ve Slack bildirimi gönderen kapsamlı bir script hazırlayalım:
#!/bin/bash
# restic_health_check.sh
# Cron: 0 6 * * * /usr/local/bin/restic_health_check.sh
set -euo pipefail
# Konfigürasyon
REPO="${RESTIC_REPOSITORY:-rclone:myremote:backups/restic}"
PASSWORD_FILE="/etc/restic/password"
SLACK_WEBHOOK="${SLACK_WEBHOOK_URL:-}"
ALERT_EMAIL="[email protected]"
MAX_BACKUP_AGE_HOURS=25
CHECK_SUBSET="10%"
LOG_FILE="/var/log/restic-health.log"
REPORT=""
STATUS="OK"
log() {
local message="$1"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $message" | tee -a "$LOG_FILE"
REPORT="${REPORT}n${message}"
}
send_slack_alert() {
local message="$1"
if [ -n "$SLACK_WEBHOOK" ]; then
curl -s -X POST "$SLACK_WEBHOOK"
-H 'Content-type: application/json'
--data "{"text": "*Restic Sağlık Kontrolü*n${message}"}" > /dev/null
fi
}
send_email_alert() {
local subject="$1"
local body="$2"
echo -e "$body" | mail -s "$subject" "$ALERT_EMAIL" 2>/dev/null || true
}
log "=== Restic Sağlık Kontrolü Başlıyor ==="
# 1. Temel Repository Kontrolü
log "1. Repository metadata kontrolü..."
if restic -r "$REPO" --password-file "$PASSWORD_FILE" check 2>&1 | tee -a "$LOG_FILE"; then
log "Metadata kontrolü: BAŞARILI"
else
STATUS="CRITICAL"
log "Metadata kontrolü: BAŞARISIZ"
fi
# 2. Veri örnekleme kontrolü
log "2. Veri bütünlüğü kontrolü (%${CHECK_SUBSET})..."
if restic -r "$REPO" --password-file "$PASSWORD_FILE"
check --read-data-subset="$CHECK_SUBSET" 2>&1 | tee -a "$LOG_FILE"; then
log "Veri kontrolü: BAŞARILI"
else
STATUS="CRITICAL"
log "Veri kontrolü: BAŞARISIZ"
fi
# 3. Son yedek yaşı kontrolü
log "3. Son yedek zamanı kontrol ediliyor..."
LATEST_TIME=$(restic -r "$REPO" --password-file "$PASSWORD_FILE"
snapshots --json --last 2>/dev/null |
python3 -c "import json,sys; d=json.load(sys.stdin); print(d[-1]['time'] if d else 'NONE')")
if [ "$LATEST_TIME" = "NONE" ]; then
STATUS="CRITICAL"
log "Son snapshot zamanı: BULUNAMADI"
else
HOURS_OLD=$(python3 -c "
from datetime import datetime, timezone
t = datetime.fromisoformat('${LATEST_TIME}'.replace('Z','+00:00'))
diff = (datetime.now(timezone.utc) - t).total_seconds() / 3600
print(f'{diff:.1f}')
")
log "Son yedek $HOURS_OLD saat önce alındı"
if (( $(echo "$HOURS_OLD > $MAX_BACKUP_AGE_HOURS" | bc -l) )); then
STATUS="WARNING"
log "UYARI: Yedek $MAX_BACKUP_AGE_HOURS saatten daha eski!"
fi
fi
# 4. Snapshot sayısı kontrolü
SNAP_COUNT=$(restic -r "$REPO" --password-file "$PASSWORD_FILE"
snapshots --json 2>/dev/null | python3 -c "import json,sys; print(len(json.load(sys.stdin)))")
log "Toplam snapshot sayısı: $SNAP_COUNT"
# Sonuç değerlendirme
log "=== Kontrol Tamamlandı - Durum: $STATUS ==="
if [ "$STATUS" != "OK" ]; then
ALERT_MSG="Durum: $STATUSnRepository: $REPOnn$(echo -e "$REPORT")"
send_slack_alert "$ALERT_MSG"
send_email_alert "Restic Yedekleme Uyarisi: $STATUS" "$ALERT_MSG"
exit 1
fi
exit 0
Prune Sonrası Bütünlük Kontrolü
Restic’in forget ve prune operasyonları sonrasında repository’nin tutarlı kalmasını doğrulamak çok önemli. Bu operasyonlar veri siler ve bazen beklenmedik durumlar yaşanabilir:
#!/bin/bash
# prune_and_verify.sh
REPO="rclone:b2:my-bucket/restic"
PASSWORD_FILE="/etc/restic/password"
echo "Eski snapshot'ları temizleniyor..."
# Retention policy uygula
restic -r "$REPO" --password-file "$PASSWORD_FILE" forget
--keep-daily 7
--keep-weekly 4
--keep-monthly 6
--keep-yearly 2
--prune
FORGET_STATUS=$?
if [ $FORGET_STATUS -ne 0 ]; then
echo "HATA: forget/prune başarısız (exit: $FORGET_STATUS)"
exit 1
fi
echo "Prune sonrası bütünlük kontrolü..."
# Prune sonrası mutlaka check çalıştır
restic -r "$REPO" --password-file "$PASSWORD_FILE" check
CHECK_STATUS=$?
if [ $CHECK_STATUS -ne 0 ]; then
echo "KRİTİK: Prune sonrası repository bütünlük hatası!"
echo "Acil müdahale gerekiyor. Repository'yi incelleyin."
# Acil bildirim gönder
echo "Restic repo bütünlük hatası: $REPO" |
mail -s "KRİTİK: Yedekleme Hatası" [email protected]
exit 1
fi
echo "Tüm kontroller başarılı."
Repository Onarımı: repair Komutu
Restic 0.14.0 ile birlikte gelen repair komutu, bazı repository sorunlarını otomatik olarak çözebilir. Bu komutu dikkatlice kullanın, önce ne yapacağını anlayın:
# Önce sorunlu pack'leri listele
restic -r "$REPO" --password-file "$PASSWORD_FILE"
check 2>&1 | grep "pack" | head -20
# Index'i yeniden oluştur (index bozulması durumunda)
restic -r "$REPO" --password-file "$PASSWORD_FILE" repair index
# Snapshot'ları eksik bloklara karşı onar
restic -r "$REPO" --password-file "$PASSWORD_FILE" repair snapshots
# Onarım sonrası tekrar kontrol et
restic -r "$REPO" --password-file "$PASSWORD_FILE" check --read-data-subset=20%
Önemli Not: repair komutu bazı durumlarda veri kaybına yol açabilir. Eksik veri bloklarına sahip snapshot’lar işaretlenerek erişilemez hale getirilebilir. Bu yüzden mümkünse önce repository’nin bir kopyasını alın.
Monitoring Sistemine Entegrasyon
Prometheus ve Grafana kullanan bir ortamda restic metriklerini nasıl toplayabileceğinizi göstereyim. Textfile collector yöntemi en pratik olanı:
#!/bin/bash
# restic_metrics.sh - Prometheus textfile collector için
# /etc/cron.d/restic-metrics: */30 * * * * root /usr/local/bin/restic_metrics.sh
REPO="rclone:wasabi:backups/prod-restic"
PASSWORD_FILE="/etc/restic/password"
METRICS_FILE="/var/lib/prometheus/node-exporter/restic.prom"
TEMP_FILE="${METRICS_FILE}.tmp"
# Snapshot bilgilerini al
SNAP_DATA=$(restic -r "$REPO" --password-file "$PASSWORD_FILE"
snapshots --json --last 5 2>/dev/null || echo "[]")
SNAP_COUNT=$(echo "$SNAP_DATA" | python3 -c "import json,sys; print(len(json.load(sys.stdin)))" 2>/dev/null || echo "0")
LATEST_TIMESTAMP=$(echo "$SNAP_DATA" | python3 -c "
import json, sys
from datetime import datetime, timezone
d = json.load(sys.stdin)
if d:
t = datetime.fromisoformat(d[-1]['time'].replace('Z','+00:00'))
print(int(t.timestamp()))
else:
print(0)
" 2>/dev/null || echo "0")
# Check sonucu
if restic -r "$REPO" --password-file "$PASSWORD_FILE" check 2>/dev/null; then
CHECK_STATUS=1
else
CHECK_STATUS=0
fi
# Metrikleri yaz
cat > "$TEMP_FILE" << EOF
# HELP restic_check_status Repository sağlık durumu (1=sağlıklı, 0=sorunlu)
# TYPE restic_check_status gauge
restic_check_status{repository="prod"} $CHECK_STATUS
# HELP restic_last_backup_timestamp Son yedekleme Unix timestamp
# TYPE restic_last_backup_timestamp gauge
restic_last_backup_timestamp{repository="prod"} $LATEST_TIMESTAMP
# HELP restic_snapshot_count Toplam snapshot sayısı
# TYPE restic_snapshot_count gauge
restic_snapshot_count{repository="prod"} $SNAP_COUNT
EOF
mv "$TEMP_FILE" "$METRICS_FILE"
Sık Karşılaşılan Sorunlar ve Çözümleri
Gerçek hayatta en çok şu durumlarla karşılaşılıyor:
Lock kalan repository sorunu: Bir yedekleme işlemi yarıda kesilirse repository üzerinde bir lock kalabilir. Bu durum sonraki check veya backup işlemlerini engeller.
# Mevcut lock'ları listele
restic -r "$REPO" --password-file "$PASSWORD_FILE" list locks
# Eski lock'ları temizle (dikkatli kullanın, başka bir işlem varsa bozabilir)
restic -r "$REPO" --password-file "$PASSWORD_FILE" unlock
Cache bozulması: Yerel cache bazen yanlış sonuçlara yol açabilir.
# Cache'i bypass ederek kontrol et
restic -r "$REPO" --password-file "$PASSWORD_FILE"
check --no-cache
# Cache'i sıfırla
restic cache --cleanup
Timeout sorunları: Büyük remote repository’lerde check işlemi zaman aşımına uğrayabilir. Bu durumda daha küçük subset kullanın veya bağlantı timeout’unu artırın:
# Rclone backend için timeout ayarı
export RCLONE_TIMEOUT=600s
restic -r "$REPO" --password-file "$PASSWORD_FILE"
check --read-data-subset=5%
Check Sıklığı Önerileri
Ortam büyüklüğüne ve kritikliğine göre farklı stratejiler uygun olur:
- Günlük: Metadata-only check (
restic check) her gece çalışmalı - Haftalık: %15-20 veri örnekleme ile check, tercihen hafta sonu
- Aylık: Tam veri doğrulama (
--read-data) ve gerçek restore testi - Her prune sonrası: Mutlaka metadata check çalıştırılmalı
Bu kontrolleri cron veya systemd timer ile otomatize edin. Özellikle systemd timer kullanıyorsanız OnCalendar direktifini esnek biçimde kullanabilirsiniz:
# /etc/systemd/system/restic-check.timer
[Unit]
Description=Restic Repository Health Check
[Timer]
OnCalendar=*-*-* 04:30:00
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target
Sonuç
Restic güçlü ve güvenilir bir araç ama “set and forget” mantığıyla kullanılamaz. Bugün anlattığım adımları uygularsanız yedekleme sağlığınız konusunda çok daha güvende olursunuz:
- Günlük metadata check ile repository yapısını izleyin
- Haftalık veri örnekleme ile gerçek bütünlüğü doğrulayın
- Aylık restore testi ile geri yüklemenin çalıştığını kanıtlayın
- Prune sonrası otomatik check ile temizlik işlemlerini güvenceye alın
- Monitoring entegrasyonu ile anında haberdar olun
En kötü senaryo şu: Felakete uğruyorsunuz, restore için komutları çalıştırıyorsunuz ve “repository contains errors” çıktısını görüyorsunuz. Bu anı yaşamamak için bugün bir sağlık kontrol rutini oluşturun. Gelecekteki kendiniz size teşekkür edecek.
