Skip to content

Node Upgrade Strategies: Surge vs Blue-Green

Bài toán cốt lõi: Không thể vừa nhanh vừa không disruptive

Upgrade node pool là bài toán không có free lunch. Có ba biến số ràng buộc lẫn nhau:

  • Tốc độ upgrade — Hoàn thành trong bao lâu
  • Resource cost — Cần bao nhiêu compute overhead trong quá trình
  • Disruption risk — Workload bị ảnh hưởng như thế nào

Surge upgrade tối ưu cho tốc độ + resource, đánh đổi bằng disruption risk cao hơn. Blue-green tối ưu cho disruption risk, đánh đổi bằng resource cost và thời gian. Không có strategy nào "tốt hơn" tuyệt đối — lựa chọn phụ thuộc vào đặc điểm workload và yêu cầu SLA.

Surge Upgrade: Cơ chế rolling update

Mental model

Surge upgrade là rolling update — tại mỗi thời điểm, một số lượng nodes nhỏ được upgrade trong khi phần còn lại tiếp tục phục vụ workload. Tham số maxSurgemaxUnavailable kiểm soát trade-off giữa speed và disruption.

maxSurge: tăng capacity tạm thời

maxSurge xác định số nodes mới được tạo thêm trong khi nodes cũ vẫn đang chạy. Nodes mới này chạy version mới, nodes cũ chạy version cũ.

Pool size: 10 nodes
maxSurge=1, maxUnavailable=0 (cấu hình mặc định)

Bước 1: Tạo 1 node mới (version mới) → pool tạm thời có 11 nodes
Bước 2: Chờ node mới Ready
Bước 3: Cordon + drain 1 node cũ → pods migrate sang nodes có sẵn (bao gồm node mới)
Bước 4: Xóa node cũ → pool có 10 nodes (1 node đã upgrade)
Bước 5: Lặp lại cho 9 nodes còn lại

Yêu cầu quota: Surge nodes tốn quota thực. Với maxSurge=3 trên pool 100 nodes, cần đảm bảo project có quota cho 103 nodes (hoặc 100 + 3 VM cores) trong thời gian upgrade. Đây là điểm fail thực tế — quota exhaustion làm upgrade stuck.

maxUnavailable: upgrade in-place

maxUnavailable xác định số nodes có thể unavailable đồng thời trong quá trình upgrade. Không tạo surge node; thay vào đó cordon, drain và recreate node tại chỗ.

Pool size: 10 nodes
maxSurge=0, maxUnavailable=3

Bước 1: Cordon 3 nodes → không schedule pods mới
Bước 2: Drain 3 nodes → pods migrate sang 7 nodes còn lại
Bước 3: Recreate 3 nodes với version mới
Bước 4: Uncordon 3 nodes mới
Bước 5: Lặp cho 7 nodes còn lại

Trade-off: Không cần thêm quota, nhưng cluster tạm thời có ít capacity hơn trong quá trình upgrade. Nếu cluster đang ở mức utilization cao, pods có thể bị evict do không đủ resources.

Kết hợp maxSurge và maxUnavailable

Có thể kết hợp cả hai. Constraint duy nhất: maxSurge + maxUnavailable <= 20.

bash
# Aggressive upgrade: vừa có surge vừa có unavailable
# Nhanh nhất nhưng disruptive nhất
gcloud container node-pools update NODE_POOL_NAME \
    --cluster=CLUSTER_NAME \
    --max-surge-upgrade=5 \
    --max-unavailable-upgrade=5 \
    --zone=ZONE

Với cấu hình trên, trên pool 50 nodes:

  • Tại một thời điểm: 5 surge nodes được tạo + 5 nodes existing bị drain = 10 nodes đang trong quá trình upgrade
  • Tổng pool size tạm thời: 55 nodes (50 + 5 surge)
  • Thời gian upgrade giảm đáng kể so với mặc định (1 node tại một lúc)

Cấu hình mặc định và lý do thiết kế

Cấu hình mặc định của GKE: maxSurge=1, maxUnavailable=0

Đây là cấu hình conservative nhất: tại mọi thời điểm, chỉ 1 surge node được tạo, không có node nào bị làm unavailable trước khi có replacement. Pods không bao giờ bị evict vào môi trường có ít capacity hơn ban đầu.

