Kubernetes Cluster Felaket Kurtarma: etcd Yedekleme ve Hızlı Geri Yükleme Rehberi
Sabah 3’te telefonun çaldığını ve nöbetçi mühendisinin “production Kubernetes cluster’ımız çöktü, etcd corrupted görünüyor” dediğini düşün. Bu tam olarak yaşamak istemediğin ama hazırlıklı olmak zorunda olduğun senaryo. etcd, Kubernetes’in beyni sayılır; cluster state’i, pod tanımları, secret’lar, configmap’ler, RBAC kuralları, hepsi burada saklanır. etcd giderse cluster gider. Bu yüzden etcd yedekleme stratejisi bir “güzel olur” değil, bir “olmazsa olmaz” gereksinimdir.
Bu yazıda production ortamında gerçekten işe yarayan etcd yedekleme ve felaket kurtarma süreçlerini ele alacağız. Teorik değil, sahada test edilmiş yaklaşımlar bunlar.
etcd Neden Bu Kadar Kritik?
Kubernetes control plane bileşenlerinden kube-apiserver, kube-scheduler ve kube-controller-manager stateless çalışır; yani çökerlerse yeniden başlatmak yeterlidir. Ama etcd öyle değil. etcd, cluster’ın tüm istenen durumunu (desired state) ve mevcut durumunu (current state) tutan distributed key-value store’dur.
etcd kaybedildiğinde şunlar olur:
- Çalışan pod’lar çalışmaya devam eder ama yönetilemez hale gelir
- Yeni deployment yapılamaz
- Scale operasyonları çalışmaz
- kubectl komutları yanıt vermez
- Node’lar cluster’a kayıt olamaz
Kısaca, cluster fiziksel olarak ayakta olsa bile işlevsiz kalır. Bu yüzden etcd yedeklemesi olmayan bir cluster, yedeklemesi olmayan production veritabanından farksızdır.
etcd Mimarisini Anlamak
Yedekleme yapmadan önce ne yedeklediğimizi anlamak önemli. etcd tipik olarak Kubernetes master node’larında çalışır ve production ortamlarında genellikle 3 veya 5 node’luk bir HA cluster oluşturur. Raft consensus algoritması kullanır, yani yazma işlemleri için çoğunluk (quorum) gerekir.
Yedekleme işlemi her zaman tek bir etcd üyesinden snapshot alınarak yapılır. Bu snapshot, o anki cluster state’in tutarlı bir kopyasıdır. Geri yükleme ise tüm etcd cluster’ını bu snapshot’tan yeniden oluşturmayı gerektirir.
etcd cluster bilgilerini kontrol etmek için:
# etcd pod'larının durumunu görmek
kubectl get pods -n kube-system | grep etcd
# etcd endpoint'lerini listele
ETCDCTL_API=3 etcdctl
--endpoints=https://127.0.0.1:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
member list
# Cluster health durumu
ETCDCTL_API=3 etcdctl
--endpoints=https://127.0.0.1:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
endpoint health
Manuel etcd Snapshot Alma
En temel yedekleme yöntemi etcdctl snapshot save komutudur. Bu komutu master node’dan çalıştırırsın:
SNAPSHOT_DATE=$(date +%Y%m%d_%H%M%S)
SNAPSHOT_FILE="/backup/etcd/etcd-snapshot-${SNAPSHOT_DATE}.db"
ETCDCTL_API=3 etcdctl snapshot save ${SNAPSHOT_FILE}
--endpoints=https://127.0.0.1:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
# Snapshot doğrulama - bu adımı ASLA atlama
ETCDCTL_API=3 etcdctl snapshot status ${SNAPSHOT_FILE}
--write-out=table
Snapshot status çıktısında hash, revision, total keys ve total size bilgilerini görürsün. Revision numarası her yazma işlemiyle artar, bu yüzden production cluster’ında bu sayının milyonlarda olması normal. Total size genellikle birkaç yüz MB ile birkaç GB arasında değişir; cluster’ın büyüklüğüne ve içindeki obje sayısına göre.
Otomatik Yedekleme Script’i
Manuel yedekleme afet anları için değil, periyodik otomatik yedekleme için bir script yazmak şart. İşte gerçekçi bir production-ready script:
#!/bin/bash
# /usr/local/bin/etcd-backup.sh
set -euo pipefail
# Konfigürasyon
BACKUP_DIR="/backup/etcd"
RETENTION_DAYS=7
ETCD_ENDPOINTS="https://127.0.0.1:2379"
ETCD_CACERT="/etc/kubernetes/pki/etcd/ca.crt"
ETCD_CERT="/etc/kubernetes/pki/etcd/server.crt"
ETCD_KEY="/etc/kubernetes/pki/etcd/server.key"
S3_BUCKET="s3://your-company-k8s-backups/etcd"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
HOSTNAME=$(hostname -s)
SNAPSHOT_NAME="etcd-${HOSTNAME}-${TIMESTAMP}.db"
SNAPSHOT_PATH="${BACKUP_DIR}/${SNAPSHOT_NAME}"
LOG_FILE="/var/log/etcd-backup.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a ${LOG_FILE}
}
# Backup dizini oluştur
mkdir -p ${BACKUP_DIR}
log "etcd snapshot alınıyor: ${SNAPSHOT_NAME}"
# Snapshot al
ETCDCTL_API=3 etcdctl snapshot save ${SNAPSHOT_PATH}
--endpoints=${ETCD_ENDPOINTS}
--cacert=${ETCD_CACERT}
--cert=${ETCD_CERT}
--key=${ETCD_KEY}
# Doğrulama
SNAPSHOT_HASH=$(ETCDCTL_API=3 etcdctl snapshot status ${SNAPSHOT_PATH}
--write-out=json | jq -r '.hash')
if [ -z "${SNAPSHOT_HASH}" ]; then
log "HATA: Snapshot doğrulaması başarısız!"
exit 1
fi
log "Snapshot hash: ${SNAPSHOT_HASH} - Doğrulama başarılı"
# Sıkıştır
gzip ${SNAPSHOT_PATH}
COMPRESSED_FILE="${SNAPSHOT_PATH}.gz"
# S3'e yükle
log "S3'e yükleniyor: ${S3_BUCKET}"
aws s3 cp ${COMPRESSED_FILE} "${S3_BUCKET}/${SNAPSHOT_NAME}.gz"
--storage-class STANDARD_IA
# Eski local backup'ları temizle
find ${BACKUP_DIR} -name "*.db.gz" -mtime +${RETENTION_DAYS} -delete
log "Eski yedekler temizlendi (${RETENTION_DAYS} günden eski)"
log "Yedekleme tamamlandı: ${COMPRESSED_FILE}"
Bu script’i crontab’a ekle, her master node’da ayrı çalıştır ama S3’e aynı bucket’a yazmasını sağla. Böylece hangi master’dan alındığını hostname ile anlayabilirsin.
# /etc/cron.d/etcd-backup
# Her 6 saatte bir yedekle
0 */6 * * * root /usr/local/bin/etcd-backup.sh
Kubernetes CronJob ile Yedekleme
Eğer etcd pod olarak çalışıyorsa (yani static pod değilse veya managed Kubernetes kullanıyorsan), yedeklemeyi Kubernetes CronJob olarak da yapılandırabilirsin:
apiVersion: batch/v1
kind: CronJob
metadata:
name: etcd-backup
namespace: kube-system
spec:
schedule: "0 */6 * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
hostNetwork: true
nodeSelector:
node-role.kubernetes.io/control-plane: ""
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
containers:
- name: etcd-backup
image: bitnami/etcd:3.5.9
command:
- /bin/sh
- -c
- |
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-${TIMESTAMP}.db
--endpoints=https://127.0.0.1:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
volumeMounts:
- name: etcd-certs
mountPath: /etc/kubernetes/pki/etcd
readOnly: true
- name: backup-dir
mountPath: /backup
volumes:
- name: etcd-certs
hostPath:
path: /etc/kubernetes/pki/etcd
- name: backup-dir
hostPath:
path: /backup/etcd
restartPolicy: OnFailure
Felaket Kurtarma: etcd Geri Yükleme
Şimdi asıl kritik kısma geldik. Geri yükleme senaryosunu canlı ortamda test etmemiş bir yedekleme planı yoktur. Aşağıdaki adımları önce staging ortamında mutlaka uygula.
Senaryo: Tüm etcd veritabanı bozuldu, cluster yanıt vermiyor.
Adım 1: Hazırlık
# Tüm master node'larda etcd'yi durdur
# Kubeadm kurulumunda etcd static pod olarak çalışır
# Static pod manifest'ini taşıyarak durdurabilirsin
# Master node'da:
mkdir -p /tmp/k8s-manifests-backup
mv /etc/kubernetes/manifests/etcd.yaml /tmp/k8s-manifests-backup/
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/k8s-manifests-backup/
# kubelet etcd pod'unu otomatik durduracak
# Birkaç saniye bekle
sleep 10
# etcd process'inin durduğunu doğrula
ps aux | grep etcd | grep -v grep
Adım 2: Snapshot’tan Geri Yükleme
# Compressed backup'ı uncompress et
gunzip /backup/etcd-master1-20240115_060000.db.gz
# Veya S3'ten indir
aws s3 cp s3://your-company-k8s-backups/etcd/etcd-master1-20240115_060000.db.gz /tmp/
gunzip /tmp/etcd-master1-20240115_060000.db.gz
# Mevcut etcd data dizinini yedekle (ihtiyaç olursa diye)
mv /var/lib/etcd /var/lib/etcd.bak.$(date +%Y%m%d)
# Snapshot'tan geri yükle
# HA cluster'da her member için farklı --initial-cluster-token ve
# doğru --name ve --initial-advertise-peer-urls kullanmalısın
ETCDCTL_API=3 etcdctl snapshot restore /tmp/etcd-master1-20240115_060000.db
--name master1
--initial-cluster "master1=https://192.168.1.10:2380,master2=https://192.168.1.11:2380,master3=https://192.168.1.12:2380"
--initial-cluster-token etcd-cluster-restored-$(date +%Y%m%d)
--initial-advertise-peer-urls https://192.168.1.10:2380
--data-dir /var/lib/etcd
# Ownership'i düzelt
chown -R etcd:etcd /var/lib/etcd 2>/dev/null || true
HA cluster’da bu restore komutunu her master node’da çalıştırman gerekir. Her node için --name ve --initial-advertise-peer-urls değerleri o node’a özgü olmalı, ama --initial-cluster-token aynı olmalı.
Adım 3: Control Plane’i Yeniden Başlatma
# Static pod manifest'lerini geri taşı
mv /tmp/k8s-manifests-backup/etcd.yaml /etc/kubernetes/manifests/
mv /tmp/k8s-manifests-backup/kube-apiserver.yaml /etc/kubernetes/manifests/
# kubelet pod'ları otomatik başlatacak
# etcd'nin başlamasını bekle
sleep 30
# Cluster durumunu kontrol et
ETCDCTL_API=3 etcdctl
--endpoints=https://127.0.0.1:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
endpoint health
# Kubernetes'in ayağa kalkmasını bekle
kubectl get nodes
kubectl get pods -n kube-system
Felaket Kurtarma Tatbikatı
Yedekleme planının gerçekten çalıştığını doğrulamanın tek yolu düzenli tatbikattır. Altı ayda bir aşağıdaki tatbikatı staging ortamında yapmanı öneririm:
#!/bin/bash
# dr-test.sh - Felaket kurtarma tatbikat script'i
echo "=== FELAKET KURTARMA TATBİKATI BAŞLIYOR ==="
echo "Ortam: STAGING"
echo "Tarih: $(date)"
# 1. Mevcut cluster state'i kaydet
echo "--- Mevcut cluster state kaydediliyor ---"
kubectl get all --all-namespaces > /tmp/dr-test-before-state.txt
kubectl get configmaps --all-namespaces >> /tmp/dr-test-before-state.txt
kubectl get secrets --all-namespaces >> /tmp/dr-test-before-state.txt
NAMESPACE_COUNT=$(kubectl get namespaces --no-headers | wc -l)
POD_COUNT=$(kubectl get pods --all-namespaces --no-headers | wc -l)
echo "Namespace sayısı: ${NAMESPACE_COUNT}"
echo "Pod sayısı: ${POD_COUNT}"
# 2. Test snapshot al
echo "--- Test snapshot alınıyor ---"
ETCDCTL_API=3 etcdctl snapshot save /tmp/dr-test-snapshot.db
--endpoints=https://127.0.0.1:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
ETCDCTL_API=3 etcdctl snapshot status /tmp/dr-test-snapshot.db --write-out=table
echo "=== SNAPSHOT ALINMA SÜRESİ ÖLÇÜLDÜ ==="
echo "Tatbikat tamamlandı - Restore adımları manuel olarak test edilmeli"
Velero ile Uygulama Seviyesi Yedekleme
etcd snapshot cluster state’ini yedekler ama persistent volume verilerini kapsamaz. Production’da Velero kullanarak uygulama seviyesi yedekleme de yapmalısın:
# Velero kurulumu (S3 backend ile)
velero install
--provider aws
--plugins velero/velero-plugin-for-aws:v1.7.0
--bucket your-velero-backup-bucket
--backup-location-config region=eu-west-1
--snapshot-location-config region=eu-west-1
--secret-file ./credentials-velero
# Günlük otomatik backup schedule oluştur
velero schedule create daily-backup
--schedule="0 2 * * *"
--ttl 168h
--include-namespaces production,staging
# Manuel backup al
velero backup create pre-upgrade-backup
--include-namespaces production
--wait
# Backup durumunu kontrol et
velero backup describe pre-upgrade-backup --details
İzleme ve Alerting
Yedeklemenin çalışıp çalışmadığını izlemek, yedekleme kadar önemli. Prometheus ile basit bir monitoring ekleyebilirsin:
# etcd metrics endpoint'ini kontrol et
curl -sk https://127.0.0.1:2381/metrics | grep etcd_server_
# Son backup zamanını track etmek için custom metric
# /usr/local/bin/backup-metric-exporter.sh
#!/bin/bash
LATEST_BACKUP=$(ls -t /backup/etcd/*.db.gz 2>/dev/null | head -1)
if [ -n "${LATEST_BACKUP}" ]; then
BACKUP_AGE=$(($(date +%s) - $(stat -c %Y ${LATEST_BACKUP})))
echo "# HELP etcd_backup_age_seconds Son etcd backup'ının yaşı saniye cinsinden"
echo "# TYPE etcd_backup_age_seconds gauge"
echo "etcd_backup_age_seconds ${BACKUP_AGE}"
else
echo "etcd_backup_age_seconds 999999"
fi
Prometheus alerting kuralı ekle: backup 12 saatten eskiyse alarm gönder. Bu sayede crontab sessizce başarısız olduğunda haberin olur.
Sık Yapılan Hatalar
Yıllar içinde gördüğüm en kritik hatalar şunlar:
- Yedeklemeyi test etmemek: “Yedek var” demek ile “yedekten restore ettim ve çalıştı” çok farklı. Her yedekleme planı düzenli restore testi içermeli.
- Sertifika dosyalarını yedeklememek: etcd snapshot’ı /etc/kubernetes/pki/ diziniyle birlikte yedeklemelisin. Cluster’ı yeni node’da ayağa kaldırmak için bu sertifikalara ihtiyacın var.
- Tek lokasyona yedeklemek: S3 kullanıyorsan versioning ve cross-region replication aktif et. Master node’daki yerel backup her şeyden önce düşebilir.
- etcd defrag’ı unutmak: etcd zamanla space kullanımını artırır. Düzenli
etcdctl defragveetcdctl compactyapmak hem performansı artırır hem snapshot boyutunu küçültür.
- HA cluster’da yanlış restore yapmak: Tek node’dan restore yaparken diğer node’ların eski data dizinlerini temizlemeden başlatmak split-brain’e yol açar. Tüm node’ların data dizinlerini silip hepsini aynı snapshot’tan restore etmelisin.
RTO ve RPO Hedefleri
Felaket kurtarma planı somut hedefler içermelidir:
- RPO (Recovery Point Objective): Ne kadar veri kaybını tolere edebilirsin? 6 saatlik yedekleme aralığı, en fazla 6 saatlik veri kaybı anlamına gelir. Kritik production ortamları için saatlik yedekleme düşünmeli.
- RTO (Recovery Time Objective): Sistem ne kadar sürede ayağa kalkmalı? etcd restore işlemi genellikle 15-30 dakika sürer ama buna yeni node provisioning ve uygulama doğrulama süresini de ekle. Gerçekçi bir RTO 1-2 saat olabilir.
Bu hedefleri belirleyip tatbikatlarda ölç. Tatbikatta 3 saat süren bir restore, alarm çalmadan saate bakarak bekleme anlamına gelir.
Sonuç
etcd yedekleme ve felaket kurtarma, Kubernetes operasyonunun en az göz atılan ama en kritik parçasından biri. Yedekleme script’ini yaz, crontab’a ekle, S3’e at ve unut alma. Altı ayda bir staging’de restore tatbikatı yap, RTO/RPO hedeflerini ölç, sertifika dosyalarını da yedeklediğinden emin ol.
Gerçek bir felakette paniklememen için şimdiden prova yapman gerekiyor. Üç yıl önce yaşadığım bir üretim olayında etcd restore dokümantasyonu olmayan bir ekip vardı; o gece 8 saat boyunca stack overflow okudular. Senin ekibinin aynı kaderi yaşamaması için bu adımları bugün belgele, test et ve ekibinle paylaş.
Yedekleme olmayan bir cluster, zamanlanmış bir felaket bekleyen bir cluster demektir. Senin tercihini yapman gerekiyor: şimdiden 2 saat mi harca, yoksa o 3’lük telefonu mu bekle?
