Snapshot ile Elasticsearch Yedekleme ve Geri Yükleme

Elasticsearch cluster’ınız canlıda koşarken bir gün disklerinizden biri bozulur, yanlışlıkla kritik bir index silinir ya da bir güncelleme sonrası veriler tutarsız hale gelir. İşte tam bu noktada snapshot mekanizması hayat kurtarır. Elasticsearch’ün built-in snapshot sistemi, production ortamlarında veri kaybını önlemek için geliştirilmiş güçlü ve esnek bir yapıdır. Bu yazıda snapshot repository kurulumundan otomatik yedekleme politikalarına, geri yükleme senaryolarından hata ayıklamaya kadar her şeyi ele alacağız.

Snapshot Nedir ve Nasıl Çalışır?

Elasticsearch snapshot’ları, index verilerinin ve cluster durumunun belirli bir andaki kopyasını alır. Geleneksel dosya yedeklemelerinden farklı olarak, snapshot işlemi incremental çalışır. Yani ilk snapshot tam yedek alırken, sonraki her snapshot yalnızca değişen segment dosyalarını kaydeder. Bu özellik hem depolama alanı hem de zaman açısından büyük avantaj sağlar.

Snapshot sistemi bir repository üzerine inşa edilir. Repository, snapshot verilerinin nerede saklanacağını tanımlar. Desteklenen repository tipleri şunlardır:

  • fs (Shared File System): NFS mount veya yerel paylaşımlı dizin
  • s3: Amazon S3 veya S3 uyumlu depolama (MinIO gibi)
  • gcs: Google Cloud Storage
  • azure: Azure Blob Storage
  • hdfs: Hadoop Distributed File System

Çoğu on-premise kurulumda ya shared filesystem ya da S3 uyumlu bir çözüm kullanılır. Bu yazıda her ikisini de göstereceğiz.

Repository Kurulumu

Shared Filesystem Repository

Öncelikle her Elasticsearch node’unda snapshot dizinini oluşturup erişim izinlerini ayarlıyoruz. Eğer multi-node cluster kullanıyorsanız bu dizin NFS üzerinden tüm node’lara mount edilmiş olmalıdır.

# Snapshot dizinini oluştur
sudo mkdir -p /mnt/elasticsearch-snapshots

# Elasticsearch kullanıcısına sahiplik ver
sudo chown -R elasticsearch:elasticsearch /mnt/elasticsearch-snapshots

# İzinleri ayarla
sudo chmod 755 /mnt/elasticsearch-snapshots

Ardından elasticsearch.yml dosyasına path.repo direktifini ekliyoruz. Bu adım tüm node’larda yapılmalıdır:

# /etc/elasticsearch/elasticsearch.yml dosyasına ekle
echo 'path.repo: ["/mnt/elasticsearch-snapshots"]' >> /etc/elasticsearch/elasticsearch.yml

# Elasticsearch'ü yeniden başlat
sudo systemctl restart elasticsearch

Elasticsearch ayağa kalktıktan sonra API üzerinden repository’yi kayıt ediyoruz:

curl -X PUT "localhost:9200/_snapshot/my_backup" 
  -H 'Content-Type: application/json' 
  -d '{
    "type": "fs",
    "settings": {
      "location": "/mnt/elasticsearch-snapshots",
      "compress": true,
      "chunk_size": "1gb",
      "max_restore_bytes_per_sec": "500mb",
      "max_snapshot_bytes_per_sec": "500mb",
      "readonly": false
    }
  }'

Ayarların anlamına bakalım:

  • compress: Metadata dosyalarını sıkıştırır, segment dosyaları zaten sıkıştırılmıştır
  • chunk_size: Büyük dosyaları parçalara böler, dosya sistemi limitleri için önemli
  • max_restore_bytes_per_sec: Geri yükleme hızını throttle eder, production trafiğini korumak için kullanılır
  • max_snapshot_bytes_per_sec: Snapshot alma hızını throttle eder

Repository’nin düzgün çalışıp çalışmadığını doğrulayalım:

curl -X POST "localhost:9200/_snapshot/my_backup/_verify"

Başarılı çıktı şöyle görünür:

