Skip to content

Node Surge Strategy: Sizing Cho Rollout Node Pool Ổn Định

Tại sao chiến lược surge quan trọng trong production

Nâng cấp một node pool về bản chất là thay thế toàn bộ node bằng node mới chạy image Kubernetes/OS mới — không có khái niệm "patch tại chỗ" cho node như control plane. Điều đó có nghĩa mọi Pod đang chạy trên node cũ đều phải bị di chuyển. Câu hỏi vận hành cốt lõi không phải "có nên nâng cấp" mà là: trong lúc di chuyển, cluster có đủ capacity để giữ toàn bộ workload chạy liên tục không? Câu trả lời hoàn toàn phụ thuộc vào cách bạn cấu hình surge strategy — sai một tham số, bạn có thể biến một upgrade định kỳ thành một sự cố giảm capacity giữa giờ cao điểm.

Internal model: thuật toán surge upgrade hoạt động theo từng bước như thế nào

Hai tham số, hai ngữ nghĩa hoàn toàn khác nhau

GKE cung cấp hai tham số cấu hình cho surge upgrade, và ngộ nhận phổ biến nhất là coi chúng như hai mặt của cùng một con số. Thực tế chúng kiểm soát hai pha khác nhau của rollout:

  • maxSurge (mặc định: 1): số node thêm vào node pool, vượt trên kích thước hiện tại, tại mỗi thời điểm trong quá trình upgrade — tính theo từng zone.
  • maxUnavailable (mặc định: 0): số node được phép ngừng hoạt động đồng thời trong quá trình upgrade — cũng tính theo từng zone.

"maxSurge: maximum number of additional, surge nodes that can be added to the node pool during an upgrade, per zone. maxUnavailable: maximum number of nodes that can be simultaneously unavailable during an upgrade, per zone" (Node pool upgrade strategies).

Hai tham số này không loại trừ nhau — bạn có thể kết hợp cả hai. Nhưng cấu hình mặc định (maxSurge=1, maxUnavailable=0) mang một ý nghĩa vận hành rất cụ thể: GKE ưu tiên tạo thêm capacity trước khi rút capacity cũ đi, chứ không rút trước rồi mới bù. Đây là lựa chọn thiết kế có chủ đích để tối đa hoá độ khả dụng của workload trong lúc upgrade.

Thuật toán khi maxSurge > 0: "tạo trước, xoá sau"

Với surge dương, mỗi node trong pool được xử lý theo trình tự 5 bước:

  1. Provision node mới — GKE tạo một node bổ sung (vượt trên kích thước pool gốc) chạy version đích.
  2. Chờ node mới sẵn sàng (Ready trong Kubernetes) trước khi động vào node cũ.
  3. Cordon node cũ — đánh dấu unschedulable, ngăn scheduler gán Pod mới vào đó.
  4. Drain node cũ — gửi eviction request tới toàn bộ Pod trên node, tôn trọng PodDisruptionBudget và terminationGracePeriodSeconds, tối đa 60 phút.
  5. Xoá node cũ.

Nguồn: Node pool upgrade strategies.

Điểm mấu chốt về mặt cơ chế: node mới phải Ready trước khi node cũ bị cordon. Đây chính là lý do surge upgrade giữ được capacity liên tục — tại bất kỳ thời điểm nào trong 5 bước trên, tổng số node "khả dụng để nhận Pod" không bao giờ thấp hơn kích thước pool gốc, miễn là maxUnavailable=0. Google Cloud xác nhận trực tiếp thuộc tính này:

"GKE maintains the existing node capacity during a surge upgrade, as long as maxSurge is more than 0 and maxUnavailable=0. Your workloads continue to run even when the node upgrade fails" (Standard cluster upgrades).

Câu cuối trong trích dẫn trên rất quan trọng: nếu node mới upgrade thất bại (ví dụ image pull lỗi, health check không pass), workload trên node cũ vẫn đang chạy vì bước cordon/drain chỉ xảy ra sau khi node mới Ready. Đây là lý do surge upgrade an toàn hơn về mặt capacity so với việc "xoá trước tạo sau".

Thuật toán khi không có surge (maxSurge=0, dựa vào maxUnavailable): "xoá trước, tạo lại"

Khi không cấp thêm capacity tạm thời, quy trình đảo ngược thứ tự rủi ro hơn:

  1. Cordon node cũ.
  2. Drain node cũ (tôn trọng PDB, tối đa 60 phút).
  3. Tái tạo node với configuration mới (không phải "tạo node khác" — chính node đó bị xoá và dựng lại).
  4. Chờ node mới Ready.
  5. Uncordon.

Ở mô hình này, trong khoảng thời gian giữa bước 2 và 4, cluster có ít capacity hơn bình thường — đây chính xác là ý nghĩa của maxUnavailable: bạn cho phép cluster "thiếu" một số node nhất định trong lúc rollout, đổi lại không cần request thêm quota Compute Engine cho node tạm thời.

Giới hạn concurrency: không chỉ là con số bạn đặt, mà là con số GKE cho phép

maxSurge/maxUnavailable không phải là "vô hạn cấu hình" — GKE áp giới hạn cứng lên tổng số node được xử lý đồng thời:

"Maximum simultaneous upgrades: Limited to 20 nodes for Autopilot, 100 for Standard" (Node pool upgrade strategies).

Với node pool đa zone, còn thêm một ràng buộc tuần tự khác: "Multi-zone node pools upgrade one zone at a time" — tức là dù bạn có set maxSurge=10, GKE vẫn xử lý từng zone lần lượt, chỉ áp dụng surge parallelism trong zone đang xử lý, không dàn đều song song trên tất cả zone cùng lúc. Đây là một constraint thường bị bỏ sót khi ước lượng thời gian upgrade cho pool trải 3 zone — tổng thời gian gần với "3 lần thời gian upgrade một zone" hơn là "1 lần upgrade song song 3 zone".

Constraints, trade-offs & failure modes

Quota là giới hạn cứng, và GKE tự động hạ cấp thầm lặng khi vượt quota

Một hành vi quan trọng cần biết trước khi lên kế hoạch capacity: nếu số surge node bạn yêu cầu vượt quota Compute Engine khả dụng trong project/region, GKE không báo lỗi và dừng — nó tự giảm mức độ song song:

"If surge node requests exceed Compute Engine quotas, GKE reduces the number of concurrent surge nodes to attempt to meet the quota" (Standard cluster upgrades).

Hệ quả vận hành: một upgrade được lên kế hoạch chạy trong 30 phút với maxSurge=5 có thể thực tế kéo dài hàng giờ nếu quota CPU trong region chỉ đủ cho 1-2 surge node đồng thời, và không có cảnh báo rõ ràng nào báo rằng bạn đang bị giới hạn quota — bạn chỉ thấy tiến độ rollout chậm bất thường. Đây là lý do bước sizing phải bao gồm kiểm tra quota Compute Engine (CPU, IP address, persistent disk) khớp với maxSurge × số zone × machine type trước khi bắt đầu upgrade, không phải sau khi thấy nó chậm.

Trade-off giữa tốc độ rollout và chi phí tạm thời

Surge cao hơn đồng nghĩa nhiều node tạm thời hơn chạy song song, rút ngắn tổng thời gian upgrade, nhưng cũng đồng nghĩa chi phí Compute Engine tăng tạm thời (bạn trả tiền cho cả node cũ và node mới trong lúc chuyển tiếp) và tăng áp lực lên control plane API server (nhiều node đăng ký/watch đồng thời hơn). Với cluster lớn (hàng trăm đến hàng nghìn node), surge quá cao còn có thể cạnh tranh watch/list load với cluster autoscaler đang cố gắng scale cùng lúc — hai cơ chế này không "biết" về nhau và có thể cùng request capacity từ managed instance group underlying tại cùng thời điểm.

Failure mode: maxUnavailable > 0 bị hiểu lầm là "an toàn hơn vì ít tốn tài nguyên"

