Blue-Green Deployments with API Gateways — Zero-Downtime Releases
In this tutorial, you'll learn about Blue-Green. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Blue-green deployment uses the gateway to instantly switch traffic between two identical environments, enabling zero-downtime releases with immediate rollback capability.
What You'll Learn
By the end of this lesson, you will implement blue-green environment switching at the gateway, manage database compatibility across environments, drain connections gracefully, and automate rollback.
Why It Matters
Blue-green deployments eliminate downtime during releases and provide instant rollback by simply switching traffic back to the previous environment.
Real-World Use
Durga Antivirus Pro maintains blue (current) and green (staging) environments, with the gateway switching all traffic to green within seconds after validation.
Blue-Green Architecture
flowchart LR
Traffic-->Gateway
Gateway-->Switch{Active Environment}
Switch-->|Blue Active|Blue[Blue Environment]
Switch-->|Green Active|Green[Green Environment]
Blue-->Backend1
Green-->Backend2
Environment Router
Route all traffic to the active environment with instant switching.
from typing import Dict, Optional, Tuple
import time
class BlueGreenRouter:
def __init__(self, blue_host: str, green_host: str):
self.blue_host = blue_host
self.green_host = green_host
self.active: str = "blue"
self.previous: str = "green"
self.switch_history: list = []
def get_active_host(self) -> str:
return self.blue_host if self.active == "blue" \
else self.green_host
def get_staging_host(self) -> str:
return self.green_host if self.active == "blue" \
else self.blue_host
def switch(self) -> Dict:
self.previous = self.active
self.active = "green" if self.active == "blue" \
else "blue"
switch_event = {
"timestamp": time.time(),
"from": self.previous,
"to": self.active,
}
self.switch_history.append(switch_event)
return switch_event
def rollback(self) -> Dict:
return self.switch()
def get_status(self) -> Dict:
return {
"active": self.active,
"active_host": self.get_active_host(),
"staging_host": self.get_staging_host(),
"last_switch": self.switch_history[-1]
if self.switch_history else None,
}
def route(self, path: str) -> str:
return self.get_active_host()
router = BlueGreenRouter("blue-svc:8080", "green-svc:8080")
print(f"Active: {router.get_active_host()}")
switch = router.switch()
print(f"Switched to: {router.get_active_host()}")
print(f"Switch event: {switch}")
Connection Draining
Drain active connections before switching environments.
from typing import Dict, Set, Optional
import time
import threading
class ConnectionDrainer:
def __init__(self, drain_timeout: int = 30):
self.active_connections: Dict[str, Set[str]] = {}
self.drain_timeout = drain_timeout
self.draining = False
def register_connection(self, env: str,
conn_id: str):
if env not in self.active_connections:
self.active_connections[env] = set()
self.active_connections[env].add(conn_id)
def remove_connection(self, env: str,
conn_id: str):
if env in self.active_connections:
self.active_connections[env].discard(conn_id)
def drain_environment(self, env: str) -> Dict:
connections = self.active_connections.get(env, set())
if not connections:
return {"status": "no_connections", "drained": 0}
self.draining = True
start = time.time()
while connections and \
time.time() - start < self.drain_timeout:
time.sleep(0.5)
connections = self.active_connections.get(
env, set()
)
remaining = len(connections)
return {
"status": "drained" if remaining == 0
else "timeout",
"drained": len(connections) - remaining,
"remaining": remaining,
"duration": time.time() - start
}
def health_check(self, host: str) -> bool:
return True
drainer = ConnectionDrainer(drain_timeout=5)
drainer.register_connection("blue", "conn-1")
drainer.register_connection("blue", "conn-2")
result = drainer.drain_environment("blue")
print(f"Drain result: {result}")
Database Compatibility Check
Ensure the new environment's database schema is compatible before switching.
from typing import Dict, List, Optional, Tuple
class DBCompatibilityChecker:
def __init__(self):
self.required_migrations: List[str] = []
self.breaking_changes: List[str] = []
def add_required_migration(self, migration: str):
self.required_migrations.append(migration)
def add_breaking_change(self, change: str):
self.breaking_changes.append(change)
def check_environment(self, env: str
) -> Tuple[bool, List[str]]:
issues = []
for migration in self.required_migrations:
if not self._migration_applied(env, migration):
issues.append(
f"Migration {migration} not applied on {env}"
)
for change in self.breaking_changes:
if self._change_present(env, change):
issues.append(
f"Breaking change {change} present on {env}"
)
return len(issues) == 0, issues
def _migration_applied(self, env: str,
migration: str) -> bool:
return True
def _change_present(self, env: str,
change: str) -> bool:
return False
checker = DBCompatibilityChecker()
checker.add_required_migration("V20260628_add_scan_status")
checker.add_breaking_change("DROP COLUMN old_format")
ok, issues = checker.check_environment("green")
print(f"DB compatible: {ok}, issues: {issues}")
Common Mistakes
Mistake 1: Long-Lived Connections
Websocket and long-poll connections survive environment switches. Drain them before switching.
Mistake 2: Database Backward Incompatibility
If the new schema is incompatible with the old code, rollback is impossible. Ensure both directions work.
Mistake 3: Cache Warm-up Delay
Cold caches in the new environment cause slow responses. Pre-warm caches before switching.
Mistake 4: Not Testing the Switch Process
Test the switch itself. Failures in the switch mechanism cause outage even if both environments are healthy.
Mistake 5: Configuration Drift
If environments have different configurations, behavior changes unexpectedly. Use infrastructure as code.
Practice Questions
- How does blue-green differ from Canary Deployment?
- What is connection draining and why is it necessary?
- Why must blue-green environments share the database?
- How do you handle cache warm-up in blue-green?
- What is the rollback procedure for blue-green?
Challenge
Build a blue-green deployment manager for the gateway that supports instant environment switching, connection draining with configurable timeout, health checks before switching, and automatic rollback if the new environment is unhealthy.
FAQ
Mini Project
Build a blue-green deployment system for the gateway that maintains two environment configurations, supports instant switching with a single API call, drains active connections before switching, performs health checks on the new environment, and provides instant rollback capability.
What's Next
Learn about Canary Deployments for gradual traffic shifting, or explore Gateway Testing for comprehensive test strategies.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro