Home > Blog > tech

Zero-Downtime Deployment คืออะไร? กลยุทธ์ Deploy แบบไม่มี Downtime สำหรับ Production 2026

Zero-Downtime Deployment Strategies 2026
2026-04-11 | tech | 4100 words

Zero-Downtime Deployment คือการ Deploy application เวอร์ชันใหม่ขึ้น Production โดยที่ผู้ใช้ไม่รู้สึกถึงการเปลี่ยนแปลง ไม่มี Downtime ไม่มี Error page ไม่มี "ระบบกำลังปรับปรุง กรุณารอสักครู่" ผู้ใช้ใช้งานได้ต่อเนื่องตลอดเวลาระหว่างที่ระบบกำลัง Deploy เวอร์ชันใหม่

ในยุคที่ทุก Application ต้อง Available 24/7 ไม่ว่าจะเป็น E-commerce, Banking, Social media หรือ SaaS Downtime แม้แค่ไม่กี่นาทีก็สร้างความเสียหายมหาศาล ทั้งรายได้ที่สูญเสีย ความเชื่อมั่นของลูกค้าที่ลดลง และ SLA ที่ถูกละเมิด

ทำไม Zero-Downtime ถึงสำคัญ?

Revenue Loss: Amazon เคยประเมินว่า Downtime 1 นาที = เสียรายได้ ~$220,000 สำหรับ E-commerce ในไทยที่มียอดขาย 10 ล้านบาท/วัน Downtime 1 ชั่วโมง = เสีย ~416,000 บาท

User Trust: ผู้ใช้ที่เจอ Error page หรือ Downtime จะเสีย Trust และอาจไปใช้ Competitor จากสถิติ 88% ของผู้ใช้จะไม่กลับมาหลังเจอ Bad experience

SLA Obligations: หลาย Contract กำหนด SLA 99.9% uptime (Downtime ได้ไม่เกิน 8.7 ชั่วโมง/ปี) หรือ 99.99% (52.6 นาที/ปี) ถ้า Deploy ทุกสัปดาห์ แต่ละครั้งมี Downtime 30 นาที = 26 ชั่วโมง/ปี ซึ่งเกิน SLA 99.9%

Deploy Frequency: Modern software teams Deploy 10-100+ ครั้ง/วัน (CI/CD) ถ้าทุกครั้งมี Downtime แม้แค่ 1 นาที ก็เท่ากับ 100 นาที/วัน = Unacceptable

Deployment Strategies เปรียบเทียบ

StrategyDowntimeความซับซ้อนRollback SpeedResource CostRisk
Recreate (ลบเก่า สร้างใหม่)มี Downtimeง่ายมากช้า (Deploy ใหม่)ต่ำ (1x)สูง
Rolling Updateไม่มีง่ายปานกลางต่ำ (1x + buffer)ปานกลาง
Blue-Greenไม่มีปานกลางทันที (สลับ Route)สูง (2x)ต่ำ
Canaryไม่มีสูงเร็ว (ลด Traffic)ต่ำ-กลางต่ำมาก
Shadow/Darkไม่มีสูงมากไม่ต้อง (ยังไม่ได้ Serve จริง)สูง (2x)ต่ำมาก

Rolling Update — Default ของ Kubernetes

Rolling Update คือการทยอยเปลี่ยน Pods ทีละกลุ่ม จาก Version เก่าไป Version ใหม่ โดยตลอดเวลาจะมี Pods ที่พร้อมให้บริการอยู่เสมอ เป็น Default strategy ของ Kubernetes Deployment:

# Kubernetes Rolling Update
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 6
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1    # ลดได้ทีละ 1 pod (ต้อง Available อย่างน้อย 5)
      maxSurge: 2           # เพิ่มได้ 2 pods เกินจาก replicas (สูงสุด 8 pods)
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web-app
        image: myapp:v2.0    # เปลี่ยน Image tag → Trigger rolling update
        readinessProbe:       # สำคัญมาก! ต้องมี Readiness probe
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 10

# Deploy: kubectl apply -f deployment.yaml
# ดูสถานะ: kubectl rollout status deployment/web-app
# Rollback: kubectl rollout undo deployment/web-app

ข้อดี: ง่าย ใช้ Resources น้อย (ไม่ต้อง 2x) K8s ทำให้อัตโนมัติ Rollback ง่ายด้วย kubectl rollout undo