{
  "nodes": {
    "abc123": {
      "name": "node-1"
    }
  }
}

S3 Repository Kurulumu

S3 veya MinIO kullanacaksanız önce repository plugin’ini yüklemeniz gerekir:

# S3 plugin kurulumu (tüm node'larda yapılmalı)
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install repository-s3

# MinIO veya AWS credential ayarları
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore add s3.client.default.access_key
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore add s3.client.default.secret_key

# Elasticsearch'ü yeniden başlat
sudo systemctl restart elasticsearch

MinIO için repository tanımı:

curl -X PUT "localhost:9200/_snapshot/s3_backup" 
  -H 'Content-Type: application/json' 
  -d '{
    "type": "s3",
    "settings": {
      "bucket": "elasticsearch-backups",
      "endpoint": "minio.sirket.local:9000",
      "protocol": "http",
      "path_style_access": true,
      "base_path": "snapshots",
      "compress": true,
      "server_side_encryption": false,
      "buffer_size": "100mb",
      "max_retries": 3
    }
  }'

Manuel Snapshot Alma

Repository hazır olduğunda snapshot almak son derece basit:

# Tüm index'leri yedekle
curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_$(date +%Y%m%d_%H%M%S)?wait_for_completion=true" 
  -H 'Content-Type: application/json' 
  -d '{
    "indices": "*",
    "ignore_unavailable": true,
    "include_global_state": true,
    "metadata": {
      "taken_by": "sysadmin",
      "reason": "weekly_backup"
    }
  }'

Belirli index’leri yedeklemek için:

# Sadece log index'lerini yedekle
curl -X PUT "localhost:9200/_snapshot/my_backup/logs_snapshot_20240115?wait_for_completion=false" 
  -H 'Content-Type: application/json' 
  -d '{
    "indices": "logs-*,metrics-*",
    "ignore_unavailable": true,
    "include_global_state": false,
    "partial": false
  }'
  • ignore_unavailable: Erişilemeyen index varsa snapshot işlemini durdurmaz
  • include_global_state: Cluster ayarları, template’ler ve ILM policy’leri de yedekler
  • partial: Primary shard’ları eksik olan index’lerin de dahil edilmesine izin verir

wait_for_completion=false ile asenkron snapshot başlatabilirsiniz. Durumu takip etmek için:

curl -X GET "localhost:9200/_snapshot/my_backup/logs_snapshot_20240115"

Tüm snapshot’ları listelemek için:

curl -X GET "localhost:9200/_snapshot/my_backup/_all?verbose=true"

Snapshot Lifecycle Management (SLM) ile Otomatik Yedekleme

Elasticsearch 7.4 ile gelen SLM (Snapshot Lifecycle Management), cron benzeri bir yapıyla otomatik snapshot almayı, eski snapshot’ları silmeyi ve retention policy uygulamayı mümkün kılar. Production ortamlar için bu mekanizma manuel yedeklemeden çok daha güvenilirdir.

curl -X PUT "localhost:9200/_slm/policy/daily-snapshots" 
  -H 'Content-Type: application/json' 
  -d '{
    "schedule": "0 30 2 * * ?",
    "name": "<daily-snap-{now/d}>",
    "repository": "my_backup",
    "config": {
      "indices": ["*"],
      "ignore_unavailable": true,
      "include_global_state": true,
      "metadata": {
        "policy": "daily-snapshots"
      }
    },
    "retention": {
      "expire_after": "30d",
      "min_count": 5,
      "max_count": 50
    }
  }'

Retention ayarları:

  • expire_after: 30 günden eski snapshot’ları sil
  • min_count: Ne kadar eski olursa olsun en az 5 snapshot tut
  • max_count: Maksimum 50 snapshot sakla

SLM policy’sini hemen test etmek için:

# Policy'yi manuel tetikle
curl -X POST "localhost:9200/_slm/policy/daily-snapshots/_execute"

# SLM istatistiklerini görüntüle
curl -X GET "localhost:9200/_slm/stats"

# Son çalışma bilgisi
curl -X GET "localhost:9200/_slm/policy/daily-snapshots"