Một sai lầm thiết kế phổ biến: chọn maxUnavailable=2 (thay vì giữ mặc định 0) với lý do "tiết kiệm chi phí, không cần tạo node phụ". Về mặt cơ chế, điều này đồng nghĩa bạn chủ động chấp nhận giảm capacity thật trong lúc upgrade — không phải một sự đánh đổi miễn phí. Nếu tổng capacity pool không có biên dự phòng (headroom), việc mất 2 node đồng thời có thể đẩy phần còn lại vào tình trạng resource pressure, gây Pod pending hoặc bị OOM-evict trên node còn lại — một hệ quả hoàn toàn khác với "upgrade chậm hơn một chút" mà maxSurge gây ra khi bị quota giới hạn.

Spot VM: ngoại lệ trong thứ tự cordon

Với node pool dùng Spot VM, thứ tự "chờ Ready rồi mới cordon" không được áp dụng nghiêm ngặt như node thường:

"Spot VMs in surge upgrades don't wait for readiness before cordoning existing nodes."

Đây là hệ quả tự nhiên của bản chất Spot VM (có thể bị preempt bất kỳ lúc nào, nên GKE không thể đảm bảo "chờ sẵn sàng" theo cách thông thường) — nhưng nó có nghĩa node pool Spot có độ đảm bảo capacity liên tục yếu hơn node pool on-demand trong lúc upgrade. Đây là một constraint cần đưa vào quyết định: nếu workload tier 1 chạy trên node pool Spot, surge upgrade không cung cấp cùng mức bảo vệ capacity như đã mô tả ở phần thuật toán chính.

Pattern minh họa: công thức sizing thực tế trước khi đặt tham số

Áp dụng cơ chế "tạo trước, xoá sau" vào một bài toán capacity cụ thể: giả sử node pool có 30 node trải trên 3 zone (10 node/zone), mỗi node chạy tối đa 20 Pod theo giới hạn max-pods-per-node. Với maxSurge=2, maxUnavailable=0, tại một thời điểm bất kỳ trong zone đang được xử lý, cluster có tối đa 12 node (10 gốc + 2 surge) — nghĩa là bạn cần đảm bảo quota Compute Engine trong zone đó đủ cho 12 node, không phải 10, và bạn cần đủ IP address trong subnet cho 2 node bổ sung tồn tại tạm thời. Nhân với machine type cụ thể (ví dụ e2-standard-4), đây là con số CPU/memory quota thật cần verify trước khi chạy — không phải ước lượng cảm tính. Bài học kiến thức ở đây: sizing surge không phải là chọn một số "cảm thấy an toàn", mà là một phép tính ràng buộc trực tiếp bởi quota thật của zone đang chạy.

Anti-pattern: đặt maxSurge cao đồng loạt cho mọi node pool mà không phân biệt workload tier

Sai lầm gốc rễ: áp cùng một cấu hình surge cho tất cả node pool trong cluster (kể cả pool chạy batch job không nhạy cảm downtime) với giả định "surge cao luôn tốt hơn". Hệ quả ở scale: tổng surge request từ nhiều pool cộng dồn cùng lúc có thể vượt quota project nhanh hơn dự tính, khiến GKE tự động hạ song song ở đúng pool bạn cần nhanh nhất (workload tier 1), vì quota bị pool ít quan trọng hơn "ăn" trước. Cách phòng tránh đúng: phân bổ surge theo tier — pool tier 1 được ưu tiên surge cao và chạy upgrade trước; pool batch/non-critical có thể dùng maxUnavailable (rẻ hơn, chấp nhận gián đoạn tạm) và chạy sau, tránh cạnh tranh quota cùng thời điểm.

GCP-native implementation guidance

Xem cấu hình surge hiện tại của một node pool:

bash
gcloud container node-pools describe NODE_POOL_NAME \
  --cluster=CLUSTER_NAME --location=LOCATION \
  --format="yaml(upgradeSettings)"

Cập nhật chiến lược surge cho một node pool (ví dụ tăng surge cho pool tier 1 trước cửa sổ upgrade):

bash
gcloud container node-pools update NODE_POOL_NAME \
  --cluster=CLUSTER_NAME --location=LOCATION \
  --max-surge-upgrade=3 \
  --max-unavailable-upgrade=0

Kiểm tra quota Compute Engine trong zone/region trước khi upgrade, đối chiếu với công thức maxSurge × số zone:

bash
gcloud compute regions describe REGION --format="table(quotas)"

References