← blog · 28 Temmuz 2026

Velero ile küme yedeği almak ve başka bir kümeye geri yüklemek

Kubernetes yedeklerinde asıl problem etcd değil, küme nesneleriyle kalıcı disk verisinin ayrı ayrı alınması. Velero mimarisi, anlık görüntü ile dosya bazlı yedek arasındaki seçim, başka bir kümeye geri yüklerken çıkan somut sorunlar ve tatbikatı otomatikleştirip RPO/RTO ölçmenin yolu.

Yedeğin yarısı diskte kalır

Kubernetes kümelerinde kurtarma planı çoğu yerde ikiye bölünmüş hâlde yaşar: bir tarafta etcd anlık görüntüsü, diğer tarafta depolama katmanının kendi disk yedekleri. Bu iki parça birbirinden habersiz alındığı sürece geri yükleme anında tutarlı bir sistem elde edemezsiniz. Nesne tanımları bir andan, disk içerikleri başka bir andan gelir; PVC bağlanacağı PV'yi bulamaz, veritabanı yarım yazılmış bir günlükle açılmaya çalışır. Velero'nun çözdüğü asıl problem "yedek almak" değil, küme nesneleriyle kalıcı disk verisini aynı işin içinde, birbirine referans veren tek bir küme hâlinde toplamaktır.

Velero neyi nasıl saklar

Velero küme içinde bir denetleyici olarak çalışır ve her şeyi CRD üzerinden yönetir. Bir Backup nesnesi oluştuğunda denetleyici API sunucusunu tarar, seçtiğiniz kaynakları JSON olarak toplar, tar arşivine koyar ve BackupStorageLocation ile tarif ettiğiniz nesne depolamaya yükler. Restore nesnesi ters yönde çalışır: arşivi indirir ve kaynakları belirli bir öncelik sırasıyla API sunucusuna basar. Varsayılan sıra CRD'lerle başlar, sonra namespace'ler, StorageClass'lar, PV ve PVC'ler, RBAC nesneleri gelir, pod'lar sona kalır.

Bu tasarımın iki pratik sonucu var. Birincisi, yedek işi kümenin içindedir; velero backup create ile bir Schedule manifesti arasında davranış farkı yoktur, dolayısıyla yedek politikanızı GitOps deposunda tutabilirsiniz. İkincisi, arşiv kümeden bağımsız bir yerde durduğu için kümeyi tamamen kaybettiğinizde yeni bir kümeye Velero kurup aynı depolama konumunu göstermeniz yeter: Velero orayı tarar ve mevcut yedekleri kendi nesneleri olarak geri senkronize eder.

Disk verisi bu arşivin içinde değildir. Onun için üç ayrı yol var ve hangisini seçtiğiniz, kümeler arası taşımanın mümkün olup olmadığını doğrudan belirler.

Anlık görüntü mü, dosya bazlı yedek mi

Sağlayıcı anlık görüntüsü. CSI sürücüsü bir VolumeSnapshot oluşturur, veri diskin arkasındaki depolama sisteminde kalır. Hızlıdır ve uygulamayı neredeyse hiç yavaşlatmaz. Kusuru şu: görüntü o depolama sistemine, çoğu zaman da o bölgeye bağlıdır. Kümeyi kaybettiğinizde görüntüler yerinde duruyorsa iş görür, ama başka bir sağlayıcıdaki kümeye geri yüklemek istediğinizde elinizde taşınabilir hiçbir şey yoktur.

Dosya bazlı yedek. Velero'nun node-agent DaemonSet'i pod'a bağlı volume'u düğüm üzerinden okur ve Kopia ile nesne depolamaya yazar. Taşınabilir, CSI anlık görüntü desteği gerektirmez, tekilleştirme ve artımlı yükleme yapar. Bedeli açık: veriyi canlı dosya sisteminden okur, yani anlık görüntü tutarlılığı yoktur. hostPath volume'lar desteklenmez, büyük dosyalarda tarama maliyeti gözle görülür hâle gelir, hiçbir pod'a bağlı olmayan PVC yedeklenmez.