Nhược điểm của default: Rất chậm. Pool 100 nodes với default sẽ mất khoảng 100 × (thời gian tạo node + thời gian drain) = thường là 3–6 giờ.

Pod scheduling trên surge nodes

Khi surge node được tạo, nó sẽ join cluster và trở thành schedulable ngay lập tức với version mới. Scheduler có thể schedule pods lên surge node trước khi drain bắt đầu trên bất kỳ node nào.

Potential issue: Nếu workload có nodeSelector hoặc nodeAffinity theo node pool, pods mới có thể không match surge node nếu node pool label chưa được cấu hình đúng. Kết quả: surge node idle, trong khi pods cần drain không có chỗ để migrate.

Kiểm tra: Đảm bảo surge nodes có đầy đủ labels/taints phù hợp với workload scheduling rules của pool.

Timeline của drain trong surge upgrade

T+0:   Node X bị cordon (mark unschedulable)
T+0:   GKE gửi DELETE event cho tất cả pods trên Node X
T+0:   Pods nhận SIGTERM, bắt đầu graceful shutdown
T+Xm:  terminationGracePeriodSeconds hết → SIGKILL nếu chưa exit
T+60m: Hard deadline — GKE force delete pods bất kể PDB và grace period
T+60m: Node X bị xóa (surge upgrade) hoặc recreate (maxUnavailable)

Force eviction sau 60 phút là điều KHÔNG thể negotiate. Nếu workload cần graceful shutdown dài hơn 60 phút (ví dụ: checkpoint ML job), surge upgrade không phải là chiến lược đúng.

Blue-Green Upgrade: Parallel environment với rollback

Mental model

Blue-green upgrade tạo ra một parallel node pool với version mới (green pool) trong khi pool hiện tại (blue pool) vẫn đang chạy workload. Pods được migrate từng batch từ blue sang green, với soak period để validate health. Chỉ sau khi validation thành công, blue pool mới bị xóa.

Khác với surge upgrade chỉ có 1 node tạm thời, blue-green có toàn bộ pool mới tồn tại đồng thời với pool cũ.

Năm phase của blue-green upgrade

Phase 1: Tạo Green Pool

GKE tạo các Managed Instance Groups (MIGs) mới cho green pool với:

  • Kubernetes version mới
  • Cùng machine type, disk type, labels, taints như blue pool
  • Node count = số nodes hiện tại trong blue pool (hoặc min nodes cho autoscaled variant)

Trong phase này, blue pool vẫn phục vụ 100% workload. Green pool tồn tại nhưng chưa có pods.

Lưu ý: Blue pool autoscaling bị pause trong suốt quá trình blue-green upgrade để tránh scale-up không cần thiết.

Phase 2: Cordon Blue Pool

Toàn bộ nodes trong blue pool bị cordon — không có pods mới được schedule lên blue pool. Pods đang chạy trên blue pool vẫn tiếp tục chạy bình thường.

Blue pool: [Node1✓] [Node2✓] [Node3✓]  ← Cordon, no new pods
Green pool: [NodeA□] [NodeB□] [NodeC□]  ← Ready, accepting pods

Phase 3: Drain Blue Pool theo batch

Drain diễn ra theo batches được cấu hình bởi BATCH_NODE_COUNT hoặc BATCH_PERCENT, với BATCH_SOAK_DURATION giữa mỗi batch:

Batch 1: Drain nodes 1,2 từ blue pool
→ Pods migrate sang green pool (hoặc các blue nodes chưa drain)
→ Đợi BATCH_SOAK_DURATION (ví dụ: 10 phút)

Batch 2: Drain nodes 3,4 từ blue pool
→ Pods tiếp tục migrate
→ Đợi BATCH_SOAK_DURATION

... tiếp tục cho đến khi toàn bộ blue pool được drain

Implication của BATCH_SOAK_DURATION: Đây là window để kiểm tra workload health sau mỗi batch migration. Nếu monitoring phát hiện lỗi, team có thể rollback trước khi drain tiếp.

Phase 4: Node Pool Soak

