Blue Green Deployment
Blue-Green Deployment: Zero-Downtime Releases
Blue-Green Deployment ermöglicht Releases ohne Ausfallzeit. Lernen Sie die Implementierung mit Load Balancern, Kubernetes und Cloud-Diensten.
Das Konzept
┌─────────────────────────────────────────────────────────────┐ │ BLUE-GREEN DEPLOYMENT │ ├─────────────────────────────────────────────────────────────┤ │ │ │ VORHER (Blue = Aktiv, Green = Idle) │ │ ┌───────────┐ ┌───────────────────────────────┐ │ │ │ LOAD │───────►│ BLUE ENVIRONMENT (v1.0) │ │ │ │ BALANCER │ │ - App Server 1 │ │ │ └───────────┘ │ - App Server 2 │ │ │ │ └───────────────────────────────┘ │ │ │ ┌───────────────────────────────┐ │ │ └─ ─ ─ ─ ─ ─ ─►│ GREEN ENVIRONMENT (v1.1) ❌ │ │ │ (idle) │ - App Server 1 │ │ │ │ - App Server 2 │ │ │ └───────────────────────────────┘ │ │ │ │ NACHHER (Switch: Green = Aktiv, Blue = Idle) │ │ ┌───────────┐ ┌───────────────────────────────┐ │ │ │ LOAD │─ ─ ─ ─►│ BLUE ENVIRONMENT (v1.0) ❌ │ │ │ │ BALANCER │ │ (bereit für Rollback) │ │ │ └───────────┘ └───────────────────────────────┘ │ │ │ ┌───────────────────────────────┐ │ │ └─────────────►│ GREEN ENVIRONMENT (v1.1) ✓ │ │ │ │ - App Server 1 │ │ │ │ - App Server 2 │ │ │ └───────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘
Vorteile
| Vorteil | Beschreibung |
|---|---|
| Zero Downtime | Umschaltung ist sofort, keine Ausfallzeit |
| Instant Rollback | Probleme? Einfach zurückschalten |
| Testing in Prod | Green kann vor Switch getestet werden |
| Einfach | Konzeptuell simpel zu verstehen |
Mit Nginx (Manuell)
# /etc/nginx/conf.d/app.conf
# Upstream-Definitionen
upstream blue {
server blue-app-1:8080;
server blue-app-2:8080;
}
upstream green {
server green-app-1:8080;
server green-app-2:8080;
}
# Aktive Environment via Variable
# In /etc/nginx/conf.d/active-environment.conf:
# set $active_env blue;
server {
listen 80;
server_name app.example.com;
include /etc/nginx/conf.d/active-environment.conf;
location / {
proxy_pass http://$active_env;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
#!/bin/bash
# deploy.sh - Blue-Green Switch Script
ACTIVE_FILE="/etc/nginx/conf.d/active-environment.conf"
CURRENT=$(grep -oP "set \\\$active_env \K\w+" $ACTIVE_FILE)
if [ "$CURRENT" == "blue" ]; then
NEW="green"
else
NEW="blue"
fi
# Switch durchführen
echo "Switching from $CURRENT to $NEW..."
echo "set \$active_env $NEW;" > $ACTIVE_FILE
# Nginx neu laden (kein Restart nötig!)
nginx -s reload
echo "Deployment complete. Now active: $NEW"
echo "Rollback: bash $0"
Mit Docker Compose
# docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./active-env.conf:/etc/nginx/active-env.conf
depends_on:
- blue
- green
blue:
image: myapp:v1.0
environment:
- ENV=blue
deploy:
replicas: 2
green:
image: myapp:v1.1
environment:
- ENV=green
deploy:
replicas: 2
#!/bin/bash
# deploy.sh für Docker Compose
# 1. Neues Image für inactive Environment deployen
docker-compose pull green
docker-compose up -d green
# 2. Health Check
echo "Waiting for green to be healthy..."
sleep 30
curl -f http://localhost/health?env=green || {
echo "Green is not healthy, aborting"
exit 1
}
# 3. Switch Traffic
echo "set \$active_env green;" > active-env.conf
docker-compose exec nginx nginx -s reload
# 4. Verifizieren
echo "Verifying deployment..."
sleep 5
curl -f http://localhost/health || {
echo "Deployment failed, rolling back"
echo "set \$active_env blue;" > active-env.conf
docker-compose exec nginx nginx -s reload
exit 1
}
echo "Deployment successful!"
Mit Kubernetes
# blue-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-blue
labels:
app: myapp
version: blue
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: myapp
image: myapp:v1.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
# green-deployment.yaml (identisch, nur version: green und image: v1.1)
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-green
labels:
app: myapp
version: green
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: green
template:
metadata:
labels:
app: myapp
version: green
spec:
containers:
- name: myapp
image: myapp:v1.1
ports:
- containerPort: 8080
# service.yaml - Traffic Routing via Label Selector
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
version: blue # ← Ändern zu "green" für Switch
ports:
- port: 80
targetPort: 8080
#!/bin/bash
# k8s-deploy.sh
# 1. Green Deployment erstellen/aktualisieren
kubectl apply -f green-deployment.yaml
# 2. Warten bis Ready
kubectl rollout status deployment/myapp-green
# 3. Optional: Green testen (via temporären Service)
kubectl port-forward deployment/myapp-green 8081:8080 &
curl http://localhost:8081/health
kill %1
# 4. Service auf Green switchen
kubectl patch service myapp -p '{"spec":{"selector":{"version":"green"}}}'
# 5. Optional: Blue auf 0 Replicas skalieren
kubectl scale deployment myapp-blue --replicas=0
echo "Rollback: kubectl patch service myapp -p '{\"spec\":{\"selector\":{\"version\":\"blue\"}}}'"
Mit AWS (ALB + Target Groups)
# Terraform Beispiel
resource "aws_lb_target_group" "blue" {
name = "myapp-blue"
port = 80
protocol = "HTTP"
vpc_id = var.vpc_id
health_check {
path = "/health"
}
}
resource "aws_lb_target_group" "green" {
name = "myapp-green"
port = 80
protocol = "HTTP"
vpc_id = var.vpc_id
health_check {
path = "/health"
}
}
resource "aws_lb_listener" "app" {
load_balancer_arn = aws_lb.main.arn
port = 80
default_action {
type = "forward"
target_group_arn = var.active_target_group # blue oder green
}
}
# Switch via:
# aws elbv2 modify-listener --listener-arn xxx \
# --default-actions Type=forward,TargetGroupArn=green-arn
Database Migrations
// Herausforderung: Beide Versionen müssen mit der DB funktionieren // Strategie: Expand/Contract Pattern // PHASE 1: Expand (Backward Compatible) // - Neue Spalte hinzufügen, aber optional ALTER TABLE users ADD COLUMN display_name VARCHAR(255) NULL; // - Code V1.1 schreibt beide Spalten $user->name = $name; $user->display_name = $name; // - Code V1.0 funktioniert weiter (ignoriert neue Spalte) // PHASE 2: Migrate // - Daten migrieren UPDATE users SET display_name = name WHERE display_name IS NULL; // PHASE 3: Contract (nach erfolgreicher Umstellung) // - Alte Spalte entfernen (wenn V1.0 nicht mehr läuft) ALTER TABLE users DROP COLUMN name;
Rollback-Prozess
#!/bin/bash
# rollback.sh
echo "Starting rollback..."
# 1. Traffic zurück zu Blue
kubectl patch service myapp -p '{"spec":{"selector":{"version":"blue"}}}'
# 2. Health Check
sleep 5
curl -f http://app.example.com/health || {
echo "CRITICAL: Rollback failed!"
# Alert auslösen
exit 1
}
# 3. Green-Deployment bereinigen (optional)
kubectl scale deployment myapp-green --replicas=0
echo "Rollback complete. Blue is now active."
💡 Best Practices:
1. Smoke Tests auf Green vor dem Switch
2. Database Migrations müssen backward-compatible sein
3. Blue mindestens 1 Stunde nach Switch behalten
4. Monitoring-Alerts für schnelle Rollback-Entscheidung
5. Automatisierte Rollback-Trigger bei Error-Rate > X%
2. Database Migrations müssen backward-compatible sein
3. Blue mindestens 1 Stunde nach Switch behalten
4. Monitoring-Alerts für schnelle Rollback-Entscheidung
5. Automatisierte Rollback-Trigger bei Error-Rate > X%