sunucu.com.tr

Yedekleme rehberi

Sunucu dosyalarınızı ve veritabanlarınızı her gece İstanbul'daki Object Storage'a otomatik yedekleyin. Bu rehber açık kaynak araçlarla (restic ve rclone) kurulumu, zamanlamayı, fidye yazılımına karşı korumayı ve geri yükleme testini anlatır.

Belgeler ve içindekiler

Object Storage verisi bizim tarafımızda ayrıca yedeklenmez

Her nesne ayrı depolama düğümlerinde 3 kopya tutulur; bu donanım arızasına karşı korur ama yedek değildir. Sildiğiniz ya da üzerine yazdığınız nesne kopyalarda da değişir. Aşağıdaki sürüm geçmişi ve object lock adımları bu yüzden önemlidir. Başka lokasyona ek kopya isterseniz talep üzerine ek hizmet olarak hazırlarız.

3-2-1 kuralı

Sağlam bir yedek düzeni verinin 3 kopyasını, 2 farklı ortamda, 1 tanesi başka bir yerde tutar. Sunucunuzdaki canlı veri birinci kopyadır; Object Storage'daki yedek ikinci ve sunucudan bağımsız kopyadır. Kritik veride üçüncü kopyayı ofisinizdeki bir diske ya da başka bir lokasyona alın.

Hazırlık

  1. Panelde yedek için ayrı bir bucket oluşturun. Fidye yazılımı koruması istiyorsanız oluştururken object lock'u açın ve saklama süresini seçin (ör. 30 gün; aşağıda ayrıntı).
  2. Yalnız bu bucket'a erişen bir erişim anahtarı açın ve sunucunuzun genel IP adresiyle sınırlayın.
  3. Paket kotasını planlayın: yedek boyutu × saklanan gün sayısı kadar yer gerekmez (restic ve rclone yalnız değişeni yükler), ama sürüm geçmişi açıkken eski sürümler de kotaya sayılır.

restic ile sunucu yedeği

restic, yedeği sunucunuzda şifreleyip parçalara ayırarak yükler; aynı veriyi ikinci kez göndermez, her çalışma bağımsız bir geri dönüş noktası (snapshot) üretir. Ayarları bir dosyada toplayın:

/root/.restic-env

# /root/.restic-env  (chmod 600)
export AWS_ACCESS_KEY_ID=<ERISIM_ANAHTARI>
export AWS_SECRET_ACCESS_KEY=<GIZLI_ANAHTAR>
export AWS_DEFAULT_REGION=tr-ist-1
export RESTIC_REPOSITORY=s3:https://s3.tr.sunucu.com.tr/<bucket-adi>/$(hostname -s)
export RESTIC_PASSWORD_FILE=/root/.restic-parola

Terminal

apt install restic            # ya da: dnf install restic
openssl rand -base64 32 > /root/.restic-parola && chmod 600 /root/.restic-parola
source /root/.restic-env

restic -o s3.bucket-lookup=path init
restic -o s3.bucket-lookup=path backup /etc /var/www /home --exclude-caches
restic -o s3.bucket-lookup=path snapshots

Depo parolasını kaybetmeyin

restic verinizi parolanızla şifreler; parola olmadan yedek açılamaz ve biz de açamayız. /root/.restic-parola dosyasının bir kopyasını parola yöneticinizde saklayın.

-o s3.bucket-lookup=path path-style adreslemeyi açar. Depo adresindeki $(hostname -s) her sunucunun aynı bucket içinde kendi klasörünü kullanmasını sağlar.

rclone ile dosya yedeği

Dosyaları tek tek açılabilir biçimde (şifresiz, klasör yapısı korunarak) tutmak istiyorsanız rclone kullanın. Yapılandırma Araçlar sayfasındadır.

Terminal

# Günlük eşitleme: değişen ve silinen dosyaların eski hali tarihli klasöre taşınır
rclone sync /var/www sunucu:<bucket-adi>/www/guncel \
  --backup-dir sunucu:<bucket-adi>/www/eski/$(date +%F) \
  --transfers 8 --fast-list --log-file /var/log/rclone-yedek.log