Sau khi tất cả nodes đã được drain khỏi blue pool, hệ thống vào soak period (mặc định: 1 giờ, tối đa: 7 ngày). Toàn bộ workload đang chạy trên green pool. Blue pool vẫn còn tồn tại nhưng không có workload.

Đây là window quan trọng nhất để rollback — nếu phát hiện regression trong soak period, blue pool vẫn còn đó và có thể restore bằng rollback command.

Phase 5: Xóa Blue Pool

Sau khi soak period kết thúc (tự động hoặc manual), blue pool bị xóa. Blue-green upgrade hoàn thành.

Lưu ý về deletion: Trong phase này, GKE attempt delete remaining pods trên blue pool mà không tôn trọng PDB. Tuy nhiên, trong thực tế, nếu drain phase đã hoàn thành đúng, không còn pods nào trên blue pool ở phase 5.

Cấu hình blue-green upgrade

bash
# Tạo node pool với blue-green upgrade bằng absolute batch count
gcloud container node-pools create NODE_POOL_NAME \
    --cluster=CLUSTER_NAME \
    --enable-blue-green-upgrade \
    --standard-rollout-policy=batch-node-count=2,batch-soak-duration=600s \
    --node-pool-soak-duration=3600s \
    --zone=ZONE

# Tạo với percentage-based batches (25% pool mỗi batch, soak 15 phút)
gcloud container node-pools create NODE_POOL_NAME \
    --cluster=CLUSTER_NAME \
    --enable-blue-green-upgrade \
    --standard-rollout-policy=batch-percent=0.25,batch-soak-duration=900s \
    --node-pool-soak-duration=7200s \
    --zone=ZONE

Thông số quan trọng:

Thông sốDefaultMô tả
batch-node-count1Số nodes drain mỗi batch (exclusive với batch-percent)
batch-percent% pool drain mỗi batch (0-1)
batch-soak-duration0sThời gian chờ giữa các batch để validate
node-pool-soak-duration3600s (1h)Soak period sau khi toàn bộ blue được drain

Rollback trong blue-green

Rollback là tính năng khác biệt lớn nhất của blue-green so với surge:

bash
# Rollback khi phát hiện vấn đề trong soak period
gcloud container node-pools rollback NODE_POOL_NAME \
    --cluster=CLUSTER_NAME \
    --zone=ZONE

Rollback sẽ:

  1. Uncordon blue pool nodes
  2. Cordon green pool nodes
  3. Drain pods từ green pool về blue pool
  4. Xóa green pool

Điều kiện để rollback thành công: Blue pool vẫn còn tồn tại (chưa vào phase 5). Nếu soak period đã kết thúc và blue pool đã bị xóa, rollback không khả thi.

Pod migration strategy trong blue-green

Pods từ blue pool không tự động migrate sang green pool khi blue nodes bị cordon. Chỉ khi node bị drain thì pods mới bị evict và reschedule.

Khi pods reschedule, scheduler cần tìm node phù hợp. Nếu pods có nodeSelector hoặc nodeAffinity theo node pool label, cần đảm bảo green pool có cùng labels. GKE tự động propagate node pool labels, nhưng custom labels cần được cấu hình tường minh.

Ví dụ, nếu pod có:

yaml
nodeSelector:
  cloud.google.com/gke-nodepool: production-pool

Thì green pool cần có label cloud.google.com/gke-nodepool: production-pool. GKE tự động set label này khi tạo green pool với cùng tên pool.

Anti-pattern: Deploy workload mới lên blue pool trong thời gian blue-green upgrade đang chạy. Pods mới deploy sẽ không match bất kỳ schedulable node nào (blue bị cordon, green đang chờ pods migrate) → Pods stuck Pending.

Autoscaled blue-green upgrade (Preview)

Biến thể autoscaled tối ưu cost bằng cách bắt đầu green pool với zero nodes:

bash
gcloud container node-pools create NODE_POOL_NAME \
    --cluster CLUSTER_NAME \
    --enable-autoscaling \
    --max-nodes=MAX_NODES \
    --enable-blue-green-upgrade \
    --autoscaled-rollout-policy=wait-for-drain-duration=259200s \  # 3 ngày
    --zone=ZONE

