Blazor Zero Downtime
C# Blazor Zero Downtime Deployment Rolling Update Blue-Green Canary SignalR Health Check Graceful Shutdown Kubernetes Production
| Strategy | Downtime | Rollback | Resource | Complexity |
|---|---|---|---|---|
| Rolling Update | Zero | Rolling Back | +1 Pod | ต่ำ |
| Blue-Green | Zero | Instant (สลับ Traffic) | 2x Resource | ปานกลาง |
| Canary | Zero | ส่ง 100% กลับเวอร์ชันเก่า | +10-20% Resource | สูง |
| Recreate | มี Downtime | Deploy เวอร์ชันเก่า | ปกติ | ต่ำ |
Rolling Update Configuration
# === Blazor Server Rolling Update ===
# apiVersion: apps/v1
# kind: Deployment
# metadata:
# name: blazor-app
# spec:
# replicas: 3
# strategy:
# type: RollingUpdate
# rollingUpdate:
# maxSurge: 1
# maxUnavailable: 0
# template:
# spec:
# terminationGracePeriodSeconds: 60
# containers:
# - name: blazor
# image: registry.example.com/blazor-app:v2.0
# ports:
# - containerPort: 8080
# lifecycle:
# preStop:
# exec:
# command: ["/bin/sh", "-c", "sleep 15"]
# readinessProbe:
# httpGet:
# path: /health/ready
# port: 8080
# initialDelaySeconds: 5
# periodSeconds: 5
# failureThreshold: 3
# livenessProbe:
# httpGet:
# path: /health
# port: 8080
# initialDelaySeconds: 15
# periodSeconds: 10
# minReadySeconds: 10
# Program.cs - Graceful Shutdown
# var builder = WebApplication.CreateBuilder(args);
# builder.Services.AddHealthChecks()
# .AddDbContextCheck("database")
# .AddCheck("signalr", () => HealthCheckResult.Healthy());
#
# var app = builder.Build();
# app.MapHealthChecks("/health");
# app.MapHealthChecks("/health/ready");
#
# app.Lifetime.ApplicationStopping.Register(() => {
# Log.Information("Shutting down gracefully...");
# Thread.Sleep(10000); // Wait for connections to drain
# });
from dataclasses import dataclass
@dataclass
class RollingConfig:
setting: str
value: str
purpose: str
blazor_note: str
configs = [
RollingConfig("maxSurge", "1",
"เพิ่ม Pod ใหม่ 1 ตัวก่อนลบ Pod เก่า",
"Pod ใหม่ต้อง Ready ก่อนรับ SignalR Connections"),
RollingConfig("maxUnavailable", "0",
"ห้ามลด Pod จนกว่า Pod ใหม่ Ready",
"ป้องกัน Connection Drop ระหว่าง Deploy"),
RollingConfig("minReadySeconds", "10",
"รอ 10 วินาทีหลัง Ready ก่อนดำเนินการต่อ",
"ให้ SignalR Hub เริ่มต้นเสร็จสมบูรณ์"),
RollingConfig("terminationGracePeriodSeconds", "60",
"รอ 60 วินาทีก่อน Force Kill",
"ให้เวลา SignalR Connection Drain"),
RollingConfig("preStop sleep 15", "sleep 15",
"รอ 15 วินาทีก่อน SIGTERM",
"ให้ Endpoint ถูกถอดจาก Service ก่อน"),
]
print("=== Rolling Update Config ===")
for c in configs:
print(f" [{c.setting}] = {c.value}")
print(f" Purpose: {c.purpose}")
print(f" Blazor: {c.blazor_note}")
Blue-Green Deployment
# === Blue-Green Deployment Script ===
# # deploy-blue-green.sh
# #!/bin/bash
# NEW_VERSION=$1
# CURRENT=$(kubectl get svc blazor-app -o jsonpath='{.spec.selector.version}')
#
# if [ "$CURRENT" = "blue" ]; then
# TARGET="green"
# else
# TARGET="blue"
# fi
#
# # Deploy new version to target
# kubectl set image deployment/blazor-$TARGET \
# blazor=registry.example.com/blazor-app:$NEW_VERSION
#
# # Wait for rollout
# kubectl rollout status deployment/blazor-$TARGET --timeout=300s
#
# # Run smoke tests
# ENDPOINT=$(kubectl get svc blazor-$TARGET -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
# curl -f "http://$ENDPOINT/health/ready" || exit 1
#
# # Switch traffic
# kubectl patch svc blazor-app -p "{\"spec\":{\"selector\":{\"version\":\"$TARGET\"}}}"
#
# echo "Switched to $TARGET (v$NEW_VERSION)"
@dataclass
class BlueGreenStep:
step: int
action: str
command: str
duration: str
rollback: str
steps = [
BlueGreenStep(1, "Deploy to Green",
"kubectl set image deployment/blazor-green",
"2-5 นาที",
"ไม่ต้อง (Blue ยังทำงาน)"),
BlueGreenStep(2, "Wait for Rollout",
"kubectl rollout status deployment/blazor-green",
"1-3 นาที",
"kubectl rollout undo"),
BlueGreenStep(3, "Smoke Test Green",
"curl /health/ready + E2E tests",
"1-5 นาที",
"ไม่ Switch ถ้า Test Fail"),
BlueGreenStep(4, "Switch Traffic",
"kubectl patch svc (change selector)",
"< 1 วินาที",
"Switch กลับ Blue ทันที"),
BlueGreenStep(5, "Monitor Green",
"Watch error rate, latency, logs",
"15-30 นาที",
"Switch กลับ Blue ถ้าผิดปกติ"),
BlueGreenStep(6, "Cleanup Blue",
"Scale down Blue (optional)",
"ทำเมื่อมั่นใจ",
"เก็บ Blue ไว้ 24 ชม. ก่อนลบ"),
]
print("=== Blue-Green Steps ===")
for s in steps:
print(f" Step {s.step}: {s.action} ({s.duration})")
print(f" Command: {s.command}")
print(f" Rollback: {s.rollback}")
SignalR Connection Handling
# === SignalR Zero Downtime Handling ===
@dataclass
class SignalRStrategy:
scenario: str
problem: str
solution: str
config: str
signalr_strategies = [
SignalRStrategy("Pod Termination",
"SignalR WebSocket Connection หลุดเมื่อ Pod ถูกลบ",
"preStop hook + Graceful Shutdown รอ Connection Drain",
"terminationGracePeriodSeconds: 60, preStop sleep 15"),
SignalRStrategy("Client Reconnection",
"Client ต้อง Reconnect หลัง Connection หลุด",
"Blazor Circuit Reconnect UI + Auto-reconnect",
"Blazor.start({circuit: {reconnectionOptions}})"),
SignalRStrategy("Session Affinity",
"SignalR ต้องกลับ Pod เดิม (Sticky Session)",
"Ingress Cookie Affinity + Redis Backplane",
"nginx affinity: cookie + AddSignalR().AddRedis()"),
SignalRStrategy("State Preservation",
"Circuit State หายเมื่อ Pod ตาย",
"เก็บ State ใน Redis/Database ไม่ใช่ Memory",
"IDistributedCache + Redis State Store"),
SignalRStrategy("Load Balancer Drain",
"New Connection ยังถูกส่งมา Pod ที่กำลังจะตาย",
"Readiness Probe Fail → ถอดจาก Service → Drain",
"readinessProbe + preStop + SIGTERM handler"),
]
print("=== SignalR Strategies ===")
for s in signalr_strategies:
print(f"\n [{s.scenario}]")
print(f" Problem: {s.problem}")
print(f" Solution: {s.solution}")
print(f" Config: {s.config}")
เคล็ดลับ
- preStop: ใช้ preStop sleep 15 ก่อน SIGTERM ให้ Endpoint ถูกถอด
- Drain: ตั้ง terminationGracePeriodSeconds 60 รอ Connection Drain
- Redis: ใช้ Redis Backplane สำหรับ SignalR Multi-pod
- Blue-Green: เก็บ Environment เก่าไว้ 24 ชม. ก่อนลบ
- Test: รัน Smoke Test ก่อน Switch Traffic ทุกครั้ง
Zero Downtime Deployment คืออะไร
Deploy ไม่หยุดบริการ Rolling Update Blue-Green Canary SignalR Graceful Shutdown Circuit Reconnect Kubernetes ผู้ใช้ไม่รู้สึก
Rolling Update ทำอย่างไร
maxSurge 1 maxUnavailable 0 minReadySeconds 10 terminationGracePeriod 60 preStop sleep Readiness Probe PDB SignalR Drain
Blue-Green Deployment ทำอย่างไร
2 Environment Blue Green Deploy Green Test Switch Traffic Service Selector Instant Rollback 2x Resource Smoke Test Monitor
Health Check ตั้งอย่างไร
ASP.NET Core AddHealthChecks Liveness /health Readiness /health/ready Startup Probe Database Redis SignalR Graceful Shutdown SIGTERM
สรุป
C# Blazor Zero Downtime Rolling Update Blue-Green Canary SignalR preStop Graceful Shutdown Health Check Redis Backplane Kubernetes Production