Bucket'ta versioning açıksa --backup-dir gerekmez: üzerine yazılan ve silinen dosyaların eski sürümleri bucket'ta kalır. İstemci tarafı şifreleme isterseniz rclone'un crypt uzak türünü bu bağlantının üzerine kurun.

Veritabanı yedeği

Çalışan veritabanının dosyalarını kopyalamak tutarlı yedek vermez; döküm (dump) alın. Döküm diske yazılmadan doğrudan yedeğe akar:

Terminal

# MySQL / MariaDB: kimlik bilgisi /root/.my.cnf içinde ([client] user=... password=...)
mysqldump --single-transaction --quick --routines --triggers --events --all-databases \
  | restic -o s3.bucket-lookup=path backup --stdin --stdin-filename mysql-tum.sql --tag mysql

# PostgreSQL: tek veritabanı, sıkıştırılmış özel biçim (pg_restore ile seçerek geri yüklenir)
sudo -u postgres pg_dump -Fc uygulama \
  | restic -o s3.bucket-lookup=path backup --stdin --stdin-filename postgres-uygulama.dump --tag postgres

# restic kullanmıyorsanız: dökümü doğrudan rclone ile yükleyin
mysqldump --single-transaction --quick --all-databases | gzip \
  | rclone rcat sunucu:<bucket-adi>/mysql/$(hostname -s)-$(date +%F-%H%M).sql.gz
  • --single-transaction, InnoDB tablolarını kilitlemeden tutarlı bir anlık görüntü alır. MyISAM tablolarınız varsa döküm sırasında yazmalar beklemeli ya da gece sakin saatte alınmalıdır.
  • Büyük PostgreSQL veritabanlarında pg_dump -Fc yerine paralel döküm (-Fd -j 4) klasöre alınıp restic ile yedeklenebilir.

Zamanlama

Yedek, veritabanı dökümü ve saklama temizliğini tek betikte toplayın:

/usr/local/bin/yedek.sh

#!/usr/bin/env bash
# /usr/local/bin/yedek.sh  (chmod 700)
set -euo pipefail
source /root/.restic-env
R="restic -o s3.bucket-lookup=path"

mysqldump --single-transaction --quick --routines --triggers --events --all-databases \
  | $R backup --stdin --stdin-filename mysql-tum.sql --tag mysql
$R backup /etc /var/www /home --exclude-caches --tag dosya

# Saklama: 7 günlük, 4 haftalık, 6 aylık
$R forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
$R check

cron ile

crontab

# crontab -e  (her gece 03:15; çıktı günlüğe)
15 3 * * * /usr/local/bin/yedek.sh >> /var/log/yedek.log 2>&1

systemd timer ile (önerilen)

systemd timer, sunucu kapalıyken kaçırılan çalışmayı açılışta yapar (Persistent=true) ve günlüğü journal'da tutar.

yedek.service ve yedek.timer

# /etc/systemd/system/yedek.service
[Unit]
Description=Object Storage yedegi
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/yedek.sh
Nice=10
IOSchedulingClass=idle

# /etc/systemd/system/yedek.timer
[Unit]
Description=Her gece yedek al

[Timer]
OnCalendar=*-*-* 03:15:00
RandomizedDelaySec=15m
Persistent=true

[Install]
WantedBy=timers.target

Terminal

systemctl daemon-reload
systemctl enable --now yedek.timer
systemctl list-timers yedek.timer     # sonraki çalışma
journalctl -u yedek.service -n 50     # son çalışmanın günlüğü

Yedeğin çalışmadığını fark etmemek en büyük risktir. Betiğin sonunda başarı durumunu bir izleme servisine bildirin ya da hata çıktısını e-postayla alın.

Fidye yazılımına karşı koruma

Fidye yazılımı sunucunuza girerse çoğu zaman yedek anahtarını da bulur ve yedekleri silmeye çalışır. Buna karşı bucket'ı sürüm geçmişi + object lock ile kurun:

DurumNe olur
Saldırgan dosyayı silerYalnız silme işareti (delete marker) eklenir; kilitli sürüm yerinde kalır, geri getirilir.
Saldırgan dosyanın üzerine yazarYeni sürüm oluşur; eski sürüm kilit süresince silinemez.
Saldırgan eski sürümü silmeye çalışırReddedilir: GOVERNANCE kilidini aşma yetkisi erişim anahtarlarında yoktur.
Kilit süresi dolarSürüm normal nesne gibi silinebilir; aşağıdaki kural eski sürümleri kendiliğinden temizler.

Kilitli sürümler kotaya sayıldığı için eski sürümleri, kilit süresi dolduktan kısa süre sonra silen bir yaşam döngüsü (lifecycle) kuralı ekleyin. Kuraldaki gün sayısını bucket'ın saklama süresinden biraz uzun seçin:

lifecycle.json

# lifecycle.json: eski sürümleri saklama süresinden sonra temizle
{
  "Rules": [{
    "ID": "eski-surumler",
    "Status": "Enabled",
    "Filter": { "Prefix": "" },
    "NoncurrentVersionExpiration": { "NoncurrentDays": 35 },
    "Expiration": { "ExpiredObjectDeleteMarker": true },
    "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 }
  }]
}

aws s3api put-bucket-lifecycle-configuration --bucket <bucket-adi> \
  --lifecycle-configuration file://lifecycle.json --profile sunucu

Mevcut kuralı koruyun

Bucket'larda yarım kalan parçalı yüklemeleri 7 gün sonra temizleyen hazır bir kural vardır. Kendi kuralınızı yazdığınızda yapılandırmanın tamamı değişir; örnekteki AbortIncompleteMultipartUpload satırını bu yüzden koruyun.

restic'in forget --prune komutu kilitli bucket'ta da çalışır: silinen parçalar silme işaretine dönüşür, yer kilit süresi ve yukarıdaki kural dolunca açılır. Bir dosyanın eski sürümünü geri almak için:

Terminal

# Bir nesnenin bütün sürümleri (silme işaretleri dahil)
aws s3api list-object-versions --bucket <bucket-adi> --prefix www/guncel/index.php --profile sunucu

# Belirli bir sürümü indir
aws s3api get-object --bucket <bucket-adi> --key www/guncel/index.php \
  --version-id <SURUM_NO> index.php --profile sunucu

# Silinmiş görünen nesneyi geri getir: silme işaretini (delete marker) sil
aws s3api delete-object --bucket <bucket-adi> --key www/guncel/index.php \
  --version-id <SILME_ISARETI_NO> --profile sunucu

Geri yükleme testi

Hiç denenmemiş yedek, yedek sayılmaz. Ayda bir, ve sunucuda büyük bir değişiklikten sonra, geri yüklemeyi gerçekten deneyin:

Terminal

source /root/.restic-env
R="restic -o s3.bucket-lookup=path"

# Son yedeği geçici klasöre aç ve birkaç dosyayı karşılaştır
$R restore latest --tag dosya --target /tmp/geri-yukleme-testi
diff -rq /etc /tmp/geri-yukleme-testi/etc | head

# Veritabanı dökümünü test veritabanına yükle
$R dump --tag postgres latest /postgres-uygulama.dump > /tmp/test.dump
sudo -u postgres createdb uygulama_test && sudo -u postgres pg_restore -d uygulama_test /tmp/test.dump

# Depodaki verinin bir bölümünü gerçekten okuyarak doğrula
$R check --read-data-subset 5%
  • Testi canlı veritabanının üzerine değil, ayrı bir test veritabanına yapın.
  • Geri yüklemenin ne kadar sürdüğünü not edin; felaket gününde bu süre işinizin ne kadar duracağını belirler.
  • Test bittikten sonra geçici klasörü ve test veritabanını silin.