Traffic Splitting Anleitung¶
Umfassende Anleitung für A/B Testing, Canary Deployments und Traffic Splitting in GAL (Gateway Abstraction Layer)
Inhaltsverzeichnis¶
- Übersicht
- Schnellstart
- Konfigurationsoptionen
- Provider-Implementierung
- Häufige Anwendungsfälle
- Best Practices
- Traffic Splitting Testen
- Troubleshooting
Übersicht¶
Traffic Splitting ist ein essentielles Feature für moderne Deployment-Strategien wie Canary Deployments, A/B Testing und Blue/Green Deployments. GAL bietet eine einheitliche Traffic Splitting-Konfiguration für alle unterstützten Gateway-Provider.
Was ist Traffic Splitting?¶
Traffic Splitting verteilt eingehende Requests auf verschiedene Backend-Versionen basierend auf Gewichtungen, Headers oder Cookies. Dies ermöglicht kontrollierte Rollouts neuer Features ohne Risiko für alle Nutzer.
Traffic Splitting Strategien:
Weight-based: 90% → Stable, 10% → Canary
Header-based: X-Version: beta → Beta Backend
Cookie-based: canary_user=true → Canary Backend
Warum ist Traffic Splitting wichtig?¶
- ✅ Risikofreie Deployments: Neue Versionen schrittweise ausrollen
- ✅ A/B Testing: Feature-Varianten mit echten Nutzern testen
- ✅ Canary Deployments: Kleine Nutzergruppen als "Kanarienvögel"
- ✅ Blue/Green Deployments: Sofortiges Switching zwischen Versionen
- ✅ Feature Flags: Header-basierte Feature-Freischaltung
- ✅ User Segmentation: Verschiedene Nutzergruppen testen unterschiedliche Features
Provider-Unterstützung¶
| Feature | Envoy | Kong | APISIX | Traefik | Nginx | HAProxy | Azure APIM | AWS API GW | GCP API GW |
|---|---|---|---|---|---|---|---|---|---|
| Traffic Splitting | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Weight-based | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Header-based | ⚠️ | ⚠️ | ✅ | ⚠️ | ✅ | ❌ | ⚠️ | ⚠️ | ❌ |
| Cookie-based | ⚠️ | ⚠️ | ✅ | ⚠️ | ✅ | ❌ | ⚠️ | ⚠️ | ❌ |
| Dynamic Weights | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ |
Coverage: 88.9% (8 von 9 Providern haben Unterstützung)
Open-Source Providers:
- Envoy: Weighted Clusters ✅
- Kong: Weighted Upstream Targets (0-1000 scale) ✅
- APISIX: Native traffic-split Plugin ✅
- Traefik: WeightedRoundRobin Services ✅
- Nginx: set_random + conditional routing ✅
- HAProxy: Weighted Servers (0-256 scale) ✅
Cloud Providers: - Azure APIM: Load-Balanced Backend Pool mit Gewichten ✅ - AWS API Gateway: VTL (Velocity Template Language) mit stageVariables ✅ - GCP API Gateway: ❌ Keine native Unterstützung (externe Cloud Load Balancer erforderlich)
Hinweis: Azure APIM und AWS API Gateway haben vollständige Weight-based Unterstützung. GCP API Gateway benötigt externe Cloud Load Balancer für Traffic Splitting.
Schnellstart¶
1. Einfaches Canary Deployment (90/10)¶
Verteile 90% Traffic auf stabile Version, 10% auf Canary:
version: "1.0"
provider: envoy
services:
- name: api
type: rest
protocol: http
upstream:
host: placeholder # Wird von traffic_split überschrieben
port: 8080
routes:
- path_prefix: /api/v1
methods: [GET, POST, PUT, DELETE]
traffic_split:
enabled: true
targets:
- name: stable
weight: 90
upstream:
host: api-v1-stable
port: 8080
description: "Stable production version"
- name: canary
weight: 10
upstream:
host: api-v1-canary
port: 8080
description: "Canary deployment for testing"
Deployment:
2. A/B Testing (50/50 Split)¶
Teste zwei Feature-Varianten mit gleichverteiltem Traffic:
services:
- name: ab_testing_api
type: rest
protocol: http
upstream:
host: placeholder
port: 8080
routes:
- path_prefix: /api/v2
methods: [GET, POST]
traffic_split:
enabled: true
targets:
- name: version_a
weight: 50
upstream:
host: api-v2-a
port: 8080
description: "Version A - existing algorithm"
- name: version_b
weight: 50
upstream:
host: api-v2-b
port: 8080
description: "Version B - new algorithm"
3. Header-based Beta Testing¶
Route Nutzer mit X-Version: beta Header zum Beta-Backend:
services:
- name: beta_testing_api
type: rest
protocol: http
upstream:
host: placeholder
port: 8080
routes:
- path_prefix: /api/beta
methods: [GET, POST, PUT, DELETE]
traffic_split:
enabled: true
targets:
- name: production
weight: 100
upstream:
host: api-prod
port: 8080
- name: beta
weight: 0 # Nur via Header-Routing
upstream:
host: api-beta
port: 8080
routing_rules:
header_rules:
- header_name: "X-Version"
header_value: "beta"
target_name: "beta"
fallback_target: "production"
Nutzung:
# Normale Nutzer → Production
curl http://api.example.com/api/beta
# Beta-Nutzer → Beta Backend
curl -H "X-Version: beta" http://api.example.com/api/beta
4. Cookie-based User Segmentation¶
Route Nutzer mit spezifischem Cookie zum Canary-Backend:
services:
- name: user_segment_api
type: rest
protocol: http
upstream:
host: placeholder
port: 8080
routes:
- path_prefix: /api/users
traffic_split:
enabled: true
targets:
- name: stable
weight: 100
upstream:
host: api-stable
port: 8080
- name: canary_users
weight: 0 # Nur via Cookie
upstream:
host: api-canary
port: 8080
routing_rules:
cookie_rules:
- cookie_name: "canary_user"
cookie_value: "true"
target_name: "canary_users"
fallback_target: "stable"
Konfigurationsoptionen¶
TrafficSplitConfig¶
Hauptkonfiguration für Traffic Splitting auf Route-Ebene.
traffic_split:
enabled: true|false # Traffic Splitting aktivieren
targets: [] # Liste von SplitTarget (min. 1)
routing_rules: # Optional: Header/Cookie Routing
header_rules: [] # Header-basierte Regeln
cookie_rules: [] # Cookie-basierte Regeln
fallback_target: "name" # Fallback bei nicht-matchenden Rules
SplitTarget¶
Einzelnes Backend-Ziel mit Gewichtung.
targets:
- name: "stable" # Eindeutiger Name (required)
weight: 90 # Gewichtung 0-100 (required)
upstream: # Backend-Konfiguration (required)
host: "api-stable"
port: 8080
description: "Stable version" # Optional
Validierung: - ✅ Gewichte müssen zwischen 0-100 sein - ✅ Summe aller Gewichte muss 100 sein (außer bei Routing Rules) - ✅ Target-Namen müssen eindeutig sein - ✅ Mindestens 1 Target erforderlich
Header-based Routing Rules¶
Route basierend auf HTTP-Headern.
routing_rules:
header_rules:
- header_name: "X-Version" # HTTP Header (case-insensitive)
header_value: "beta" # Erwarteter Wert
target_name: "beta" # Ziel-Target
- header_name: "X-Environment"
header_value: "staging"
target_name: "staging"
Priorität: Header Rules > Cookie Rules > Weight-based
Cookie-based Routing Rules¶
Route basierend auf HTTP-Cookies.
routing_rules:
cookie_rules:
- cookie_name: "canary_user" # Cookie-Name
cookie_value: "true" # Erwarteter Wert
target_name: "canary" # Ziel-Target
- cookie_name: "user_tier"
cookie_value: "premium"
target_name: "premium_backend"
Provider-Implementierung¶
Envoy - Weighted Clusters¶
Envoy verwendet weighted_clusters für Traffic Splitting.
Generierte Konfiguration:
routes:
- match:
prefix: '/api/v1'
route:
weighted_clusters:
clusters:
- name: canary_deployment_api_stable_cluster
weight: 90
- name: canary_deployment_api_canary_cluster
weight: 10
total_weight: 100
clusters:
- name: canary_deployment_api_stable_cluster
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: api-v1-stable
port_value: 8080
Eigenschaften: - ✅ Exakte Gewichtungen (90% = weight 90) - ✅ Separate Cluster pro Target - ✅ Deterministisches Routing - ⚠️ Header/Cookie-Routing benötigt Lua-Filter
Deployment:
Nginx - set_random + Conditional Routing¶
Nginx verwendet set_random für probabilistische Verteilung.
Generierte Konfiguration:
location /api/v1 {
# Weight-based traffic splitting
set $split_backend '';
# stable: 90% (cumulative: 90%)
set_random $rand_split 0 100;
if ($rand_split < 90) {
set $split_backend 'stable';
proxy_pass http://api-v1-stable:8080;
}
if ($split_backend = '') {
set $split_backend 'canary';
proxy_pass http://api-v1-canary:8080;
}
}
# Header-based routing
location /api/beta {
set $backend_target '';
if ($http_x_version = 'beta') {
set $backend_target 'beta';
}
if ($backend_target = 'beta') {
proxy_pass http://api-beta:8080;
}
}
Eigenschaften:
- ✅ set_random für probabilistische Verteilung
- ✅ Header-Routing via $http_* Variablen
- ✅ Cookie-Routing via $cookie_* Variablen
- ⚠️ Nicht session-persistent (jede Request neu)
- ⚠️ Mehrere if-Statements (nicht optimal, aber funktional)
Kong - Weighted Upstream Targets¶
Kong verwendet Upstreams mit gewichteten Targets.
Generierte Konfiguration:
upstreams:
- name: canary_deployment_api_route0_upstream
algorithm: round-robin
targets:
- target: api-v1-stable:8080
weight: 900 # Kong: 0-1000 (90% = 900)
- target: api-v1-canary:8080
weight: 100 # Kong: 0-1000 (10% = 100)
services:
- name: canary_deployment_api
protocol: http
host: canary_deployment_api_route0_upstream
routes:
- name: canary_deployment_api_route
paths:
- /api/v1
Eigenschaften: - ✅ Gewichtungsskala 0-1000 (GAL 0-100 × 10) - ✅ Round-robin respektiert Gewichte - ✅ Dynamische Rekonfiguration via Admin API - ⚠️ 1 Service = 1 Upstream (Limitation) - ⚠️ Header/Cookie-Routing benötigt request-transformer Plugin
APISIX - traffic-split Plugin¶
APISIX hat natives traffic-split Plugin mit Rules.
Generierte Konfiguration:
{
"routes": [{
"plugins": {
"traffic-split": {
"rules": [
{
"match": [{
"vars": [["http_x_version", "==", "beta"]]
}],
"weighted_upstreams": [{
"upstream": {
"name": "beta_testing_api_beta_upstream",
"type": "roundrobin",
"nodes": {"api-beta:8080": 1}
},
"weight": 1
}]
},
{
"weighted_upstreams": [
{
"upstream": {"nodes": {"api-prod:8080": 1}},
"weight": 100
},
{
"upstream": {"nodes": {"api-beta:8080": 1}},
"weight": 0
}
]
}
]
}
}
}]
}
Eigenschaften:
- ✅ Native Plugin-Unterstützung
- ✅ Rules mit match Conditions
- ✅ Header-Matching (http_* vars)
- ✅ Cookie-Matching (cookie_* vars)
- ✅ Keine Lua benötigt
- ✅ Dynamische Rekonfiguration via Admin API
Traefik - WeightedRoundRobin Services¶
Traefik verwendet weighted Services.
Generierte Konfiguration:
http:
routers:
canary_deployment_api_router_0:
rule: 'PathPrefix(`/api/v1`)'
service: canary_deployment_api_route0_service
services:
canary_deployment_api_route0_service:
weighted:
services:
- name: canary_deployment_api_stable_service
weight: 90
- name: canary_deployment_api_canary_service
weight: 10
canary_deployment_api_stable_service:
loadBalancer:
servers:
- url: 'http://api-v1-stable:8080'
canary_deployment_api_canary_service:
loadBalancer:
servers:
- url: 'http://api-v1-canary:8080'
Eigenschaften: - ✅ Native weighted Services - ✅ Saubere YAML-Struktur - ✅ Dynamische File Provider Rekonfiguration - ✅ Kompatibel mit Kubernetes IngressRoute - ⚠️ Header/Cookie-Routing benötigt separate Router mit Priority
HAProxy - Weighted Servers¶
HAProxy verwendet Server-Gewichte (0-256 scale).
Generierte Konfiguration:
backend backend_canary_deployment_api
balance roundrobin
option httpclose
option forwardfor
# Backend servers mit Gewichten
server server_stable api-v1-stable:8080 check inter 10s fall 3 rise 2 weight 230
server server_canary api-v1-canary:8080 check inter 10s fall 3 rise 2 weight 25
Eigenschaften: - ✅ Weighted Servers (0-256 scale) - ✅ Robustes Load Balancing - ✅ Health Checks integriert - ✅ Production-grade Performance - ⚠️ Weight-Umrechnung: GAL 0-100 → HAProxy 0-256 (×2.56) - ❌ Keine Header/Cookie-Routing Unterstützung
Deployment:
gal generate -f gateway.yaml -o haproxy.cfg --provider haproxy
haproxy -f haproxy.cfg -c # Config validieren
haproxy -f haproxy.cfg # Starten
Azure API Management - Load-Balanced Backend Pool¶
Azure APIM verwendet Load-Balanced Backend Pools mit Gewichten.
Generierte ARM Template Konfiguration:
{
"type": "Microsoft.ApiManagement/service/backends",
"properties": {
"description": "Load-balanced backend pool for canary_deployment_api",
"type": "pool",
"pool": {
"services": [
{
"id": "/backends/canary_deployment_api-stable-backend",
"priority": 1,
"weight": 90
},
{
"id": "/backends/canary_deployment_api-canary-backend",
"priority": 1,
"weight": 10
}
]
}
}
}
Policy XML:
<policies>
<inbound>
<base />
<set-backend-service backend-id="canary_deployment_api-backend-pool" />
</inbound>
</policies>
Eigenschaften: - ✅ Native Backend Pool Support - ✅ Weighted Targets (gleiche Scale wie GAL: 0-100) - ✅ Azure Portal Verwaltung - ✅ Multi-Region Deployment - ⚠️ Header/Cookie-Routing benötigt Policy XML - ⚠️ ARM Template Deployment erforderlich
Deployment:
gal generate -f gateway.yaml -o azure-apim.json --provider azure_apim
az deployment group create \
--resource-group my-rg \
--template-file azure-apim.json
AWS API Gateway - VTL (Velocity Template Language)¶
AWS API Gateway verwendet VTL Request Templates mit stageVariables.
Generierte OpenAPI 3.0 Konfiguration:
{
"paths": {
"/api/v1": {
"get": {
"x-amazon-apigateway-integration": {
"type": "http_proxy",
"httpMethod": "GET",
"uri": "${stageVariables.backend_url}/api/v1",
"requestTemplates": {
"application/json": "## AWS API Gateway Traffic Splitting via VTL\\n#set($random = $context.requestTimeEpoch % 100)\\n#if($random < 90)\\n #set($backend_url = $stageVariables.canary_deployment_api_stable_url)\\n#elseif($random < 100)\\n #set($backend_url = $stageVariables.canary_deployment_api_canary_url)\\n#end\\n#set($context.requestOverride.path.backend_url = $backend_url)"
}
}
}
}
}
}
Stage Variables:
{
"canary_deployment_api_stable_url": "https://api-v1-stable.example.com:8080",
"canary_deployment_api_canary_url": "https://api-v1-canary.example.com:8080"
}
Eigenschaften: - ✅ VTL Template Support - ✅ Weighted Routing via Random Selection - ✅ Stage Variables für Flexibilität - ✅ Deployment History & Rollback - ⚠️ Komplexe VTL Syntax - ⚠️ Stage Variables müssen manuell konfiguriert werden - ⚠️ Header/Cookie-Routing benötigt zusätzliche VTL Logic
Deployment:
gal generate -f gateway.yaml -o aws-apigateway.json --provider aws_apigateway
# Import OpenAPI
aws apigateway import-rest-api \
--body file://aws-apigateway.json \
--fail-on-warnings
# Deploy Stage mit Variables
aws apigateway create-deployment \
--rest-api-id <api-id> \
--stage-name prod \
--variables canary_deployment_api_stable_url=https://api-stable:8080,canary_deployment_api_canary_url=https://api-canary:8080
GCP API Gateway - ❌ Nicht Unterstützt¶
GCP API Gateway unterstützt KEIN natives Traffic Splitting.
Grund: - GCP API Gateway unterstützt nur ein einzelnes Backend pro Route - Keine Weighted Targets oder Load Balancing Features - OpenAPI 2.0 (Swagger) hat keine Traffic Splitting Extensions
Workaround: Verwende Google Cloud Load Balancer VOR GCP API Gateway:
Client → Cloud Load Balancer (Traffic Split 90/10)
├─ 90% → Backend 1 (Stable)
└─ 10% → Backend 2 (Canary)
Cloud Load Balancer Konfiguration:
# Weighted Backend Services
backends:
- name: stable-backend
backends:
- group: instance-group-stable
balancingMode: UTILIZATION
capacityScaler: 0.9 # 90%
- name: canary-backend
backends:
- group: instance-group-canary
balancingMode: UTILIZATION
capacityScaler: 0.1 # 10%
Alternative: Cloud Run Traffic Splitting
Wenn du Cloud Run als Backend verwendest:
# Cloud Run native Traffic Splitting
gcloud run services update-traffic my-service \
--to-revisions=stable=90,canary=10
Eigenschaften: - ❌ GCP API Gateway: Kein Traffic Splitting - ✅ Cloud Load Balancer: Weighted Backends - ✅ Cloud Run: Native Traffic Splitting - ⚠️ Workaround benötigt zusätzliche GCP-Komponenten
Empfehlung: Für Traffic Splitting auf GCP, verwende: 1. Cloud Run (wenn serverless Backend) 2. Cloud Load Balancer + Compute Engine/GKE (wenn VM/Container Backend) 3. Alternative: Istio auf GKE (volle Traffic Management Kontrolle)
Häufige Anwendungsfälle¶
1. Canary Deployment (Schrittweiser Rollout)¶
Strategie: Neue Version schrittweise ausrollen (5% → 25% → 50% → 100%)
Phase 1: 5% Canary
traffic_split:
enabled: true
targets:
- name: current
weight: 95
upstream: {host: api-v1, port: 8080}
- name: new
weight: 5
upstream: {host: api-v2, port: 8080}
Phase 2: 25% Canary (wenn Phase 1 erfolgreich)
Phase 3: 50/50 Split
Phase 4: 100% Neues Backend
Rollback: Gewichte wieder auf current: 100, new: 0 setzen
2. A/B Testing¶
Strategie: Zwei Versionen parallel testen und Metriken vergleichen
traffic_split:
enabled: true
targets:
- name: version_a
weight: 50
upstream: {host: api-v2-a, port: 8080}
description: "Existing recommendation algorithm"
- name: version_b
weight: 50
upstream: {host: api-v2-b, port: 8080}
description: "New ML-based recommendation"
Metriken sammeln: - Conversion Rate - Click-Through Rate (CTR) - Response Time - Error Rate
Auswertung:
# Prometheus Queries
rate(http_requests_total{version="a"}[5m])
rate(http_requests_total{version="b"}[5m])
3. Blue/Green Deployment¶
Strategie: Sofortiges Switching zwischen zwei Versionen
Blue aktiv, Green standby:
traffic_split:
enabled: true
targets:
- name: blue
weight: 100
upstream: {host: api-blue, port: 8080}
- name: green
weight: 0
upstream: {host: api-green, port: 8080}
Instant Switch zu Green:
Rollback: Instant Switch zurück zu Blue
4. Feature Flags via Headers¶
Strategie: Features via HTTP-Header freischalten
traffic_split:
enabled: true
targets:
- name: stable
weight: 100
upstream: {host: api-stable, port: 8080}
- name: experimental
weight: 0
upstream: {host: api-experimental, port: 8080}
routing_rules:
header_rules:
- header_name: "X-Feature-Flag"
header_value: "new-checkout"
target_name: "experimental"
fallback_target: "stable"
Nutzung:
# Standard-Nutzer → Stable
curl http://api.example.com/checkout
# Beta-Tester → Experimental
curl -H "X-Feature-Flag: new-checkout" http://api.example.com/checkout
5. User Segmentation (Premium/Free)¶
Strategie: Verschiedene Nutzergruppen auf verschiedene Backends routen
traffic_split:
enabled: true
targets:
- name: free_tier
weight: 80
upstream: {host: api-free, port: 8080}
- name: premium_tier
weight: 20
upstream: {host: api-premium, port: 8080}
routing_rules:
cookie_rules:
- cookie_name: "user_tier"
cookie_value: "premium"
target_name: "premium_tier"
header_rules:
- header_name: "X-Subscription"
header_value: "premium"
target_name: "premium_tier"
fallback_target: "free_tier"
6. Multi-Stage Rollout (3 Versionen gleichzeitig)¶
Strategie: Stable, Beta und Canary gleichzeitig laufen lassen
traffic_split:
enabled: true
targets:
- name: stable
weight: 70
upstream: {host: api-stable, port: 8080}
- name: beta
weight: 20
upstream: {host: api-beta, port: 8080}
- name: canary
weight: 10
upstream: {host: api-canary, port: 8080}
Best Practices¶
1. Gewichte schrittweise erhöhen¶
❌ Schlecht: Sofort von 0% auf 100%
# Riskant: Neue Version erhält sofort 100% Traffic
targets:
- {name: old, weight: 0}
- {name: new, weight: 100}
✅ Gut: Schrittweise erhöhen mit Monitoring
# Phase 1: 5% Canary
targets:
- {name: old, weight: 95}
- {name: new, weight: 5}
# Phase 2 (nach 24h Monitoring): 25%
# Phase 3 (nach weiteren 24h): 50%
# Phase 4 (nach weiteren 24h): 100%
2. Immer Monitoring einrichten¶
Überwache folgende Metriken pro Traffic Split Target:
# Prometheus Metriken
- http_requests_total{target="stable"}
- http_requests_total{target="canary"}
- http_request_duration_seconds{target="stable"}
- http_request_duration_seconds{target="canary"}
- http_errors_total{target="stable"}
- http_errors_total{target="canary"}
3. Fallback-Target definieren¶
Immer einen Fallback für Header/Cookie-Routing:
traffic_split:
enabled: true
targets: [...]
routing_rules: [...]
fallback_target: "stable" # ✅ Wichtig!
4. Session Stickiness beachten¶
⚠️ Problem: Nutzer wechseln zwischen Backends
# Schlecht: Nutzer kann zwischen stable/canary springen
# → Schlechte User Experience
traffic_split:
enabled: true
targets:
- {name: stable, weight: 50}
- {name: canary, weight: 50}
✅ Lösung: Session Cookies verwenden
# Nutzer bleiben auf demselben Backend
routing_rules:
cookie_rules:
- cookie_name: "backend_version"
cookie_value: "canary"
target_name: "canary"
5. Rollback-Plan bereithalten¶
Immer einen schnellen Rollback-Mechanismus:
# Rollback via CLI
gal generate -f gateway-rollback.yaml -o envoy.yaml
envoy -c envoy.yaml --hot-restart
# Oder: Gewichte dynamisch anpassen (APISIX/Kong)
curl -X PATCH http://apisix-admin:9080/apisix/admin/routes/1 \
-d '{"plugins": {"traffic-split": {"rules": [...]}}}'
6. Dokumentiere Target-Versionen¶
Klare Beschreibungen für jedes Target:
targets:
- name: v1_2_5
weight: 90
upstream: {host: api-v1-2-5, port: 8080}
description: "Stable version 1.2.5 (2024-01-15)"
- name: v1_3_0_rc1
weight: 10
upstream: {host: api-v1-3-0-rc1, port: 8080}
description: "Release Candidate 1.3.0-rc1 (2024-01-20)"
7. Rate Limiting pro Target¶
Verhindere, dass Canary überlastet wird:
routes:
- path_prefix: /api
traffic_split: [...]
rate_limit:
enabled: true
requests_per_second: 1000 # Limit für gesamten Endpunkt
Traffic Splitting Testen¶
1. Unit Tests (Lokale Entwicklung)¶
Teste Config-Validierung:
from gal.config import TrafficSplitConfig, SplitTarget, UpstreamTarget
def test_weight_sum_validation():
target1 = SplitTarget(name="stable", weight=90, ...)
target2 = SplitTarget(name="canary", weight=20, ...) # Summe = 110
with pytest.raises(ValueError, match="must sum to 100"):
TrafficSplitConfig(enabled=True, targets=[target1, target2])
2. Integration Tests (Provider Output)¶
Teste generierte Konfiguration:
def test_envoy_weighted_clusters():
config = Config.from_yaml("traffic-split-example.yaml")
provider = EnvoyProvider()
output = provider.generate(config)
assert "weighted_clusters:" in output
assert "weight: 90" in output
assert "weight: 10" in output
3. Manuelle Tests (Browser/cURL)¶
Weight-based Testing:
# 100 Requests senden, Verteilung prüfen
for i in {1..100}; do
curl -s http://localhost:8080/api | grep -o "Backend: [a-z]*"
done | sort | uniq -c
# Erwartete Ausgabe (90/10 Split):
# 90 Backend: stable
# 10 Backend: canary
Header-based Testing:
# Normal → Production
curl -v http://localhost:8080/api | grep "Backend:"
# Output: Backend: production
# Mit Beta-Header → Beta
curl -v -H "X-Version: beta" http://localhost:8080/api | grep "Backend:"
# Output: Backend: beta
Cookie-based Testing:
# Mit Canary-Cookie → Canary
curl -v --cookie "canary_user=true" http://localhost:8080/api | grep "Backend:"
# Output: Backend: canary
4. Load Testing (k6, Apache Bench)¶
k6 Script für Traffic Split Testing:
import http from 'k6/http';
import { check } from 'k6';
export let options = {
vus: 100,
duration: '30s',
};
export default function() {
let res = http.get('http://localhost:8080/api');
check(res, {
'is status 200': (r) => r.status === 200,
'backend is stable or canary': (r) =>
r.body.includes('Backend: stable') ||
r.body.includes('Backend: canary'),
});
}
Ausführen:
k6 run traffic-split-test.js
# Ergebnis analysieren:
# http_req_duration..........: avg=10ms min=5ms max=50ms
# Backend stable: ~90% der Requests
# Backend canary: ~10% der Requests
Troubleshooting¶
Problem 1: Gewichte summieren sich nicht zu 100¶
Fehlermeldung:
Lösung:
# ❌ Falsch: 90 + 20 = 110
targets:
- {name: stable, weight: 90}
- {name: canary, weight: 20}
# ✅ Korrekt: 90 + 10 = 100
targets:
- {name: stable, weight: 90}
- {name: canary, weight: 10}
Problem 2: Fallback Target existiert nicht¶
Fehlermeldung:
Lösung:
# ❌ Falsch: Target "production" nicht in Liste
targets:
- {name: stable, weight: 100}
- {name: canary, weight: 0}
fallback_target: "production" # Existiert nicht!
# ✅ Korrekt: Fallback muss in targets sein
targets:
- {name: production, weight: 100}
- {name: canary, weight: 0}
fallback_target: "production"
Problem 3: Alle Requests gehen zu einem Backend¶
Symptom: Trotz 90/10 Split gehen 100% zu einem Backend
Mögliche Ursachen: 1. Provider nicht neu gestartet
-
Falsche Cluster-Namen (Envoy)
-
Kong Upstream nicht aktiv
Problem 4: Header-Routing funktioniert nicht¶
Symptom: Header wird ignoriert, fallback_target wird verwendet
Debug-Schritte:
-
Header-Namen Case-Sensitivity prüfen
-
Provider-spezifische Variable prüfen
-
APISIX vars prüfen
Problem 5: Traffic Split zu langsam¶
Symptom: Response Time deutlich höher mit Traffic Splitting
Mögliche Ursachen: 1. Zu viele if-Statements (Nginx)
-
Upstream Health Checks fehlen
-
Connection Pooling deaktiviert
Weiterführende Ressourcen¶
Provider-Dokumentation¶
- Envoy Weighted Clusters
- Kong Load Balancing
- APISIX traffic-split Plugin
- Traefik Weighted Services
- Nginx split_clients
Deployment Patterns¶
GAL Beispiele¶
Version: 1.4.0 Feature: A/B Testing & Traffic Splitting Letzte Aktualisierung: 2025-01-22