Cơ chế: Cluster Autoscaler scale down underutilized nodes trên blue pool khi workload migrate dần (do new pods schedule sang green). Không cần force drain — pods tự nhiên drift sang green khi CA scale down blue nodes sau khi chúng trở nên idle.

Trade-off của autoscaled variant:

  • Cost thấp nhất (green scale từ 0, blue scale xuống dần)
  • Không có rollback — vì blue pool drain là tự nhiên không có checkpoint
  • Duration không xác định (phụ thuộc CA behavior, workload churn rate)
  • Phù hợp cho batch workloads, không phù hợp cho stateful services cần rollback guarantee

So sánh chi tiết: Surge vs Blue-Green

Khía cạnhSurge (default)Blue-GreenAutoscaled Blue-Green
Resource overhead1 surge node tạm thời2× node pool đồng thờiTăng dần, tối ưu dần
Thời gian upgradeNgắn nhất (tuning được)Dài hơn (soak period)Dài nhất (CA-controlled)
RollbackKhông cóCó (trong soak window)Không có
PDB graceTối đa 1 giờCó thể kéo dài hơnKhông force drain
Disruption tức thờiCó (cordon + drain per node)Không (parallel existence)Không
Workload validationKhông cóCó (soak period)Có (gradual shift)
Stateful workloadsRủi ro (PV reattach)An toàn hơnAn toàn nhất
Phù hợp choGeneral workloadsCritical/stateful servicesBatch jobs

Khi nào dùng strategy nào: framework quyết định

Dùng Surge upgrade khi:

  1. Workload stateless, horizontally scalable — pods có thể evict và reschedule mà không có side effects
  2. terminationGracePeriodSeconds < 30 phút — drain hoàn thành trong 1 giờ limit
  3. PDB cho phép ít nhất 1 replica down — không có minAvailable: 100%
  4. Chi phí quan trọng — không muốn double resource cost
  5. Upgrade speed quan trọng — cần hoàn thành upgrade nhanh trong maintenance window

Tuning surge cho production:

bash
# Conservative cho workload quan trọng
--max-surge-upgrade=1 --max-unavailable-upgrade=0

# Balanced cho batch upgrade nhanh hơn  
--max-surge-upgrade=3 --max-unavailable-upgrade=0

# Aggressive (chỉ cho non-production)
--max-surge-upgrade=5 --max-unavailable-upgrade=5

Dùng Blue-Green upgrade khi:

  1. Workload stateful — databases, stateful applications với PVC
  2. Long graceful shutdown — pods cần >30 phút để drain sạch
  3. Yêu cầu rollback — business requirement phải có khả năng rollback nhanh
  4. Zero-downtime requirement nghiêm ngặt — không thể chấp nhận bất kỳ eviction nào
  5. Cần validation window — muốn chạy smoke tests trên green pool trước khi commit

Cấu hình production-grade cho stateful service:

bash
gcloud container node-pools create stateful-pool \
    --cluster=CLUSTER_NAME \
    --enable-blue-green-upgrade \
    --standard-rollout-policy=batch-percent=0.1,batch-soak-duration=1800s \
    --node-pool-soak-duration=86400s \  # 24 giờ soak
    --zone=ZONE

Với cấu hình trên: drain 10% pool mỗi batch, soak 30 phút giữa batch, toàn bộ soak 24 giờ. Có đủ thời gian để detect regression từ database workload.

Dùng Autoscaled Blue-Green khi:

  1. Batch processing jobs — không cần rollback, có thể restart nếu cần
  2. Cost optimization là ưu tiên — không muốn trả cho 2× capacity
  3. Workload tolerate rescheduling — job idempotent, có thể chạy lại
  4. Không cần rollback guarantee

Anti-patterns và failure modes thực tế

1. Maximal restrictive PDB + Surge upgrade

Scenario:

yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: critical-service-pdb
spec:
  minAvailable: "100%"  # Không cho phép bất kỳ pod nào unavailable
  selector:
    matchLabels:
      app: critical-service

Với PDB này và surge upgrade, GKE sẽ attempt evict pods, bị PDB chặn, đợi 1 giờ, rồi force evict bất kể PDB. Kết quả: service bị disrupted đúng vào 1 giờ sau khi drain bắt đầu — thường là không phải thời điểm team chuẩn bị.

Giải pháp: Dùng blue-green upgrade cho workload với PDB restrictive, hoặc điều chỉnh PDB thành minAvailable: 90% với đủ replicas.

2. Không đủ replicas cho PDB

Scenario: Service có 2 replicas và PDB minAvailable: 1. Trong surge upgrade, 1 pod bị evict, 1 pod còn lại — PDB được satisfy. Nhưng nếu cả 2 pods cùng chạy trên cùng 1 node đang được drain, chỉ 1 trong 2 bị evict lần đầu; lần 2 sẽ violate PDB.

Thực ra Kubernetes không cho phép evict nếu còn không đủ replicas. Chỉ khi maxDisruptions > 0 thì mới evict được. Nếu 2 replicas cùng node thì chỉ evict được 1 (vì sau khi evict 1, disruption count = 1 = maxDisruptions).

Giải pháp: minReplicas >= 3 kết hợp với topology spread constraints để đảm bảo pods spread across nodes.

3. Quota exhaustion trong surge upgrade

Với maxSurge=10 trên pool 100 nodes trong project gần limit quota, khi upgrade bắt đầu và cố tạo 10 surge nodes, request sẽ fail do quota exhaustion. Upgrade bị stuck với error message trong logs.

Kiểm tra trước khi upgrade:

bash
# Kiểm tra quota hiện tại
gcloud compute project-info describe \
    --format="table(quotas.metric,quotas.limit,quotas.usage)" | grep -i cpu

# Estimate surge capacity cần thiết
POOL_SIZE=100
MAX_SURGE=5
CURRENT_USAGE=<current_vcpu_usage>
echo "Cần thêm: $((MAX_SURGE * MACHINE_TYPE_VCPU)) vCPUs"

4. Stateful pods không tương thích với surge upgrade

Scenario: StatefulSet với PVC ReadWriteOnce. Khi pod bị evict trong surge upgrade, pod mới được schedule lên surge node. Nhưng PVC vẫn attached vào node cũ trong một số thời điểm. Kubernetes cần detach PV từ node cũ trước khi attach vào node mới.

Quá trình này có thể mất 1–2 phút (đặc biệt với Persistent Disk). Trong thời gian đó, pod mới stuck ở ContainerCreating. Nếu có nhiều pods stateful bị evict đồng thời (lớn maxSurge), tổng downtime tăng theo.

Giải pháp: Blue-green upgrade với soak period, hoặc surge với maxSurge=1terminationGracePeriodSeconds đủ dài để PV detach hoàn thành trước khi node bị xóa.

5. Blue-green timeout do soak period quá dài

Scenario: Cấu hình node-pool-soak-duration=7d (7 ngày). Trong 7 ngày, blue pool nodes vẫn còn tồn tại và bị tính phí. Nếu team quên monitor và không trigger rollback hoặc completion, bị double-charged 7 ngày.

Giải pháp: Set alerting khi blue-green upgrade bước vào soak phase. Có workflow rõ ràng: ai chịu trách nhiệm approve completion sau soak period.

Concurrent node pool upgrades: khi nào hữu ích

Theo mặc định, node pools trong cùng cluster upgrade tuần tự. Preview feature concurrent upgrades cho phép song song hóa:

Mặc định (sequential):
Pool A (30min) → Pool B (30min) → Pool C (30min) = 90min tổng

Concurrent:
Pool A ──────────────────── (30min)
Pool B ──────────────────── (30min)     = 30min tổng
Pool C ──────────────────── (30min)

Khi nào concurrent upgrade thực sự có giá trị:

  • Cluster có nhiều pools (5+) cho các team hoặc workload khác nhau
  • Pools độc lập về workload (pods từ pool A không cần pods từ pool B)
  • Cluster có đủ capacity để chịu disruption từ nhiều pools đồng thời

Khi nào KHÔNG dùng concurrent upgrade:

  • Pools phụ thuộc lẫn nhau (ví dụ: pool cho database và pool cho app server)
  • Cluster đã ở mức utilization cao
  • Workload có PDB restrictive — concurrent drain tăng nguy cơ PDB violation

References