restic + rclone ile 3-2-1 yedekleme stratejisi
restic ve rclone ile 3-2-1 yedekleme: şifreli depo kurulumu, parola yönetimi, systemd timer ile zamanlama, uzak kopya ve haftalık geri yükleme provası.
Yedek konusunda en sık duyduğum cümle "yedeğimiz var" oluyor. Biraz kurcalayınca ya tek bir diskte duruyor, ya canlı veriyle aynı makinede yaşıyor, ya da en son ne zaman geri açıldığını kimse hatırlamıyor. 3-2-1 yedekleme stratejisi bu belirsizliği ortadan kaldıran en sade kural: verinin üç kopyası, en az iki farklı ortamda, en az biri fiziksel olarak başka bir yerde. Bu yazıda bu kuralı restic ve rclone ile nasıl kurduğumu, nerede takıldığımı ve asıl mesele olan geri yükleme provasını nasıl yaptığımı anlatacağım.
Neden bu ikili? restic, veriyi içeriğe göre parçalayıp tekilleştiren, varsayılan olarak şifreleyen ve her yedeği bağımsız bir anlık görüntü (snapshot) gibi geri yükleyebilen bir araç; artımlı çalışır ama "önce tam yedeği, sonra artımlıları sırayla aç" derdi yoktur. rclone ise onlarca depolama servisiyle aynı komut setiyle konuşur ve restic'in rclone arka ucu sayesinde depoyu doğrudan bir nesne deposuna ya da uzak sunucuya yazabilirsiniz. İkisi de tek bir ikili dosya, ikisi de açık kaynak.
Örnekleri Linux sunucu üzerinden veriyorum; Windows'ta komutlar aynı, yalnızca yollar ve zamanlayıcı değişir. Hedef düzen şu: çalışan veri birinci kopya, aynı sunucuya bağlı ayrı bir diskteki restic deposu ikinci, rclone üzerinden uzaktaki depo üçüncü. Ve haftada bir, uzaktaki kopyadan geri yükleme provası.
restic deposunu kurmak ve parolayı doğru yerde tutmak
restic deposu tek bir dizindir; restic init ile açılır ve o andan itibaren içine yazılan her şey parolayla şifrelenir. Burada ilk ve en kritik karar parolanın nerede yaşayacağı. Üç kural uyguluyorum:
- Parola komut satırına ve kabuk geçmişine asla girmez;
--password-fileya daRESTIC_PASSWORD_FILEile dosyadan okunur, dosyanın izni 600'dür. - Aynı parola bir parola kasasında (benim tercihim SOPS ve age ile şifrelenmiş bir dosya) ve kâğıt üzerinde, sunucudan bağımsız bir yerde de durur. restic'te parola kurtarma diye bir şey yok: parolayı kaybederseniz depo bir daha açılmaz.
- Parola dosyasını Windows'ta ürettiyseniz satır sonuna dikkat. Başıma geldi: dosya CRLF ile kaydedilmişti, ben de parolayı
RESTIC_PASSWORD=$(cat dosya)ile ortama alıyordum. Kabuk sondaki LF'yi atar ama CR'yi bırakır; parolanın sonuna görünmez bir karakter eklendi ve depo "yanlış parola" dedi.--password-filekullanın; restic dosyayı okurken baştaki ve sondaki boşlukları kendisi kırpıyor.
# Parola dosyasi: yalnizca root okusun, icinde satir sonu olmasin
install -m 600 /dev/null /root/.restic-parola
openssl rand -base64 32 | tr -d '\n' > /root/.restic-parola
# Depo adresi ve parola dosyasi tek bir ortam dosyasinda (systemd de bunu okuyacak)
printf 'RESTIC_REPOSITORY=/mnt/yedek/restic\nRESTIC_PASSWORD_FILE=/root/.restic-parola\n' > /root/.restic-env
chmod 600 /root/.restic-env
set -a; . /root/.restic-env; set +a
# Depoyu ac, ilk yedegi al, sonucu gozle dogrula
restic init
restic backup /srv/veri /etc \
--exclude-file=/root/restic-haric.txt --exclude-caches --tag gunluk
restic snapshots
restic ls latest | head -n 40 # beklenen yollar gercekten iceride mi?
Hariç tutma listesi yalnızca yedeğin boyutunu değil, geri yükleme günü ne bulacağınızı belirler: önbellek dizinleri, derleme çıktıları ve yeniden üretilebilir her şey dışarıda kalsın; yapılandırma dosyaları ve veri içeride. İlk yedekten sonra restic ls latest çıktısını bir kez gözle okuyun; beklediğiniz yolların tamamının orada olduğunu görmek bir dakikanızı alır, eksik bir yolu felaket gününde fark etmek ise çok daha pahalıya mal olur.
Zamanlanmış yedek: cron yerine systemd timer
Yedeği elle almak sürdürülebilir değil; ama zamanlayıcı seçimi de önemsiz değil. cron yerine systemd timer kullanmamın üç sebebi var:
Persistent=trueile makine kapalıyken kaçırılan koşum, açılışta telafi edilir. cron bunu yapmaz; hafta sonu kapalı kalan bir sunucu sessizce iki gün yedeksiz kalır.- Çıktı journal'a düşer;
systemctl list-timersbir sonraki ve bir önceki koşumu tek satırda gösterir. - Servis başarısız olduğunda
OnFailure=ile bir alarm birimi tetiklenebilir; "çalışmadı ve kimse fark etmedi" senaryosu böyle kapanır.
# /etc/systemd/system/restic-yedek.service
[Unit]
Description=restic gunluk yedek
# OnFailure=yedek-alarm@%n.service (alarm birimini kurunca acin)
[Service]
Type=oneshot
EnvironmentFile=/root/.restic-env
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
ExecStart=/usr/bin/restic backup /srv/veri /etc --exclude-file=/root/restic-haric.txt --exclude-caches --tag gunluk
ExecStartPost=/usr/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
# /etc/systemd/system/restic-yedek.timer
[Unit]
Description=restic gunluk yedek zamanlayicisi
[Timer]
OnCalendar=*-*-* 03:15:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target
İki dosyayı yerine koyduktan sonra systemctl enable --now restic-yedek.timer yeterli. Saklama politikasını (forget) yedekle aynı birime koyuyorum; --prune pahalı bir işlem olduğu için büyük depolarda onu ayrı, haftalık bir timer'a almak daha doğru. Bir de veritabanları var: çalışan bir veritabanı dosyasını olduğu gibi kopyalamak tutarlı bir yedek vermez. SQLite için önce .backup komutuyla tutarlı bir kopya alıp restic'e onu veriyorum; bu konuyu SQLite ile küçük ama sağlam veri boru hatları yazısında ayrıntılı anlatmıştım. PostgreSQL ya da MySQL için aynı mantık pg_dump ve mysqldump ile geçerli: döküm dosyası yedek dizinine düşer, restic onu alır.
rclone ile üçüncü kopya: dışarıda ve bağımsız
Uzak kopya için iki yol var. İlki yerel depo dizinini rclone sync ile olduğu gibi uzağa kopyalamak: kurulumu basit, ama yerel depoda oluşan bir bozulmayı da aynen taşır ve prune sırasında çalışırsa uzaktaki depo tutarsız kalır. Tercihim ikincisi: uzakta ayrı bir restic deposu açıp restic copy ile snapshot'ları aktarmak. Bu yolda restic her parçayı okuyup sağlama toplamını doğrulayarak yazar, bozuk bir paketi sessizce çoğaltmak yerine hata verir; uzak depo kendi parolasına sahip olabilir ve iki depo birbirinden bağımsız denetlenir.
Tek incelik, uzak depoyu --copy-chunker-params ile açmak: iki depo aynı parçalama parametrelerini kullanmazsa tekilleştirme çalışmaz ve her kopyalama tüm veriyi yeniden yükler.
# rclone tarafinda 'b2yedek' adli bir uzak tanimladiginizi varsayiyorum (rclone config)
rclone lsd b2yedek:
set -a; . /root/.restic-env; set +a
# Uzak depoyu yerelden bagimsiz ama ayni parcalama parametreleriyle ac (bir kez)
restic -r rclone:b2yedek:yedek-kovasi/restic init \
--from-repo "$RESTIC_REPOSITORY" --from-password-file "$RESTIC_PASSWORD_FILE" \
--copy-chunker-params --password-file /root/.restic-parola-uzak
# Yerel depodaki yeni snapshot'lari uzaga aktar; daha once kopyalananlar atlanir
restic -r rclone:b2yedek:yedek-kovasi/restic copy \
--from-repo "$RESTIC_REPOSITORY" --from-password-file "$RESTIC_PASSWORD_FILE" \
--password-file /root/.restic-parola-uzak --limit-upload 20000
Uzak taraftaki birkaç ayrıntı, 3-2-1'in "bir dışarıda" ayağını gerçek kılar:
- rclone uzağını bir kez
rclone configile tanımlayın; erişim anahtarı rclone'un kendi yapılandırma dosyasında durur, betiklere ve birimlere girmez. - Sağlayıcıda sürümlemeyi (versioning) ya da nesne kilidini açın. Sunucuya sızan biri uzak yedeği silse bile eski sürümler saklama süresi boyunca yerinde kalır; bedeli, silinen verinin o süre boyunca alan işgal etmesidir.
copykomutunu yedek biriminden ayrı, ondan bir saat sonra çalışan ikinci bir timer'a bağlayın; iki iş aynı depoya aynı anda dokunmasın.
Geri yükleme provası: test edilmemiş yedek yedek değildir
Buraya kadar anlattıklarım, düzgün yapılsa bile yalnızca "yedek alınıyor" demeye yeter. "Yedek var" demek için geri yükleyip bakmış olmak gerekir; hem de sunucu yanmış gibi, yani uzaktaki depodan ve mümkünse başka bir makinede. Benim kuralım net: tarih ve komutla kaydedilmiş bir prova yoksa o yedek yok sayılır.
Provayı üç seviyede yapıyorum:
- Depo bütünlüğü:
restic checkdepo yapısını doğrular;--read-data-subsetile paketlerin bir kısmını gerçekten indirip okur.1/12,2/12gibi sırayla ilerleyen dilimler verirseniz on iki haftada tüm veri en az bir kez elden geçmiş olur. - Dosya geri yükleme: son snapshot'tan kritik dizin geçici bir hedefe açılır,
--verifyile geri gelen içerik depodakiyle karşılaştırılır. - Uygulama seviyesi: geri gelen veritabanı gerçekten açılıyor mu, bütünlük denetimi geçiyor mu, yapılandırma dosyası okunuyor mu. Byte'ları geri getirmek yetmez; uygulamanın onları anlaması gerekir.
#!/usr/bin/env bash
# Haftalik geri yukleme provasi: UZAK depodan, gecici dizine, uygulama seviyesinde dogrulama
set -euo pipefail
export RESTIC_REPOSITORY="rclone:b2yedek:yedek-kovasi/restic"
export RESTIC_PASSWORD_FILE=/root/.restic-parola-uzak
KAYIT=/var/log/restic-prova.log
HEDEF="$(mktemp -d /var/tmp/prova.XXXXXX)"
trap 'rm -rf "$HEDEF"' EXIT
# 1) Depo butunlugu: her hafta 12 dilimden bir sonrakini gercekten indirip oku
DILIM=$(( 10#$(date +%V) % 12 + 1 ))
restic check --read-data-subset="$DILIM/12"
# 2) Dosya geri yukleme + icerik dogrulama
restic restore latest --target "$HEDEF" --include /srv/veri/kritik --verify
# 3) Uygulama seviyesi: geri gelen veritabani gercekten aciliyor mu?
[ "$(sqlite3 "$HEDEF/srv/veri/kritik/uygulama.db" 'PRAGMA integrity_check;')" = ok ]
# 4) Kanit: tarih + snapshot kimligi kayda duser; nobetci bu dosyanin tazeligine bakar
SNAP="$(restic snapshots latest --json | jq -r '.[0].short_id')"
printf '%s PROVA GECTI snapshot=%s dilim=%s/12\n' "$(date -Is)" "$SNAP" "$DILIM" | tee -a "$KAYIT"
Betiğin son satırı en az geri yükleme kadar önemli: prova geçtiğinde tarih ve snapshot kimliği bir kayıt dosyasına düşer. Ayrı bir izleme görevi bu dosyaya bakar ve son başarılı kayıt belirlediğim gün sayısından eskiyse alarm üretir. Böylece "prova betiği bozuldu ve üç haftadır çalışmıyor" durumu da yakalanır; sessiz başarısızlığı yakalayan bu tür bir nöbetçiyi PowerShell ile sistem izleme betiği yazısında farklı bir bağlamda kurmuştum, mantık aynı. Yılda en az bir kez de provayı elle, sıfır bir makinede ve yalnızca kâğıttaki parolayla yapıyorum: rclone yapılandırmasını, restic'i ve parolayı sıfırdan kurup depoyu açabiliyor muyum? Bu tek deneme, belgelerdeki eksik adımı her seferinde ortaya çıkarıyor.
Sonuç: yedeğin varlığı kanıtla ölçülür
Kurulumun tamamı bir öğleden sonra sürüyor; kalıcı olan kısım ise disiplin. Üç kopya ve iki ortam restic ile birkaç komutluk iş, dışarıdaki kopya rclone ile bir yapılandırma satırı. Asıl fark, geri yükleme provasını takvime bağlayıp sonucunu kaydeden ve kayıt eskidiğinde ses çıkaran küçük düzenekte. Depoyu kuran ama provayı atlayan kurulumları, bir gün "yedek vardı ama açılmadı" cümlesiyle hatırlıyoruz.
Bu tür dayanıklı ve kendi kendini denetleyen otomasyonlar kurmak veri ve otomasyon danışmanlığı işimin merkezinde. Elinizdeki sunucu ya da veri için benzer bir yedek düzeni kurmak isterseniz iletişim sayfasından bana yazabilirsiniz; ilk iş olarak mevcut yedeğinizi birlikte geri yükleyerek başlarız.