SLM otomatik silme mekanizmasını aktif etmeyi unutmayın:

curl -X PUT "localhost:9200/_cluster/settings" 
  -H 'Content-Type: application/json' 
  -d '{
    "persistent": {
      "slm.retention_schedule": "0 45 2 * * ?",
      "slm.retention_duration": "1h"
    }
  }'

Snapshot Geri Yükleme

Tüm Index’lerin Geri Yüklenmesi

Bir felaket senaryosunda tüm cluster’ı geri yüklemek için önce cluster’ı salt okunur moda alıp ardından restore işlemi başlatırız:

# Önce mevcut index'leri kapat (aynı isimde index varsa restore edilemez)
curl -X POST "localhost:9200/logs-2024.01.15/_close"

# Snapshot'ı geri yükle
curl -X POST "localhost:9200/_snapshot/my_backup/snapshot_20240115_023000/_restore?wait_for_completion=true" 
  -H 'Content-Type: application/json' 
  -d '{
    "indices": "*",
    "ignore_unavailable": true,
    "include_global_state": false,
    "include_aliases": true
  }'

Belirli Index’lerin Geri Yüklenmesi

Gerçek hayatta çoğu zaman tüm cluster değil, yanlışlıkla silinen ya da bozulan belirli bir index’i geri yüklemek gerekir:

curl -X POST "localhost:9200/_snapshot/my_backup/daily-snap-2024.01.14/_restore" 
  -H 'Content-Type: application/json' 
  -d '{
    "indices": "orders-2024.01.14",
    "ignore_unavailable": false,
    "include_global_state": false,
    "rename_pattern": "(.+)",
    "rename_replacement": "restored_$1",
    "index_settings": {
      "index.number_of_replicas": 0
    },
    "ignore_index_settings": [
      "index.refresh_interval"
    ]
  }'
  • rename_pattern / rename_replacement: Index’i farklı isimle geri yükler, bu sayede mevcut veriyi silmeden yan yana çalıştırabilirsiniz
  • index_settings: Restore sırasında index ayarlarını override eder, replica’yı 0 yaparak hızlı restore sağlar
  • ignore_index_settings: Belirtilen ayarları yok sayarak default değerleri kullanır

Restore sonrası replica sayısını tekrar ayarlıyoruz:

curl -X PUT "localhost:9200/restored_orders-2024.01.14/_settings" 
  -H 'Content-Type: application/json' 
  -d '{
    "index": {
      "number_of_replicas": 1
    }
  }'

Restore Durumunu Takip Etme

# Recovery durumunu görüntüle
curl -X GET "localhost:9200/restored_orders-2024.01.14/_recovery?human=true"

# Shard durumunu kontrol et
curl -X GET "localhost:9200/_cat/shards/restored_orders-2024.01.14?v&h=index,shard,prirep,state,docs,store,ip,node"

Gerçek Dünya Senaryosu: Yanlış Silinen Index’i Kurtarma

Diyelim ki birisi orders-2024.01 index’ini yanlışlıkla sildi ve en son snapshot 4 saat önce alındı. İşte adım adım kurtarma süreci:

# 1. Mevcut snapshot'ları listele ve en güncel olanı bul
curl -X GET "localhost:9200/_snapshot/my_backup/_all?verbose=false&pretty" | 
  grep -E '"snapshot"|"start_time"' | head -20

# 2. Snapshot içindeki index'leri kontrol et
curl -X GET "localhost:9200/_snapshot/my_backup/daily-snap-2024.01.15/_status?pretty"

# 3. Index'i geçici isimle restore et
curl -X POST "localhost:9200/_snapshot/my_backup/daily-snap-2024.01.15/_restore" 
  -H 'Content-Type: application/json' 
  -d '{
    "indices": "orders-2024.01",
    "rename_pattern": "orders-2024.01",
    "rename_replacement": "orders-2024.01-restored",
    "index_settings": {
      "index.number_of_replicas": 0,
      "index.routing.allocation.total_shards_per_node": null
    }
  }'

# 4. Restore tamamlanana kadar bekle
watch -n 5 'curl -s "localhost:9200/_cat/indices/orders-2024.01-restored?v&h=index,health,status,docs.count,store.size"'