ข้อเสีย: ระหว่าง Rolling มี 2 Versions ทำงานพร้อมกัน (Old + New) ถ้า Version ใหม่มี Breaking API change อาจมีปัญหา Rollback ใช้เวลา (ต้อง Rolling กลับ)

Blue-Green Deployment — Instant Switch

Blue-Green คือการมี 2 Environment เหมือนกันทุกประการ: Blue (Production ปัจจุบัน) และ Green (Version ใหม่ที่เตรียมไว้) เมื่อ Green พร้อมแล้ว สลับ Traffic จาก Blue → Green ทันที:

# Blue-Green ด้วย Kubernetes Service
# Step 1: Blue running (current production)
apiVersion: v1
kind: Service
metadata:
  name: web-app
spec:
  selector:
    app: web-app
    version: blue       # ชี้ไปที่ Blue pods
  ports:
  - port: 80
    targetPort: 8080

---
# Blue Deployment (v1.0)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app-blue
spec:
  replicas: 6
  selector:
    matchLabels:
      app: web-app
      version: blue
  template:
    metadata:
      labels:
        app: web-app
        version: blue
    spec:
      containers:
      - name: web-app
        image: myapp:v1.0

---
# Step 2: Deploy Green (v2.0) — ยังไม่มี Traffic
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app-green
spec:
  replicas: 6
  selector:
    matchLabels:
      app: web-app
      version: green
  template:
    metadata:
      labels:
        app: web-app
        version: green
    spec:
      containers:
      - name: web-app
        image: myapp:v2.0

# Step 3: Test Green internally
# curl http://web-app-green:8080/health

# Step 4: Switch traffic (แก้ Service selector)
# kubectl patch service web-app -p '{"spec":{"selector":{"version":"green"}}}'

# Step 5: ถ้ามีปัญหา Rollback ทันที!
# kubectl patch service web-app -p '{"spec":{"selector":{"version":"blue"}}}'

# Step 6: ถ้า Green OK ลบ Blue
# kubectl delete deployment web-app-blue

ข้อดี: Switch ทันที (เปลี่ยน Selector) Rollback ทันที (เปลี่ยนกลับ) ทดสอบ Green ก่อน Switch ได้ ไม่มีช่วงที่ 2 Versions mixed

ข้อเสีย: ใช้ Resources 2 เท่า (ต้องมีทั้ง Blue + Green) Database schema change ต้องระวัง (ทั้ง Blue และ Green ใช้ DB เดียวกัน)

Canary Deployment — Traffic Splitting

Canary คือการ Deploy Version ใหม่แล้วส่ง Traffic เพียงส่วนน้อย (เช่น 5%) ไปทดสอบก่อน ถ้า Metrics ดี (Error rate ต่ำ, Latency ปกติ) จึงค่อยๆ เพิ่ม Traffic จนถึง 100%:

# Canary ด้วย Istio VirtualService
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: web-app
spec:
  hosts:
  - web-app
  http:
  - route:
    - destination:
        host: web-app
        subset: stable    # v1.0
      weight: 95           # 95% traffic
    - destination:
        host: web-app
        subset: canary    # v2.0
      weight: 5            # 5% traffic

---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: web-app
spec:
  host: web-app
  subsets:
  - name: stable
    labels:
      version: v1
  - name: canary
    labels:
      version: v2

# Canary progression:
# Day 1: 5% canary → Monitor errors, latency
# Day 2: 25% canary → Still OK? Continue
# Day 3: 50% canary → Check metrics
# Day 4: 100% canary → Full rollout!

# If errors spike at any stage → Set canary weight to 0 (instant rollback)
# Canary ด้วย Nginx Ingress (ไม่ต้อง Service mesh)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"  # 10% traffic
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-app-canary  # Service ชี้ไปที่ v2.0
            port:
              number: 80

ข้อดี: ลดความเสี่ยงมาก (ถ้า Bug เจอแค่ 5% ของ Users) ตัดสินใจจาก Metrics จริง (Data-driven) Rollback เร็ว (ลด Weight เป็น 0)

ข้อเสีย: ซับซ้อนกว่า (ต้องมี Traffic splitting: Istio, Nginx canary, etc.) ต้องมี Monitoring ดี (ต้องรู้ว่า Canary มีปัญหาหรือไม่) ใช้เวลานานกว่า (ต้อง Gradual rollout)