CSI anlık görüntüsü artı veri taşıma. --snapshot-move-data bayrağıyla Velero önce CSI görüntüsünü alır, sonra DataUpload nesneleri üzerinden içeriği nesne depolamaya taşır ve geçici görüntüyü bırakır. Hem tutarlı bir okuma noktası hem taşınabilir bir arşiv elde edersiniz; geri yüklemede aynı işi DataDownload yapar.

Seçim kuralı sade: hedef aynı küme içinde hızlı geri alma ise sağlayıcı anlık görüntüsü yeter. Hedef başka bir kümeye ya da başka bir sağlayıcıya geri yükleme ise veri nesne depolamada olmak zorundadır, yani --snapshot-move-data veya dosya bazlı yedek. CSI sürücünüz anlık görüntü desteklemiyorsa geriye dosya bazlı yedek kalır ve tutarlılığı uygulama hook'larıyla siz sağlarsınız.

apiVersion: velero.io/v1
kind: Schedule
metadata:
  name: gecelik-uygulama
  namespace: velero
spec:
  schedule: "0 2 * * *"
  template:
    includedNamespaces:
      - uygulama
      - uygulama-veri
    snapshotMoveData: true
    ttl: 720h0m0s

Kapsamı daraltmak, ama fazla daraltmamak

Kaynak seçiminde --include-namespaces, --exclude-namespaces, --include-resources ve etiket süzgeçleri (--selector, birden fazla seçeneği "veya" mantığıyla birleştirmek için --or-selector) kullanılır. Dahil etme ölçütleri kendi içinde toplayıcıdır, hariç tutma ölçütleri her zaman üstte kalır. Bir nesneyi süzgeçlerden bağımsız olarak dışarıda bırakmak isterseniz velero.io/exclude-from-backup=true etiketi bunu her koşulda yapar.

Buradaki klasik hata namespace listesi yazıp işi bitmiş saymaktır. Namespace süzgeci küme kapsamlı nesneleri kapsamaz: CRD'ler, ClusterRole ve ClusterRoleBinding'ler, StorageClass'lar, PV'ler. Yedeği yeni bir kümeye açtığınızda uygulamanız kendi CRD'si olmadan geri gelir ve operatörünüz sessizce hiçbir şey yapmaz. --include-cluster-resources=true ile bu nesneleri kapsama alın, en azından uygulamanızın bağlı olduğu CRD'leri açıkça listeleyin.

Veritabanı gibi tutarlılık isteyen bileşenler için hook kullanın. Pod'a pre.hook.backup.velero.io/command ve post.hook.backup.velero.io/command anotasyonlarını koyarak yedekten hemen önce yazma işlemlerini dondurabilir, sonrasında serbest bırakabilirsiniz. Varsayılan zaman aşımı 30 saniyedir; büyük bir veritabanında pre.hook.backup.velero.io/timeout değerini artırmayı unutursanız hook yarıda kesilir ve varsayılan davranış gereği yedek başarısız olur.

Başka bir kümeye geri yüklerken çıkan gerçek sorunlar

StorageClass adı tutmaz. Kaynak kümede fast-ssd, hedef kümede premium-rwo olabilir. PVC eski adı istediği için sonsuza kadar Pending bekler. Çözüm Velero'nun eşleme ConfigMap'idir; adı değil etiketleri okunur:

apiVersion: v1
kind: ConfigMap
metadata:
  name: change-storage-class-config
  namespace: velero
  labels:
    velero.io/plugin-config: ""
    velero.io/change-storage-class: RestoreItemAction
data:
  fast-ssd: premium-rwo

Değiştirilemez alanlar. Servisin clusterIP değeri, StatefulSet'in volumeClaimTemplates bloğu, yerel PV'lerin nodeAffinity kuralları hedef kümede geçersizdir. Bu tür alanları geri yükleme sırasında düzeltmek için --resource-modifier-configmap ile JSON patch kuralları verebilirsiniz.

Düğüm seçicileri ve taint'ler. Kaynak kümede topology.kubernetes.io/zone ya da özel bir düğüm etiketiyle sabitlenmiş pod'lar, hedef kümede o etiketi taşıyan düğüm olmadığı için zamanlanamaz. Yedek başarılı görünür, kurtarma çalışmaz.

