Production’da bir şeyler patladığında ve “bunu geri almam lazım” diye düşündüğünüzde, Kubernetes’in rollback mekanizmasının varlığına şükrediyorsunuz. Ama işin güzel yanı şu: iyi bir güncelleme stratejisiyle rollback’e hiç ihtiyaç duymayabilirsiniz. Bu yazıda hem rolling update’in derinliklerine ineceğiz hem de işler ters gittiğinde nasıl hızlıca geri döneceğinizi ele alacağız.
Rolling Update Nedir ve Neden Önemlidir?
Kubernetes’te bir deployment güncellediğinizde, varsayılan davranış rolling update stratejisidir. Eski usul yöntemde tüm pod’ları durdurur, yeni versiyonu deploy eder ve servis kesintisi yaşardınız. Rolling update ise bunu kademeli yapar: eski pod’ları birer birer öldürürken yeni versiyonlu pod’ları ayağa kaldırır. Kullanıcılarınız bu geçişi neredeyse hiç hissetmez.
Ama “varsayılan davranış” deyip geçmemek lazım. Rolling update’i doğru yapılandırmazsanız, production’da ciddi sorunlarla karşılaşabilirsiniz. Yanlış maxSurge ve maxUnavailable değerleri, hazır olmayan pod’lara gelen trafik, yanlış health check yapılandırması… Bunların hepsi geceleyin sizi uyandırabilecek senaryolar.
Deployment Stratejileri
Kubernetes’te iki temel deployment stratejisi vardır:
- RollingUpdate: Kademeli güncelleme, sıfır downtime hedefi
- Recreate: Tüm pod’ları sil, sonra yenilerini oluştur (downtime kabul edilir)
Recreate stratejisi genellikle database migration gerektiren ya da eski ve yeni versiyonun aynı anda çalışamadığı senaryolarda kullanılır. Ama çoğu web servisi için RollingUpdate doğru tercihtir.
Temel Bir Deployment YAML’ı
Önce basit bir deployment ile başlayalım:
cat <<EOF > my-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
spec:
replicas: 6
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 1
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: myrepo/my-app:1.0.0
ports:
- containerPort: 8080
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
EOF
kubectl apply -f my-app-deployment.yaml
Burada dikkat etmeniz gereken birkaç kritik parametre var:
- maxSurge: Güncelleme sırasında kaç tane ekstra pod oluşturulabilir.
2değeri, 6 replica için güncelleme sürecinde maksimum 8 pod olabileceği anlamına gelir. - maxUnavailable: Güncelleme sırasında kaç pod kullanılamaz hale gelebilir.
1değeri çok koruyucu bir yaklaşım; büyük cluster’larda25%gibi yüzdelik değerler daha yaygındır. - readinessProbe: Bu olmadan Kubernetes, pod’un hazır olup olmadığını bilemez ve trafiği erken gönderebilir. Rolling update’in sağlıklı çalışması için kritiktir.
- livenessProbe: Pod’un hayatta olup olmadığını kontrol eder. Sorunlu pod’ları yeniden başlatır.
Güncelleme Nasıl Yapılır?
Deployment’ı güncellemenin birkaç yolu var. En temizi image tag’ini güncellemektir:
# Yöntem 1: kubectl set image komutu
kubectl set image deployment/my-app my-app=myrepo/my-app:1.1.0 -n production
# Yöntem 2: kubectl edit (interaktif)
kubectl edit deployment/my-app -n production
# Yöntem 3: YAML dosyasını güncelleyip apply etmek (önerilen)
sed -i 's/my-app:1.0.0/my-app:1.1.0/g' my-app-deployment.yaml
kubectl apply -f my-app-deployment.yaml
CI/CD pipeline’larında genellikle üçüncü yöntemi tercih ederim. YAML dosyanız git’te versiyonlanıyorsa hangi versiyonun ne zaman deploy edildiğini net görebilirsiniz.
Güncellemeyi İzlemek
Güncelleme başladıktan sonra sessizce beklemek yerine süreci aktif izleyin:
# Rollout durumunu izle
kubectl rollout status deployment/my-app -n production
# Pod'ların durumunu gerçek zamanlı izle
kubectl get pods -n production -l app=my-app -w
# Deployment eventlerini görüntüle
kubectl describe deployment my-app -n production
# Rollout geçmişini görüntüle
kubectl rollout history deployment/my-app -n production
kubectl rollout status komutu özellikle CI/CD pipeline’larında çok işe yarar. Güncelleme başarılı olana kadar bekler ve başarısızlık durumunda non-zero exit code döner. Pipeline’ınızda bu komutun ardından rollback mantığı ekleyebilirsiniz.
Rollback: Geri Dönüşün Sanatı
Şimdi kritik konuya gelelim. Yeni versiyonu deploy ettiniz, monitoring’de error rate fırladı, latency arttı. Ne yapacaksınız?
# Hemen önceki versiyona geri dön
kubectl rollout undo deployment/my-app -n production
# Belirli bir revizyona geri dön
kubectl rollout undo deployment/my-app --to-revision=3 -n production
# Rollout geçmişini listele (hangi revizyona döneceğinizi görmek için)
kubectl rollout history deployment/my-app -n production
# Belirli bir revizyonun detaylarını gör
kubectl rollout history deployment/my-app --revision=2 -n production
kubectl rollout undo çalıştırdığınızda Kubernetes yeni bir rolling update başlatır, ama bu sefer eski imajla. Yani geri dönüş de kademeli olur ve downtime yaşamazsınız.
Önemli not: Kubernetes varsayılan olarak son 10 revision’ı saklar. Bu revisionHistoryLimit parametresiyle kontrol edilebilir. Çok büyük bir değer vermek etcd’de gereksiz yer kaplar, çok küçük bir değer rollback seçeneklerinizi kısıtlar. 5-10 arası genellikle yeterlidir.
Gerçek Dünya Senaryosu: Otomatik Rollback
Production’da manual rollback beklemek her zaman mümkün değildir. İşte basit bir otomatik rollback script’i:
#!/bin/bash
# deploy-with-rollback.sh
DEPLOYMENT="my-app"
NAMESPACE="production"
NEW_IMAGE="myrepo/my-app:${VERSION}"
TIMEOUT=300 # 5 dakika
ERROR_THRESHOLD=5 # Yüzde cinsinden kabul edilebilir hata oranı
echo "Deploying ${NEW_IMAGE}..."
kubectl set image deployment/${DEPLOYMENT}
my-app=${NEW_IMAGE}
-n ${NAMESPACE}
echo "Waiting for rollout to complete..."
if ! kubectl rollout status deployment/${DEPLOYMENT}
-n ${NAMESPACE}
--timeout=${TIMEOUT}s; then
echo "Rollout failed or timed out! Initiating rollback..."
kubectl rollout undo deployment/${DEPLOYMENT} -n ${NAMESPACE}
echo "Waiting for rollback to complete..."
kubectl rollout status deployment/${DEPLOYMENT}
-n ${NAMESPACE}
--timeout=${TIMEOUT}s
echo "Rollback completed. Exiting with error."
exit 1
fi
echo "Deployment successful!"
# Opsiyonel: Deployment sonrası temel health check
READY_PODS=$(kubectl get deployment ${DEPLOYMENT}
-n ${NAMESPACE}
-o jsonpath='{.status.readyReplicas}')
DESIRED_PODS=$(kubectl get deployment ${DEPLOYMENT}
-n ${NAMESPACE}
-o jsonpath='{.spec.replicas}')
if [ "${READY_PODS}" != "${DESIRED_PODS}" ]; then
echo "Warning: Not all pods are ready! Ready: ${READY_PODS}/${DESIRED_PODS}"
exit 1
fi
echo "All ${READY_PODS} pods are ready. Deployment completed successfully."
Bu script’i CI/CD pipeline’ınıza ekleyerek deployment başarısız olduğunda otomatik rollback tetikleyebilirsiniz.
Daha Gelişmiş: Deployment Annotations ile Güncelleme Takibi
Rollout geçmişini daha okunabilir hale getirmek için annotation kullanın:
# Deployment yaparken neden yapıldığını kaydet
kubectl annotate deployment my-app
kubernetes.io/change-cause="v1.1.0: Redis connection pool size artırildi, memory leak fix"
-n production
# Şimdi history çok daha anlamlı
kubectl rollout history deployment/my-app -n production
# REVISION CHANGE-CAUSE
# 1 v1.0.0: Initial deployment
# 2 v1.1.0: Redis connection pool size artırildi, memory leak fix
Eğer YAML üzerinden yönetiyorsanız, annotation’ı template’e ekleyin:
cat <<EOF > my-app-deployment-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
annotations:
kubernetes.io/change-cause: "v1.1.0: Redis connection pool fix, memory leak giderildi"
spec:
replicas: 6
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 1
revisionHistoryLimit: 5
template:
metadata:
labels:
app: my-app
version: "1.1.0"
spec:
containers:
- name: my-app
image: myrepo/my-app:1.1.0
ports:
- containerPort: 8080
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
EOF
kubectl apply -f my-app-deployment-v2.yaml
Güncellemeyi Duraklatmak ve Devam Ettirmek
Bazen güncellemeyi kademeli olarak yapmak istersiniz. Örneğin önce birkaç pod’u güncelleyip metrics’e bakarsınız, iyiyse devam edersiniz:
# Güncellemeyi başlat
kubectl set image deployment/my-app my-app=myrepo/my-app:1.2.0 -n production
# Güncellemeyi durdur (canary benzeri kontrollü geçiş için)
kubectl rollout pause deployment/my-app -n production
# Bu noktada hem eski hem yeni versiyonlu pod'lar çalışıyor
# Metrics'e bak, log'ları incele
kubectl get pods -n production -l app=my-app
# Her şey iyiyse devam et
kubectl rollout resume deployment/my-app -n production
# Sorun varsa geri dön
kubectl rollout undo deployment/my-app -n production
Bu yaklaşım basit bir canary deployment simülasyonu yapmanızı sağlar. Daha sofistike canary ihtiyaçlarınız için Argo Rollouts veya Flagger gibi araçlara bakabilirsiniz, ama temel senaryolar için bu yeterlidir.
Readiness Gate ile Daha Güvenli Güncellemeler
Kubernetes 1.14’ten itibaren gelen readinessGates özelliği, pod’un hazır sayılması için external condition’lar eklemenize olanak tanır. Örneğin load balancer’ın pod’u tam olarak kaydetmesini bekleyebilirsiniz:
kubectl patch deployment my-app -n production --patch '
{
"spec": {
"template": {
"spec": {
"readinessGates": [
{
"conditionType": "target-health.alb.ingress.k8s.aws/my-app"
}
]
}
}
}
}'
Bu özellikle AWS ALB veya GCP Load Balancer kullananlar için çok önemlidir. Normal readiness probe pod’un hazır olduğunu söyler, ama load balancer henüz pod’u hedef listesine almamış olabilir. Bu durumda trafik kaybı yaşanabilir.
Sık Yapılan Hatalar ve Çözümleri
Image pull policy sorunları: latest tag kullanıyorsanız imagePullPolicy: Always olmadan Kubernetes her zaman yeni imajı çekmez.
# Yanlış yaklaşım - production'da latest kullanmayın
# image: myrepo/my-app:latest
# Doğru yaklaşım - her zaman spesifik tag kullanın
kubectl set image deployment/my-app
my-app=myrepo/my-app:1.2.0
-n production
Yanlış replica sayısı: Çok az replica ile rolling update yaparsanız güncelleme sırasında kapasite düşebilir.
# Mevcut replica sayısını kontrol et
kubectl get deployment my-app -n production
# Gerekirse scale up yap, sonra güncelle
kubectl scale deployment my-app --replicas=6 -n production
kubectl set image deployment/my-app my-app=myrepo/my-app:1.2.0 -n production
Resource limit eksikliği: Resource limit yoksa yeni pod’lar node’da yer bulamayabilir ve güncelleme takılır:
# Node resource kullanımını kontrol et
kubectl describe nodes | grep -A 5 "Allocated resources"
# Pod'ların resource isteğini kontrol et
kubectl get pods -n production -o custom-columns=
'NAME:.metadata.name,CPU-REQUEST:.spec.containers[0].resources.requests.cpu,MEM-REQUEST:.spec.containers[0].resources.requests.memory'
Monitoring ile Entegrasyon
Rollback kararını manuel almak yerine monitoring sisteminizle entegre edebilirsiniz. İşte Prometheus metric’i kontrol eden basit bir örnek:
#!/bin/bash
# health-check-after-deploy.sh
DEPLOYMENT="my-app"
NAMESPACE="production"
PROMETHEUS_URL="http://prometheus.monitoring.svc:9090"
CHECK_DURATION=120 # 2 dakika boyunca kontrol et
CHECK_INTERVAL=10 # Her 10 saniyede bir kontrol et
ERROR_THRESHOLD="0.05" # %5 hata oranı eşiği
echo "Starting post-deployment health monitoring for ${DEPLOYMENT}..."
end_time=$((SECONDS + CHECK_DURATION))
while [ $SECONDS -lt $end_time ]; do
# Prometheus'tan hata oranını sorgula
ERROR_RATE=$(curl -s "${PROMETHEUS_URL}/api/v1/query"
--data-urlencode
'query=rate(http_requests_total{status=~"5.."}[1m]) / rate(http_requests_total[1m])'
| python3 -c "import sys,json; data=json.load(sys.stdin);
print(data['data']['result'][0]['value'][1] if data['data']['result'] else '0')"
2>/dev/null || echo "0")
echo "Current error rate: ${ERROR_RATE}"
# Hata oranı eşiği aşıldıysa rollback yap
if (( $(echo "${ERROR_RATE} > ${ERROR_THRESHOLD}" | bc -l) )); then
echo "Error rate ${ERROR_RATE} exceeds threshold ${ERROR_THRESHOLD}!"
echo "Initiating automatic rollback..."
kubectl rollout undo deployment/${DEPLOYMENT} -n ${NAMESPACE}
kubectl rollout status deployment/${DEPLOYMENT} -n ${NAMESPACE}
echo "Rollback completed."
exit 1
fi
sleep ${CHECK_INTERVAL}
done
echo "Health monitoring completed. No issues detected."
exit 0
PodDisruptionBudget ile Güvenlik Ağı
Rolling update sırasında cluster’da başka şeyler de olabilir: node bakımı, otomatik ölçeklendirme gibi. PodDisruptionBudget (PDB) ile minimum kapasitenizi garanti altına alabilirsiniz:
cat <<EOF | kubectl apply -f -
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
namespace: production
spec:
minAvailable: 4
selector:
matchLabels:
app: my-app
EOF
# PDB durumunu kontrol et
kubectl get pdb -n production
kubectl describe pdb my-app-pdb -n production
Bu konfigürasyonla, herhangi bir voluntary disruption sırasında en az 4 pod’un ayakta kalması garantilenir. 6 replica’nız varsa bu, aynı anda en fazla 2 pod’un kaldırılabileceği anlamına gelir.
Sonuç
Kubernetes’te güncelleme stratejileri bir kez kurup unutacağınız bir şey değil. Her uygulamanın kendine özgü ihtiyaçları var ve bu parametreleri ona göre ayarlamak gerekiyor. Bir CRUD API ile stateful bir servisin güncelleme stratejisi aynı olmak zorunda değil.
Temel prensipleri özetlersek:
- readinessProbe olmadan rolling update yapmayın. Bu, işlerin ters gitmesinin en yaygın nedenidir.
- Her deployment’ı annotation ile belgeleyin. İki hafta sonra hangi değişikliğin ne zaman yapıldığını bulmak zorunda kalacaksınız.
- maxSurge ve maxUnavailable değerlerini uygulamanıza göre ayarlayın. Yüksek trafikli servisler için daha koruyucu değerler tercih edin.
- Rollback mekanizmasını test edin. Gerçek kriz anında “acaba çalışır mı” diye düşünmek istemezsiniz. Staging ortamında rollback senaryolarını düzenli olarak deneyin.
- revisionHistoryLimit’i mantıklı bir değere ayarlayın. Çok düşük değer rollback seçeneklerinizi kısıtlar, çok yüksek değer gereksiz kaynak tüketir.
- PodDisruptionBudget kullanın. Özellikle kritik servisler için bu bir lüks değil, zorunluluktur.
Rolling update ve rollback mekanizmaları, Kubernetes’in en güçlü özelliklerinden biri. Bu araçları doğru kullandığınızda kullanıcılarınız güncellemenizi fark etmeden sistemi iyileştirmeye devam edebilirsiniz. Ve bir şeyler ters gittiğinde de birkaç komutla eski kararlı versiyona dönebilirsiniz. Production’da bu kadar kontrol sahibi olmak gerçekten değerli bir şey.