Shadow/Dark Deployment — Traffic Mirroring

Shadow deployment คือการส่ง Copy ของ Production traffic ไปที่ Version ใหม่ แต่ Response จาก Version ใหม่ถูก Discard (ผู้ใช้ได้รับ Response จาก Version เก่าเสมอ):

# Shadow deployment ด้วย Istio Traffic Mirroring
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: web-app
spec:
  hosts:
  - web-app
  http:
  - route:
    - destination:
        host: web-app
        subset: stable      # User ได้รับ Response จาก v1.0
    mirror:
      host: web-app
      subset: shadow         # Copy traffic ไปที่ v2.0
    mirrorPercentage:
      value: 100.0           # Mirror 100% ของ traffic

# v2.0 ได้รับ Traffic จริง (Production traffic)
# แต่ Response ของ v2.0 ถูก Discard
# ดูจาก v2.0 logs/metrics ว่ามี Error หรือ Performance issues ไหม
# ถ้า OK → ค่อย Switch traffic จริง (Blue-Green หรือ Canary)

ข้อดี: ปลอดภัยที่สุด (ผู้ใช้ไม่ได้รับผลกระทบเลย) ทดสอบด้วย Production traffic จริง (ดีกว่า Staging) เหมาะสำหรับ Major version changes

ข้อเสีย: ใช้ Resources 2 เท่า ซับซ้อนในการ Setup Write operations ต้องระวัง (Shadow อาจเขียนข้อมูลซ้ำ)

Database Migration สำหรับ Zero-Downtime

Database migration เป็นส่วนที่ยากที่สุดของ Zero-downtime deployment เพราะ Schema change อาจ Break application ที่ใช้ Schema เก่า:

Expand-Contract Pattern (แนะนำ)

# ปัญหา: ต้องเปลี่ยนชื่อ Column "name" เป็น "full_name"
# ถ้าทำทีเดียว: ALTER TABLE users RENAME COLUMN name TO full_name
# → App v1 (ใช้ "name") จะ Crash ทันที!

# วิธีที่ถูก: Expand-Contract Pattern (3 ขั้นตอน)

# Step 1: EXPAND — เพิ่ม Column ใหม่ (ไม่ลบ Column เก่า)
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
# Trigger เพื่อ Sync ข้อมูล
CREATE TRIGGER sync_name
AFTER INSERT OR UPDATE ON users
FOR EACH ROW SET NEW.full_name = COALESCE(NEW.full_name, NEW.name);
# Backfill ข้อมูลเก่า
UPDATE users SET full_name = name WHERE full_name IS NULL;

# ตอนนี้ทั้ง App v1 (ใช้ name) และ App v2 (ใช้ full_name) ทำงานได้!

# Step 2: MIGRATE — Deploy App v2 ที่ใช้ full_name
# ทำ Rolling update / Blue-Green / Canary

# Step 3: CONTRACT — ลบ Column เก่า (หลังจาก App v1 ถูกลบหมดแล้ว)
DROP TRIGGER sync_name;
ALTER TABLE users DROP COLUMN name;

Online DDL Tools

# สำหรับ MySQL — pt-online-schema-change (Percona)
pt-online-schema-change   --alter "ADD COLUMN full_name VARCHAR(255)"   --execute   D=mydb,t=users

# สำหรับ MySQL — gh-ost (GitHub)
gh-ost   --alter "ADD COLUMN full_name VARCHAR(255)"   --database=mydb   --table=users   --execute

# วิธีทำงาน:
# 1. สร้าง Shadow table ใหม่ (schema ใหม่)
# 2. Copy ข้อมูลจาก Table เก่าไปใหม่ทีละ batch
# 3. ใช้ Triggers/Binlog sync ข้อมูลที่เปลี่ยนระหว่าง copy
# 4. สลับ Table names (atomic rename)
# ไม่มี Downtime!

Connection Draining — Graceful Shutdown

Connection draining คือการรอให้ Requests ที่กำลัง Process อยู่เสร็จก่อน ก่อนที่จะ Shutdown pod เก่า:

# Kubernetes — Graceful shutdown
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      terminationGracePeriodSeconds: 60  # รอ 60 วินาทีก่อน Force kill
      containers:
      - name: web-app
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 5"]  # รอ 5 วินาทีให้ LB ถอด Pod ออก