Ağ bağımlılıkları. LoadBalancer IP'si, Ingress host adı, harici DNS kaydı, sertifika. Geri yükleme bunları taşımaz; taşısa da yanlış olur. Kurtarma planınızın DNS adımı yoksa RTO hesabınız eksiktir.

Kabul webhook'ları. Sertifika üreten ya da pod'a yan konteyner enjekte eden bir webhook, kendisi henüz ayağa kalkmadan onun denetlediği kaynaklar geri yüklenirse API sunucusu istekleri reddeder. Böyle bir bağımlılık varsa altyapı bileşenlerini ayrı bir geri yüklemede önce açın.

Var olan kaynaklar. Velero varsayılan olarak mevcut nesnelerin üzerine yazmaz, atlar. Yarım kalmış bir geri yüklemeyi tekrar denerken bunu bilmezseniz "başarılı" raporu alıp eski nesnelerle yaşamaya devam edersiniz. Bilinçli olarak üzerine yazmak için --existing-resource-policy=update var, ama en iyi ihtimalle bile "elinden geleni yapar" mantığıyla çalışır.

Geri yüklenebildiğini kanıtlamak

Yedeğin durumu Completed olması hiçbir şey kanıtlamaz. Kanıt, yedekten kalkan bir uygulamanın istek yanıtlamasıdır. Bunu ayda bir elle yapmaya çalışan ekipler er ya da geç yapmayı bırakır, o yüzden tatbikatı bir işe dönüştürün:

velero restore create tatbikat-$(date +%s) \
  --from-backup $(velero backup get -o name | head -1 | cut -d/ -f2) \
  --namespace-mappings uygulama:tatbikat \
  --wait

--namespace-mappings sayesinde aynı kümede, izole bir namespace'te canlandırma yapabilirsiniz. Ardından bir doğrulama Job'ı çalıştırın: veritabanına bağlanıp satır sayısını okusun, en son kaydın zaman damgasını çıkarsın, uygulamanın sağlık ucuna istek atsın. Bu Job başarısız olursa tatbikat başarısızdır ve alarm üretmelidir. Doğrulama bittiğinde geçici namespace silinir.

Ölçümleri de aynı otomasyondan çıkarın. RPO'yu yedeğin status.completionTimestamp alanı ile doğrulamada bulduğunuz son kaydın zaman damgası arasındaki farktan hesaplayın; zamanlamanın kâğıt üzerindeki aralığı değil, gerçekleşen veri kaybı budur. RTO'yu geri yüklemenin başlangıç ve bitiş damgalarına DNS ve doğrulama süresini ekleyerek bulun. İki sayıyı zaman serisi olarak toplayın: yedek büyüdükçe RTO sessizce büyür ve bunu ancak grafiğe bakınca fark edersiniz.

Ne zaman bu yola girmemeli

Kurtarmak istediğiniz şey tek bir büyük veritabanıysa Velero yanlış araçtır. Veritabanının kendi sürekli arşivleme ve zaman noktasına dönme mekanizması hem daha küçük bir kurtarma penceresi hem daha güvenilir bir tutarlılık verir; Velero'yu o durumda yalnızca çevresindeki nesneler için kullanın. Tamamen durumsuz, her şeyi bir depodan uygulanan bir kümede de yedeğe ihtiyacınız yoktur, kümeyi yeniden kurmak daha hızlıdır. Terabaytlar seviyesindeki volume'larda dosya bazlı yedeğin tarama maliyeti kabul edilemez hâle gelir; orada depolama katmanının çoğaltma özelliği daha doğru cevaptır. Ve dakikalarla ölçülen bir RPO hedefiniz varsa zamanlanmış yedek bunu hiçbir ayarla karşılayamaz, senkron ya da yakın senkron çoğaltmaya bakmanız gerekir.

Velero'nun güçlü olduğu yer tam olarak ortadaki durum: çok sayıda namespace, birbirine bağlı onlarca nesne, orta boyutta kalıcı diskler ve bir kümeden diğerine taşınabilir olması gereken bir bütün.