# 5. Veriyi doğrula
curl -X GET "localhost:9200/orders-2024.01-restored/_count"

# 6. Index'i orijinal ismine alias ile bağla veya reindex et
curl -X POST "localhost:9200/_aliases" 
  -H 'Content-Type: application/json' 
  -d '{
    "actions": [
      {
        "add": {
          "index": "orders-2024.01-restored",
          "alias": "orders-2024.01"
        }
      }
    ]
  }'

Bu yaklaşımın avantajı, restore tamamlanmadan önce uygulamayı yeni alias üzerinden çalıştırmaya başlayabilirsiniz.

Snapshot Boyutunu Yönetmek

Büyük cluster’larda snapshot boyutları çok hızlı büyür. Birkaç pratik yöntem:

Index boyutunu snapshot öncesi optimize edin:

# Force merge ile segment sayısını azalt (read-only index'ler için)
curl -X POST "localhost:9200/logs-2024.01.01/_forcemerge?max_num_segments=1"

# Frozen index'leri snapshot'a dahil etmeyin
curl -X PUT "localhost:9200/_snapshot/my_backup/selective_snapshot" 
  -H 'Content-Type: application/json' 
  -d '{
    "indices": "*,-frozen_*,-.security*",
    "feature_states": [],
    "include_global_state": false
  }'

Eski snapshot’ları temizleme (SLM olmadan):

#!/bin/bash
# Belirli bir repository'de 30 günden eski snapshot'ları sil
REPOSITORY="my_backup"
CUTOFF_DATE=$(date -d "30 days ago" +%s%3N)

curl -s "localhost:9200/_snapshot/${REPOSITORY}/_all" | 
  python3 -c "
import sys, json
data = json.load(sys.stdin)
for snap in data['snapshots']:
    if snap['start_time_in_millis'] < ${CUTOFF_DATE}:
        print(snap['snapshot'])
" | while read snap_name; do
    echo "Deleting old snapshot: $snap_name"
    curl -X DELETE "localhost:9200/_snapshot/${REPOSITORY}/${snap_name}"
    sleep 2
done

Snapshot Sorunlarını Gidermek

Repository Verification Hatası

# Repository durumunu detaylı kontrol et
curl -X GET "localhost:9200/_snapshot/my_backup/_status"

# Node bazlı erişimi doğrula
curl -X POST "localhost:9200/_snapshot/my_backup/_verify?pretty"

Eğer bazı node’lar verify’da hata veriyorsa, o node’larda dizin mount durumunu kontrol edin:

# Her node'da çalıştır
df -h /mnt/elasticsearch-snapshots
ls -la /mnt/elasticsearch-snapshots
sudo -u elasticsearch touch /mnt/elasticsearch-snapshots/test_write

Snapshot Durumu FAILED Gösteriyorsa

# Başarısız snapshot detayını gör
curl -X GET "localhost:9200/_snapshot/my_backup/failed_snapshot_name?verbose=true&pretty"

# Cluster log'larını incele
sudo journalctl -u elasticsearch -n 100 --no-pager | grep -i "snapshot|repository"

# Kısmen tamamlanmış snapshot'ı temizle
curl -X DELETE "localhost:9200/_snapshot/my_backup/failed_snapshot_name"

Restore Sırasında Disk Alanı Sorunu

Restore işlemi sırasında yetersiz disk alanı klasik bir problemdir. Hesaplama yapmak için:

# Snapshot boyutunu öğren
curl -X GET "localhost:9200/_snapshot/my_backup/daily-snap-2024.01.15?verbose=true&pretty" | 
  grep -E '"size_in_bytes"'

