Sofortantwort
Shadow Deployment einfach erklärt
Neue Version läuft parallel mit echtem Traffic, aber ohne Nutzer-Impact.
- Kurz gesagt
- Neue Version erhält echten Produktions-Traffic
- Typischer Einsatz
- ML-Modell-Validierung, Performance-Tests, Regressionstests
- Wichtig zu wissen
- Ermöglicht Vergleich: Neue vs. alte Version unter realen Bedingungen
Shadow Deployment im Überblick
Shadow Deployment lässt eine neue Version parallel zur Produktion laufen. Sie erhält echten Traffic, aber ihre Antworten werden verworfen – Nutzer merken nichts davon.
Der Ablauf:
Nutzer-Request
│
├──────────────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Produktion │ │ Shadow │
│ (v1.0) │ │ (v2.0) │
└──────┬───────┘ └──────┬───────┘
│ │
▼ ▼
Response Logging
an Nutzer (verworfen)
Vergleich Deployment-Strategien:
| Strategie | Nutzer sieht neue Version? | Risiko | Use Case |
|---|---|---|---|
| Shadow | Nein | Sehr niedrig | Validierung |
| Canary | Ja (1-10%) | Niedrig | Gradual Rollout |
| Blue-Green | Ja (100%) | Mittel | Schneller Switch |
| A/B Test | Ja (50%) | Mittel | Feature-Vergleich |
Technisch betrachtet
Traffic Mirroring mit Istio
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: my-service
spec:
hosts:
- my-service
http:
- route:
- destination:
host: my-service
subset: v1
weight: 100
mirror:
host: my-service
subset: v2
mirrorPercentage:
value: 100.0 # 100% des Traffics spiegeln
Nginx Traffic Mirroring
location /api {
# Primärer Backend
proxy_pass http://production;
# Mirror zu Shadow
mirror /mirror;
mirror_request_body on;
}
location = /mirror {
internal;
proxy_pass http://shadow$request_uri;
# Antwort verwerfen
proxy_ignore_client_abort on;
}
ML-Modell Shadow Testing
class ShadowModelService:
def __init__(self, production_model, shadow_model):
self.production = production_model
self.shadow = shadow_model
self.logger = MetricsLogger()
async def predict(self, input_data):
# Production: Synchron, Antwort an Nutzer
prod_result = await self.production.predict(input_data)
# Shadow: Asynchron, nur Logging
asyncio.create_task(
self._shadow_predict(input_data, prod_result)
)
return prod_result
async def _shadow_predict(self, input_data, prod_result):
try:
shadow_result = await self.shadow.predict(input_data)
# Vergleich loggen
self.logger.log({
"input": input_data,
"production": prod_result,
"shadow": shadow_result,
"match": prod_result == shadow_result,
"shadow_latency": shadow_result.latency
})
except Exception as e:
self.logger.log_error(e)
Metriken vergleichen
def analyze_shadow_results(logs):
metrics = {
"total_requests": len(logs),
"match_rate": sum(l["match"] for l in logs) / len(logs),
"shadow_avg_latency": mean(l["shadow_latency"] for l in logs),
"prod_avg_latency": mean(l["prod_latency"] for l in logs),
"shadow_errors": sum(1 for l in logs if l.get("shadow_error")),
}
# Alerts
if metrics["match_rate"] < 0.95:
alert("Shadow model divergiert von Production!")
if metrics["shadow_avg_latency"] > metrics["prod_avg_latency"] * 1.5:
alert("Shadow model ist 50% langsamer!")
return metrics
Wann Shadow Deployment nutzen?
| Szenario | Empfehlung |
|---|---|
| Neues ML-Modell | ✅ Shadow zuerst |
| Kritische API-Änderung | ✅ Shadow zuerst |
| Minor Bug Fix | ❌ Canary reicht |
| UI-Änderung | ❌ Nicht anwendbar |
| Performance-Optimierung | ✅ Shadow für Latenz-Vergleich |