Blue-green deployments eliminate downtime by running two identical production environments.
How It Works#
Before deployment:
┌─────────────┐ ┌─────────────┐
│ Router │────▶│ Blue │ (Live)
└─────────────┘ │ (v1.0.0) │
└─────────────┘
┌─────────────┐
│ Green │ (Idle)
│ (v1.0.0) │
└─────────────┘
After deployment:
┌─────────────┐ ┌─────────────┐
│ Router │ │ Blue │ (Idle)
└─────────────┘ │ (v1.0.0) │
│ └─────────────┘
│ ┌─────────────┐
└──────────▶│ Green │ (Live)
│ (v2.0.0) │
└─────────────┘
Kubernetes Implementation#
1# blue-deployment.yaml
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: app-blue
6 labels:
7 app: myapp
8 version: blue
9spec:
10 replicas: 3
11 selector:
12 matchLabels:
13 app: myapp
14 version: blue
15 template:
16 metadata:
17 labels:
18 app: myapp
19 version: blue
20 spec:
21 containers:
22 - name: app
23 image: myapp:1.0.0
24 ports:
25 - containerPort: 8080
26---
27# green-deployment.yaml
28apiVersion: apps/v1
29kind: Deployment
30metadata:
31 name: app-green
32 labels:
33 app: myapp
34 version: green
35spec:
36 replicas: 3
37 selector:
38 matchLabels:
39 app: myapp
40 version: green
41 template:
42 metadata:
43 labels:
44 app: myapp
45 version: green
46 spec:
47 containers:
48 - name: app
49 image: myapp:2.0.0
50 ports:
51 - containerPort: 8080
52---
53# service.yaml - Switch by changing selector
54apiVersion: v1
55kind: Service
56metadata:
57 name: app-service
58spec:
59 selector:
60 app: myapp
61 version: blue # Change to 'green' to switch
62 ports:
63 - port: 80
64 targetPort: 8080Deployment Script#
1#!/bin/bash
2set -e
3
4NEW_VERSION=$1
5CURRENT_ENV=$(kubectl get svc app-service -o jsonpath='{.spec.selector.version}')
6
7if [ "$CURRENT_ENV" = "blue" ]; then
8 TARGET_ENV="green"
9else
10 TARGET_ENV="blue"
11fi
12
13echo "Current: $CURRENT_ENV, Deploying to: $TARGET_ENV"
14
15# Update target deployment with new image
16kubectl set image deployment/app-$TARGET_ENV app=myapp:$NEW_VERSION
17
18# Wait for rollout
19kubectl rollout status deployment/app-$TARGET_ENV
20
21# Run smoke tests
22./run-smoke-tests.sh app-$TARGET_ENV
23
24# Switch traffic
25kubectl patch svc app-service -p "{\"spec\":{\"selector\":{\"version\":\"$TARGET_ENV\"}}}"
26
27echo "Switched to $TARGET_ENV"AWS ALB Implementation#
1import {
2 ElasticLoadBalancingV2Client,
3 ModifyListenerCommand,
4} from '@aws-sdk/client-elastic-load-balancing-v2';
5
6const client = new ElasticLoadBalancingV2Client({});
7
8async function switchTraffic(
9 listenerArn: string,
10 targetGroupArn: string
11) {
12 await client.send(new ModifyListenerCommand({
13 ListenerArn: listenerArn,
14 DefaultActions: [{
15 Type: 'forward',
16 TargetGroupArn: targetGroupArn,
17 }],
18 }));
19}
20
21// Gradual traffic shift
22async function gradualSwitch(
23 listenerArn: string,
24 blueTargetGroup: string,
25 greenTargetGroup: string
26) {
27 const stages = [90, 70, 50, 25, 0]; // Blue percentages
28
29 for (const blueWeight of stages) {
30 await client.send(new ModifyListenerCommand({
31 ListenerArn: listenerArn,
32 DefaultActions: [{
33 Type: 'forward',
34 ForwardConfig: {
35 TargetGroups: [
36 { TargetGroupArn: blueTargetGroup, Weight: blueWeight },
37 { TargetGroupArn: greenTargetGroup, Weight: 100 - blueWeight },
38 ],
39 },
40 }],
41 }));
42
43 console.log(`Traffic: Blue ${blueWeight}%, Green ${100 - blueWeight}%`);
44 await sleep(60000); // Wait 1 minute between stages
45 }
46}Database Considerations#
1// Use backward-compatible migrations
2// Phase 1: Add new column (nullable)
3await db.query(`ALTER TABLE users ADD COLUMN new_field VARCHAR(255)`);
4
5// Phase 2: Deploy app that writes to both old and new columns
6// Phase 3: Backfill data
7await db.query(`UPDATE users SET new_field = old_field WHERE new_field IS NULL`);
8
9// Phase 4: Deploy app that only uses new column
10// Phase 5: Drop old column (after rollback window)
11await db.query(`ALTER TABLE users DROP COLUMN old_field`);Health Checks#
1// Comprehensive health check endpoint
2app.get('/health', async (req, res) => {
3 const checks = {
4 database: await checkDatabase(),
5 redis: await checkRedis(),
6 externalApi: await checkExternalApi(),
7 };
8
9 const healthy = Object.values(checks).every(c => c.status === 'healthy');
10
11 res.status(healthy ? 200 : 503).json({
12 status: healthy ? 'healthy' : 'unhealthy',
13 version: process.env.APP_VERSION,
14 checks,
15 });
16});
17
18// Readiness check for traffic
19app.get('/ready', async (req, res) => {
20 const ready = await isAppReady();
21 res.status(ready ? 200 : 503).json({ ready });
22});Rollback Strategy#
1#!/bin/bash
2# Quick rollback - just switch selector back
3
4CURRENT_ENV=$(kubectl get svc app-service -o jsonpath='{.spec.selector.version}')
5
6if [ "$CURRENT_ENV" = "blue" ]; then
7 ROLLBACK_ENV="green"
8else
9 ROLLBACK_ENV="blue"
10fi
11
12echo "Rolling back from $CURRENT_ENV to $ROLLBACK_ENV"
13
14kubectl patch svc app-service -p "{\"spec\":{\"selector\":{\"version\":\"$ROLLBACK_ENV\"}}}"
15
16echo "Rollback complete"Blue-green deployments provide instant rollback capability and zero-downtime releases.