# ลำดับเหตุการณ์เมื่อ Pod ถูก Terminate:
# 1. Pod ถูก Mark เป็น "Terminating" (ไม่รับ Request ใหม่จาก Service)
# 2. preStop hook ทำงาน (sleep 5 → รอให้ LB ถอด Pod ออก)
# 3. SIGTERM ถูกส่งไป App
# 4. App จัดการ Graceful shutdown:
#    - หยุดรับ Request ใหม่
#    - รอ Request ที่กำลัง Process อยู่ให้เสร็จ
#    - ปิด Database connections
#    - ปิด File handles
# 5. ถ้า App ไม่ Shutdown ภายใน terminationGracePeriodSeconds → SIGKILL

# Node.js — Graceful shutdown
# process.on('SIGTERM', async () => {
#   server.close(() => {
#     db.close();
#     process.exit(0);
#   });
#   setTimeout(() => process.exit(1), 30000); // Force exit after 30s
# });

Health Check Alignment

Health checks ที่ถูกต้องเป็นกุญแจสำคัญของ Zero-downtime deployment:

Probeจุดประสงค์ตั้งค่าอย่างไร
Readiness Probeบอกว่า Pod พร้อมรับ Traffic หรือยังตรวจสอบว่า App start เสร็จ, DB connected, Cache warm
Liveness Probeบอกว่า Pod ยัง Healthy อยู่หรือไม่ตรวจสอบว่า App ยังตอบสนอง ไม่ Hang
Startup Probeรอจน App start เสร็จก่อน ค่อยเริ่ม Readiness/Livenessสำหรับ App ที่ Start ช้า (30-60 วินาที)
# Health check ที่ถูกต้องสำหรับ Zero-downtime
containers:
- name: web-app
  startupProbe:            # รอ App start (สูงสุด 60 วินาที)
    httpGet:
      path: /health
      port: 8080
    failureThreshold: 12   # 12 x 5s = 60 วินาที max startup time
    periodSeconds: 5
  readinessProbe:          # พร้อมรับ Traffic?
    httpGet:
      path: /ready         # ตรวจสอบ DB, Cache, Dependencies
      port: 8080
    initialDelaySeconds: 0
    periodSeconds: 5
    failureThreshold: 3    # Fail 3 ครั้ง → ถอดออกจาก Service
    successThreshold: 1    # Pass 1 ครั้ง → เพิ่มกลับเข้า Service
  livenessProbe:           # App ยัง Alive?
    httpGet:
      path: /health        # Basic health check
      port: 8080
    initialDelaySeconds: 0
    periodSeconds: 10
    failureThreshold: 3    # Fail 3 ครั้ง → Restart pod
สำคัญ: Readiness probe ต้อง "เข้มงวด" (ตรวจ DB, Cache, Dependencies) ส่วน Liveness probe ต้อง "หลวม" (ตรวจแค่ App ยัง Respond) ถ้า Liveness เข้มงวดเกินไป Pod อาจถูก Restart loop!

Feature Flags — แยก Deploy ออกจาก Release

Feature flags ช่วยให้ Deploy code ใหม่ได้โดยไม่ต้อง "เปิดใช้" ทันที:

# Feature flag concept
# Deploy v2.0 with new feature (ซ่อนไว้หลัง Flag)
if feature_flags.is_enabled("new_checkout_flow", user_id):
    return new_checkout_flow(request)
else:
    return old_checkout_flow(request)

# ข้อดี:
# 1. Deploy ได้ทุกเมื่อ (Code อยู่แล้ว แต่ Flag ปิด)
# 2. เปิด Feature ให้ User บางกลุ่มก่อน (Beta, Internal)
# 3. ถ้ามีปัญหา ปิด Flag ทันที (ไม่ต้อง Rollback deploy)
# 4. A/B Testing ได้ง่าย

# Tools: LaunchDarkly, Unleash (open-source), Flagsmith, ConfigCat
# Simple implementation: ใช้ Environment variable หรือ Database flag

Rollback Strategies

StrategyRollback MethodSpeed
Rolling Updatekubectl rollout undo (Roll กลับทีละ Pod)นาที
Blue-Greenสลับ Route กลับไป Blueวินาที
Canaryลด Canary weight เป็น 0วินาที
Feature Flagปิด Flagวินาที (ไม่ต้อง Deploy)