# Disk kullanımını kontrol et
df -h /var/lib/elasticsearch
du -sh /mnt/elasticsearch-snapshots/*

Restore sırasında disk dolmaya başlarsa throttle ayarlarıyla yavaşlatabilirsiniz:

curl -X PUT "localhost:9200/_cluster/settings" 
  -H 'Content-Type: application/json' 
  -d '{
    "transient": {
      "indices.recovery.max_bytes_per_sec": "50mb"
    }
  }'

Cross-Cluster Snapshot ile DR Senaryosu

Birden fazla cluster kullanıyorsanız, snapshot’ları farklı bir cluster’a restore etmek disaster recovery için temel yaklaşımdır. DR cluster’ında sadece repository’yi readonly olarak tanımlayıp restore edebilirsiniz:

# DR cluster'ında production snapshot repository'sini readonly olarak ekle
curl -X PUT "localhost:9200/_snapshot/production_backup" 
  -H 'Content-Type: application/json' 
  -d '{
    "type": "fs",
    "settings": {
      "location": "/mnt/elasticsearch-snapshots",
      "readonly": true,
      "compress": true
    }
  }'

# Production'dan en son snapshot'ı DR cluster'ına restore et
curl -X POST "localhost:9200/_snapshot/production_backup/daily-snap-2024.01.15/_restore" 
  -H 'Content-Type: application/json' 
  -d '{
    "indices": "*",
    "ignore_unavailable": true,
    "include_global_state": false
  }'

İzleme ve Alerting

Snapshot işlemlerini izlemek için basit bir kontrol scripti:

#!/bin/bash
# /usr/local/bin/check_es_snapshots.sh

REPO="my_backup"
ES_URL="localhost:9200"
ALERT_EMAIL="[email protected]"
MAX_AGE_HOURS=25  # Son snapshot 25 saatten eski olmamalı

LATEST_SNAPSHOT=$(curl -s "${ES_URL}/_snapshot/${REPO}/_all" | 
  python3 -c "
import sys, json
data = json.load(sys.stdin)
snaps = sorted(data['snapshots'], key=lambda x: x['start_time_in_millis'], reverse=True)
if snaps:
    latest = snaps[0]
    print(f"{latest['snapshot']}|{latest['state']}|{latest['start_time_in_millis']}")
")

SNAP_NAME=$(echo $LATEST_SNAPSHOT | cut -d'|' -f1)
SNAP_STATE=$(echo $LATEST_SNAPSHOT | cut -d'|' -f2)
SNAP_TIME=$(echo $LATEST_SNAPSHOT | cut -d'|' -f3)
CURRENT_TIME=$(($(date +%s) * 1000))
AGE_HOURS=$(( (CURRENT_TIME - SNAP_TIME) / 3600000 ))

if [ "$SNAP_STATE" != "SUCCESS" ]; then
    echo "ALERT: Son snapshot ($SNAP_NAME) durumu $SNAP_STATE" | 
      mail -s "[ES ALERT] Snapshot Hatasi" $ALERT_EMAIL
elif [ $AGE_HOURS -gt $MAX_AGE_HOURS ]; then
    echo "ALERT: Son basarili snapshot $AGE_HOURS saat once alindi" | 
      mail -s "[ES ALERT] Snapshot Gec" $ALERT_EMAIL
else
    echo "OK: Son snapshot $SNAP_NAME, $AGE_HOURS saat once, durum: $SNAP_STATE"
fi

Bu scripti cron’a ekleyin:

chmod +x /usr/local/bin/check_es_snapshots.sh
echo "0 * * * * root /usr/local/bin/check_es_snapshots.sh >> /var/log/es_snapshot_check.log 2>&1" 
  >> /etc/crontab

Sonuç

Elasticsearch snapshot sistemi, doğru kurulduğunda çok güçlü bir veri koruma katmanı sunar. Dikkat edilmesi gereken birkaç kritik nokta var: Repository dizininin tüm node’lardan erişilebilir olması, path.repo ayarının her node’a eklenmiş olması ve snapshot’ların farklı bir fiziksel lokasyonda tutulması. Production’da mutlaka SLM kullanın, manuel script’lere güvenmek yerine Elasticsearch’ün kendi lifecycle mekanizmasını devreye alın. Ayrıca yedek almak kadar geri yüklemeyi de test etmek şart; ayda en az bir kez test ortamınıza restore yaparak yedeklerinizin geçerliliğini doğrulayın. Snapshot boyutları büyüdükçe S3 uyumlu bir nesne depolama çözümüne geçmek hem maliyet hem de ölçeklenebilirlik açısından mantıklı olacaktır.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir