Blue-Green ve Canary Deployment Stratejileri
Giriş — "Deploy Etmek" Neden Bu Kadar Stresli?
Cuma akşamı saat 18:00. Yeni feature hazır, testler geçiyor, PR onaylandı. "Deploy edelim" diyorsun. Herkes sessizleşiyor. Birileri "Cuma deploy yapılır mı?" diyor. Neden? Çünkü deploy = risk. Kullanıcılar o an sistemde, bir şey bozulursa herkes etkilenir, geri almak vakit alır.
Deployment stratejileri tam da bu sorunu çözer. Uygulamanın yeni versiyonunu güvenli, kontrollü ve geri alınabilir şekilde canlıya alma yöntemleridir. Doğru stratejiyi seçersen Cuma akşamı bile deploy edebilirsin — gerçekten.
Bu derste üç temel stratejiyi (Rolling Update, Blue-Green, Canary) detaylıca öğrenecek, Spring Boot + Docker ile uygulayacak ve production-ready deployment pipeline kurmanın temellerini atacağız.
Deployment'ın Riskleri
Bir deployment sırasında iki ana risk vardır:
1. Downtime (Kesinti): Eski versiyon kapanır, yeni versiyon ayağa kalkar. Aradaki boşlukta kullanıcılar sisteme erişemez. Saniyeler bile olsa, e-ticaret sitesinde binlerce lira kayıp demek.
2. Hatalı Release: Yeni versiyon bir bug içeriyor. Testlerde yakalanamayan bir edge case, production verisinde patlıyor. Tüm kullanıcılar etkileniyor.
| Risk | Kötü Senaryo | İyi Strateji |
|---|---|---|
| Downtime | 5 dakika kesinti, 10K istek kayıp | Zero-downtime deployment |
| Hatalı release | Tüm kullanıcılar bug'ı görüyor | Kademeli rollout + hızlı rollback |
| Geri alma zorluğu | DB migration geri alınamıyor | Blue-Green + backward compatible changes |
| Config hatası | Yeni env variable eksik, uygulama başlamıyor | Health check + otomatik rollback |
Analoji — Restoran Mutfak Yenileme
Deployment stratejilerini anlamak için bir restoran düşün. Mutfağı yeniliyorsun ama müşterilere hizmet vermeye devam etmen lazım.
Recreate (Naif Yaklaşım): Restoranı kapat, mutfağı yenile, tekrar aç. Basit ama müşteriler gitti.
Rolling Update: Mutfağın bir tezgâhını yenile, o sırada diğer tezgâhlarda yemek yapmaya devam et. Kapasite biraz düşer ama hizmet kesilmez.
Blue-Green: Yan binada ikinci bir mutfak kur. Her şey hazır olduğunda müşterileri yeni mutfağa yönlendir. Sorun olursa eski mutfağa geri dön — 10 saniyede.
Canary: Yeni mutfakta sadece 2 masaya servis yap. Şikâyet gelmezse 5 masaya çık, sonra 10, sonra herkes yeni mutfaktan yesin.
Strateji 1: Rolling Update
Nasıl Çalışır?
Rolling update, instance'ları (pod, container, server) tek tek günceller. 4 instance varsa:
Zaman 0: [v1] [v1] [v1] [v1] ← Tüm instance'lar eski
Zaman 1: [v2] [v1] [v1] [v1] ← İlk instance güncellendi
Zaman 2: [v2] [v2] [v1] [v1] ← İkincisi güncellendi
Zaman 3: [v2] [v2] [v2] [v1] ← Üçüncüsü güncellendi
Zaman 4: [v2] [v2] [v2] [v2] ← Tümü yeni versiyondaHer adımda bir instance kapatılır, yerine yeni versiyonu olan instance ayağa kaldırılır. Load balancer, kapalı instance'a trafik göndermez.
Avantajlar ve Dezavantajlar
| Avantaj | Dezavantaj |
|---|---|
| Zero-downtime (doğru yapılırsa) | Geçiş sırasında v1 ve v2 aynı anda çalışır |
| Ekstra kaynak gerektirmez | API uyumsuzluğu varsa sorun çıkar |
| Basit, Kubernetes'te varsayılan | Rollback yavaş (tekrar tek tek geri al) |
Ne Zaman Kullanılır?
Rolling update, backward compatible değişiklikler için idealdir. Yeni versiyon, eski versiyonla aynı veritabanı şemasını ve API kontratını kullanıyorsa sorunsuz çalışır. Küçük ve sık deploy yapan ekipler için varsayılan tercih budur.
⚠️ Dikkat: Rolling update sırasında v1 ve v2 aynı anda request alır. Eğer v2'de bir API field'ı kaldırdıysan veya DB'de kolon sildiysen, v1 instance'ları patlayabilir. Her zaman backward compatible değişiklik yap.
Strateji 2: Blue-Green Deployment
Nasıl Çalışır?
Blue-Green deployment'ta iki tam kopya ortam vardır. Biri aktif (kullanıcılara hizmet veriyor), diğeri pasif (bekleme modunda).
┌─────────────┐
│ Load Balancer│
└──────┬──────┘
│
┌────────────┼────────────┐
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ BLUE (v1) │ │ GREEN (v2) │
│ ● Aktif │ │ ○ Pasif │
│ 4 instance │ │ 4 instance │
└─────────────┘ └─────────────┘Deploy süreci:
Green ortama yeni versiyonu deploy et
Green'de testleri çalıştır (smoke test, health check)
Load balancer'ı Green'e yönlendir → Switch!
Sorun varsa → Load balancer'ı Blue'ya geri yönlendir → Instant Rollback!
Her şey yolundaysa → Blue'yu sonraki deploy için hazırla
Avantajlar ve Dezavantajlar
| Avantaj | Dezavantaj |
|---|---|
| Anında rollback — saniyeler içinde | 2x kaynak maliyeti — iki ortam |
| Zero-downtime, temiz geçiş | Veritabanı paylaşımı karmaşık |
| Production ortamında test imkânı | State'li uygulamalarda zor (session, cache) |
Spring Boot + Docker ile Blue-Green Örneği
Basit bir Spring Boot uygulaması ve Docker Compose ile blue-green deploy yapalım:
// src/main/java/com/example/demo/HealthController.java
@RestController
public class HealthController {
@Value("${app.version:unknown}")
private String appVersion;
@GetMapping("/info")
public Map<String, String> info() {
return Map.of(
"version", appVersion,
"timestamp", Instant.now().toString()
);
}
}# docker-compose.yml
version: '3.8'
services:
app-blue:
image: myapp:1.0.0
container_name: app-blue
ports:
- "8081:8080"
environment:
- APP_VERSION=1.0.0
- SPRING_PROFILES_ACTIVE=production
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 10s
timeout: 5s
retries: 3
app-green:
image: myapp:2.0.0
container_name: app-green
ports:
- "8082:8080"
environment:
- APP_VERSION=2.0.0
- SPRING_PROFILES_ACTIVE=production
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 10s
timeout: 5s
retries: 3
nginx:
image: nginx:alpine
container_name: lb
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app-blue
- app-greenNGINX konfigürasyonu — switch burada yapılır:
# nginx.conf
events { worker_connections 1024; }
http {
upstream app_active {
server app-blue:8080; # Switch: "app-green:8080" yap
}
server {
listen 80;
location / {
proxy_pass http://app_active;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}Switch yapmak için nginx.conf içindeki upstream'i app-green:8080 olarak değiştir ve docker exec lb nginx -s reload çalıştır. Rollback için tekrar app-blue:8080 yap. Gerçek ortamda bu işlem bir bash script'i ile otomatize edilir:
#!/bin/bash
# blue-green-switch.sh
ACTIVE_ENV=$1 # "blue" veya "green"
if [ "$ACTIVE_ENV" == "green" ]; then
TARGET="app-green:8080"
else
TARGET="app-blue:8080"
fi
# Yeni ortamın sağlığını kontrol et
HEALTH=$(curl -s "http://localhost:${TARGET%%:*}/actuator/health" | jq -r '.status')
if [ "$HEALTH" != "UP" ]; then
echo "HATA: $TARGET sağlıklı değil! Switch iptal."
exit 1
fi
# NGINX upstream güncelle ve reload
sed -i "s/server app-[a-z]*:8080/server $TARGET/" nginx.conf
docker exec lb nginx -s reload
echo "Switch tamamlandı! Aktif: $ACTIVE_ENV"Strateji 3: Canary Deployment
Nasıl Çalışır?
Canary deployment, yeni versiyonu önce küçük bir kullanıcı grubuna sunar. Sorun yoksa kademeli olarak genişletir. Adı, madenlerde zehirli gazı erken tespit etmek için kullanılan kanarya kuşlarından gelir.
Aşama 1: %5 trafik → v2 %95 trafik → v1
┌──┐ ┌──┬──┬──┬──┐
│v2│ │v1│v1│v1│v1│
└──┘ └──┴──┴──┴──┘
Metrikler OK? ✅ → Aşama 2'ye geç
Aşama 2: %20 trafik → v2 %80 trafik → v1
┌──┐ ┌──┬──┬──┐
│v2│ │v1│v1│v1│
└──┘ └──┴──┴──┘
Metrikler OK? ✅ → Aşama 3'e geç
Aşama 3: %100 trafik → v2
┌──┬──┬──┬──┐
│v2│v2│v2│v2│
└──┴──┴──┴──┘Her aşamada metrikler izlenir: error rate, response time, CPU/memory. Anomali tespit edilirse otomatik rollback yapılır.
Avantajlar ve Dezavantajlar
| Avantaj | Dezavantaj |
|---|---|
| Minimum blast radius | Karmaşık setup — monitoring, trafik yönetimi |
| Gerçek production verisiyle test | İki versiyon aynı anda çalışır |
| A/B testing ile kombine edilebilir | Tam rollout uzun sürer |
Netflix, Google, Facebook gibi şirketler canary deployment'ı standart olarak kullanır. Milyonlarca kullanıcıya bir bug göndermektense, %1'ine göndermek çok daha az acı verir.
Kubernetes'te Deployment Stratejileri
Kubernetes, deployment stratejilerini native olarak destekler.
Rolling Update (Varsayılan)
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Aynı anda en fazla 1 ekstra pod
maxUnavailable: 1 # Aynı anda en fazla 1 pod kapalı
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:2.0.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10maxSurge: 1 → Güncelleme sırasında en fazla 5 pod olabilir (4+1). maxUnavailable: 1 → En fazla 1 pod aynı anda kapalı. Her zaman minimum 3 pod hizmet verir.
Blue-Green (K8s)
Kubernetes'te blue-green, iki ayrı Deployment + Service selector değişikliği ile yapılır:
# blue-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-blue
spec:
replicas: 4
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: myapp
image: myapp:1.0.0
---
# service.yaml — Switch burada yapılır
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
version: blue # "green" yaparak switch et!
ports:
- port: 80
targetPort: 8080Switch: kubectl patch service myapp-service -p '{"spec":{"selector":{"version":"green"}}}'
Rollback: kubectl patch service myapp-service -p '{"spec":{"selector":{"version":"blue"}}}'
Canary (K8s)
Gerçek canary için Istio, Flagger veya Argo Rollouts kullanılır. Native K8s'te replica sayısı ile basit bir canary yapılabilir:
# v1: 3 replica, v2: 1 replica → yaklaşık %25 canary
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-stable
spec:
replicas: 3
selector:
matchLabels:
app: myapp
track: stable
template:
metadata:
labels:
app: myapp
track: stable
spec:
containers:
- name: myapp
image: myapp:1.0.0
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-canary
spec:
replicas: 1
selector:
matchLabels:
app: myapp
track: canary
template:
metadata:
labels:
app: myapp
track: canary
spec:
containers:
- name: myapp
image: myapp:2.0.0
---
# Service — track label olmadan, her ikisini de seçer
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080💡 İpucu: Istio VirtualService ile %5, %10, %20 gibi ince ayar yapılabilir. Replica sayısı ile yapılan yöntem kaba ama basit — başlangıç için yeterli.
Health Check ile Deployment Doğrulama
Her stratejinin ortak noktası: yeni versiyon sağlıklı mı? Spring Boot Actuator bunu hazır sağlar:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency># application.yml
management:
endpoints:
web:
exposure:
include: health, info, metrics
endpoint:
health:
show-details: when-authorized
probes:
enabled: true # K8s liveness/readiness probes
health:
db:
enabled: true
diskSpace:
enabled: trueCustom health indicator ile uygulamana özel kontroller ekleyebilirsin:
@Component
public class ExternalApiHealthIndicator implements HealthIndicator {
private final RestClient restClient;
public ExternalApiHealthIndicator(RestClient.Builder builder) {
this.restClient = builder.baseUrl("https://payment-service.internal").build();
}
@Override
public Health health() {
try {
var response = restClient.get()
.uri("/health")
.retrieve()
.toEntity(String.class);
if (response.getStatusCode().is2xxSuccessful()) {
return Health.up()
.withDetail("paymentService", "reachable")
.build();
}
return Health.down()
.withDetail("paymentService", "unhealthy")
.build();
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}Rollback Planı
Her deployment'ın bir rollback planı olmalı. Onsuz deploy etmek, paraşütsüz atlamak gibi.
Ne Zaman Rollback Yapılır?
Health check DOWN dönüyor
Error rate %5'i aşıyor (5xx hatalar)
Response time kabul edilemez seviyeye çıkıyor (p99 > 2s)
Kritik iş akışı bozuluyor (ödeme, login)
Strateji Bazında Rollback
| Strateji | Rollback Hızı | Nasıl? |
|---|---|---|
| Rolling Update | Yavaş (dakikalar) | kubectl rollout undo deployment/myapp |
| Blue-Green | Anında (saniyeler) | Load balancer'ı eski ortama çevir |
| Canary | Hızlı (saniyeler) | Canary instance'ı kapat |
Deploy sonrası otomatik doğrulama scripti:
#!/bin/bash
# deployment-validator.sh
MAX_RETRIES=10
HEALTH_URL="http://localhost:8080/actuator/health"
for i in $(seq 1 $MAX_RETRIES); do
HEALTH=$(curl -s $HEALTH_URL | jq -r '.status' 2>/dev/null)
if [ "$HEALTH" == "UP" ]; then
echo "✅ Deployment başarılı! (deneme $i/$MAX_RETRIES)"
exit 0
fi
echo "⏳ Bekleniyor... (deneme $i/$MAX_RETRIES)"
sleep 5
done
echo "❌ Deployment başarısız! Rollback başlatılıyor..."
# kubectl rollout undo deployment/myapp
exit 1⚠️ Dikkat: Veritabanı migration'ları rollback'i zorlaştırır. Kolon silme gibi yıkıcı migration'ları deploy'dan ayrı yap. Önce kodu deploy et (eski kolonu kullanmayı bırak), sonra ayrı adımda kolonu sil. Buna expand and contract pattern denir.
Feature Flags + Canary Kombinasyonu
Feature flag'ler, kodun içinde açma-kapama düğmeleri oluşturur. Canary ile birleştirildiğinde çift katmanlı güvenlik sağlar.
@Service
public class FeatureFlagService {
private final Map<String, FeatureFlag> flags = new ConcurrentHashMap<>();
@PostConstruct
void initFlags() {
flags.put("new-payment-engine", new FeatureFlag(true, 10));
flags.put("new-search-algo", new FeatureFlag(false, 0));
}
public boolean isEnabled(String flagName, String userId) {
FeatureFlag flag = flags.get(flagName);
if (flag == null || !flag.enabled()) {
return false;
}
// Deterministic hash — aynı kullanıcı her zaman aynı sonucu alır
int hash = Math.abs(userId.hashCode() % 100);
return hash < flag.rolloutPercentage();
}
record FeatureFlag(boolean enabled, int rolloutPercentage) {}
}Kullanımı:
@RestController
@RequestMapping("/api/payments")
public class PaymentController {
private final FeatureFlagService featureFlags;
private final OldPaymentService oldPaymentService;
private final NewPaymentService newPaymentService;
// constructor injection...
@PostMapping
public PaymentResult processPayment(@RequestBody PaymentRequest request,
@AuthenticationPrincipal User user) {
if (featureFlags.isEnabled("new-payment-engine", user.getId())) {
return newPaymentService.process(request); // %10 kullanıcı
}
return oldPaymentService.process(request); // %90 kullanıcı
}
}Canary + Feature Flag kombinasyonu:
Canary ile yeni versiyonu %5 kullanıcıya sun
Feature flag ile yeni motoru bu %5'in %10'una aç (gerçekte %0.5)
Metrikler OK → Flag'i %50'ye, canary'yi %20'ye çık
Adım adım full rollout
Stratejileri Karşılaştırma
| Kriter | Rolling Update | Blue-Green | Canary |
|---|---|---|---|
| Karmaşıklık | Düşük | Orta | Yüksek |
| Maliyet | Düşük | Yüksek (2x kaynak) | Orta |
| Rollback hızı | Yavaş | Anında | Hızlı |
| Risk | Orta | Düşük | Çok düşük |
| Zero-downtime | ✅ | ✅ | ✅ |
| A/B test | ❌ | ❌ | ✅ |
| En uygun | Küçük-orta projeler | Kritik sistemler | Büyük ölçekli sistemler |
Local Proje Kurulumu
Bu derste anlatılan örnekleri çalıştırmak için:
Gereksinimler: Java 17+, Docker ve Docker Compose, (opsiyonel) Minikube veya kind
Adımlar:
Spring Initializr'dan proje oluştur: Web, Actuator dependency'leri
Dockerfile ekle:
FROM eclipse-temurin:17-jre-alpine
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]mvn clean package -DskipTestsile JAR oluşturdocker build -t myapp:1.0.0 .ile image oluşturBu dersteki docker-compose.yml ve nginx.conf dosyalarını kopyala
docker compose up -dile başlatcurl http://localhost/infoile test et
Özet
Rolling Update, instance'ları tek tek günceller — basit, az kaynak, ama rollback yavaş ve geçiş sırasında iki versiyon aynı anda çalışır.
Blue-Green Deployment, iki paralel ortam tutar — anında rollback, zero-downtime, ama 2x kaynak maliyeti ve DB paylaşımı komplikasyonu var.
Canary Deployment, yeni versiyonu kademeli olarak yayar (%5 → %20 → %100) — minimum risk, gerçek production verisiyle test, ama karmaşık setup gerektirir.
Health check endpoint'leri (
/actuator/health) her stratejide kritik — deployment doğrulama, otomatik rollback kararı, Kubernetes probes.Feature flags, deployment'tan bağımsız feature kontrolü sağlar — canary ile birleştirildiğinde çift katmanlı güvenlik oluşturur.
Rollback planı olmadan deploy yapma — her strateji için geri alma prosedürün hazır olsun, DB değişikliklerini backward compatible tut.
AI Asistan
Sorularını yanıtlamaya hazır