Monitoring During Deployment

ระหว่าง Deploy ต้อง Monitor metrics เหล่านี้อย่างใกล้ชิด:

Error Rate: ถ้า Error rate เพิ่มขึ้นมากกว่า 1% → Rollback ทันที

Latency (P50, P95, P99): ถ้า P99 latency เพิ่มขึ้นมากกว่า 2x → Investigate, อาจต้อง Rollback

Throughput (RPS): ถ้า RPS ลดลงผิดปกติ → มีปัญหา

CPU/Memory: ถ้า Resource usage พุ่งสูงผิดปกติ → Memory leak? CPU-intensive bug?

Business Metrics: Conversion rate, Cart abandonment, Payment success rate ถ้าตัวเลข Business ลดลงระหว่าง Deploy → อาจมี Bug ที่ Monitoring ทั่วไปจับไม่ได้

# Automated rollback ด้วย Argo Rollouts
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: web-app
spec:
  strategy:
    canary:
      steps:
      - setWeight: 5           # 5% traffic
      - pause: {duration: 5m}  # รอ 5 นาที
      - analysis:               # ตรวจ Metrics อัตโนมัติ
          templates:
          - templateName: error-rate
      - setWeight: 25
      - pause: {duration: 5m}
      - analysis:
          templates:
          - templateName: error-rate
      - setWeight: 50
      - pause: {duration: 10m}
      - setWeight: 100

---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: error-rate
spec:
  metrics:
  - name: error-rate
    interval: 1m
    successCondition: result[0] < 0.01  # Error rate < 1%
    provider:
      prometheus:
        address: http://prometheus:9090
        query: |
          sum(rate(http_requests_total{status=~"5..",app="web-app"}[5m]))
          /
          sum(rate(http_requests_total{app="web-app"}[5m]))

# ถ้า Error rate > 1% → Argo Rollouts จะ Rollback อัตโนมัติ!

Real-World Zero-Downtime Checklist

ใช้ Checklist นี้ก่อนทำ Zero-downtime deployment:

Application: App รองรับ Graceful shutdown (handle SIGTERM), Health check endpoints พร้อม (/health, /ready), Backward-compatible API (Version ใหม่ทำงานกับ Version เก่าได้), Feature flags สำหรับ Risky features

Database: Schema changes เป็น Backward-compatible (Expand-Contract), ใช้ Online DDL tools (gh-ost, pt-osc) สำหรับ Large tables, Migration scripts ถูก Test แล้ว, Rollback migration script พร้อม

Infrastructure: Readiness probe ตั้งค่าถูกต้อง, terminationGracePeriodSeconds เพียงพอ, PDB (Pod Disruption Budget) ตั้งค่าแล้ว, Connection draining ทำงาน

Monitoring: Error rate dashboard พร้อม, Latency dashboard พร้อม, Automated rollback rules ตั้งค่าแล้ว (ถ้าใช้ Argo Rollouts), Alert ตั้งค่าสำหรับ Anomaly ระหว่าง Deploy

CI/CD: Pipeline ทำงานอัตโนมัติ, Tests ผ่านก่อน Deploy, Canary/Blue-Green configured, Rollback command/button พร้อมใช้

สรุป

Zero-Downtime Deployment ไม่ใช่แค่เทคนิค แต่เป็น Culture ที่ต้องปลูกฝังในทีม ตั้งแต่ Developer ที่เขียน Code ต้องคำนึงถึง Backward compatibility, DBA ที่ต้องทำ Expand-Contract migration, DevOps ที่ต้องตั้ง Health checks และ Monitoring ให้ถูกต้อง, และ Product team ที่ต้อง Plan releases ด้วย Feature flags

เริ่มต้นจาก Rolling Update (ง่ายที่สุด) → Blue-Green (เมื่อต้องการ Instant rollback) → Canary (เมื่อต้องการ Data-driven deployment) → Shadow (สำหรับ Critical changes) แต่ไม่ว่าจะใช้ Strategy ไหน สิ่งที่ขาดไม่ได้คือ Health checks ที่ถูกต้อง, Graceful shutdown, Backward-compatible database migrations, และ Monitoring ที่ครอบคลุม


Back to Blog | iCafe Forex | SiamLanCard | Siam2R