Node Pool Upgrade Execution: Giám Sát Và Health Check Trong Lúc Rollout
Tại sao giám sát đúng cách quan trọng hơn việc chỉ "nhấn nút và chờ"
Sau khi đã quyết định chiến lược surge (file 02) và cấu hình PDB đúng (file 03), câu hỏi tiếp theo trong lúc upgrade thực sự đang chạy là: làm sao biết nó đang tiến triển bình thường, hay đang mắc kẹt? Đây là điểm mà nhiều đội vận hành thiếu kinh nghiệm phản ứng sai — một upgrade chạy chậm hơn dự kiến không đồng nghĩa nó bị lỗi, và một upgrade "trông có vẻ ổn" theo kubectl get nodes không đồng nghĩa nó không đang tiềm ẩn vấn đề. Phần này xây dựng mô hình đúng để đọc tín hiệu trong lúc rollout đang diễn ra.
Internal model: operation là đơn vị theo dõi đúng, không phải trạng thái node đơn lẻ
Mọi upgrade đều là một GKE Operation có lifecycle riêng
Khi bạn khởi tạo upgrade (thủ công hoặc tự động), GKE tạo một Operation object với operationType=UPGRADE_NODES (cho node pool) hoặc UPGRADE_MASTER (cho control plane, đã nói ở file trước). Operation này có lifecycle PENDING → RUNNING → DONE (hoặc ABORTING/ABORTED nếu bị cancel), độc lập với trạng thái từng node riêng lẻ. Đây là đối tượng đúng để theo dõi tiến độ tổng thể — không phải suy luận tiến độ từ việc đếm số node đã đổi version trong kubectl get nodes, vì cách đếm thủ công đó không phản ánh được các bước trung gian (node đang provision, đang drain, đang chờ PDB) mà Operation đã mã hoá sẵn trong trạng thái của nó.
gcloud container operations list --location=LOCATION \
--filter="operationType=UPGRADE_NODES"
gcloud container operations describe OPERATION_ID --location=LOCATIONNode health check gate: điều kiện thật để một node được coi là "upgrade thành công"
Ở bước 2 của thuật toán surge (đã mô tả ở file 02): "chờ node mới sẵn sàng trước khi cordon node cũ" — điều kiện "sẵn sàng" ở đây không phải một tín hiệu tuỳ ý, nó là NodeReady condition, được kubelet của node mới báo cáo lên API server qua heartbeat định kỳ. Cụ thể, kubelet tự đánh giá và publish một tập NodeCondition (Ready, MemoryPressure, DiskPressure, PIDPressure, NetworkUnavailable); node chỉ được coi là sẵn sàng nhận Pod khi Ready=True và các pressure condition đều False. Đây chính là "health check gate" thật sự đứng giữa việc surge node được tạo và việc nó được đưa vào phục vụ.
Hệ quả thực dụng: nếu một node mới bị stuck ở trạng thái không Ready (ví dụ CNI plugin lỗi, image pull chậm do registry rate-limit, hoặc taint chưa được gỡ đúng cách bởi một DaemonSet init), toàn bộ chuỗi surge cho zone đó dừng lại ở đây — GKE sẽ không cordon node cũ tương ứng cho tới khi gate này pass. Quan sát đúng vị trí "đứng" của upgrade (đang chờ node mới Ready, hay đang chờ node cũ drain) quyết định bạn nên tra log ở đâu: kubectl describe node <new-node> cho trường hợp đầu, hay kubectl get pdb / audit log eviction cho trường hợp sau.
Retry với backoff tăng dần: hành vi mặc định khi một node upgrade thất bại
Nếu một node cụ thể upgrade thất bại (ví dụ image mới không boot được), GKE không dừng toàn bộ operation ngay — nó tự động thử lại:
"Failed node upgrades trigger automatic retries with an increasing interval between retries" (Standard cluster upgrades).
Kết hợp với thuộc tính đã nêu ở file 02 ("GKE does not roll back the upgraded nodes... GKE tries the node pool auto-upgrade again until all the nodes are successfully upgraded"), mô hình đúng cần khắc vào đầu: một node pool upgrade không có khái niệm "thất bại và dừng lại" ở tầng mặc định — nó sẽ tự lặp lại vô hạn định với khoảng nghỉ tăng dần cho tới khi thành công hoặc bạn chủ động can thiệp (cancel operation). Đây là lý do một upgrade "chạy mãi không xong" thường không phải là dấu hiệu GKE bị treo, mà là dấu hiệu có một nguyên nhân gốc đang lặp lại liên tục (ví dụ quota, hoặc một node cụ thể luôn fail cùng một lỗi) — việc cần làm là tìm nguyên nhân đó, không phải chờ retry tự khỏi.
Extended-duration Pod: khi "chậm" là hành vi đúng theo thiết kế, không phải lỗi
Một tín hiệu dễ gây hoảng loạn nhưng thực chất đúng theo thiết kế: với upgrade do GKE tự khởi tạo (không phải thủ công), một số Pod có annotation "extended duration" có thể không bị evict trong tối đa 7 ngày — GKE chủ động chờ lâu hơn mức 60 phút PDB tiêu chuẩn cho các workload được đánh dấu cần thời gian hoàn tất dài (ví dụ batch job chạy lâu). Nếu bạn thấy một node "kẹt" ở trạng thái cordon nhiều ngày trong một auto-upgrade, trước khi kết luận đây là bug, hãy kiểm tra xem node đó có đang giữ một Pod loại này không — đây là hành vi có chủ đích, không phải một node bị treo cần can thiệp thủ công.
Chi tiết cơ chế blue-green upgrade: 5 phase tuần tự, khác hoàn toàn tư duy surge
Surge upgrade (file 02) thay từng node một cách tiệm tiến. Blue-green vận hành theo một mô hình khác hẳn — toàn bộ pool mới được tạo song song với pool cũ trước khi bất kỳ traffic nào chuyển sang:
Phase 1 — Create green pool: GKE provision một managed instance group hoàn toàn mới với version đích, chạy song song với pool hiện tại (blue). Ở phase này, chưa có Pod nào bị di chuyển; đây thuần túy là bước chuẩn bị capacity.
Phase 2 — Cordon blue pool: Toàn bộ node trong pool cũ bị đánh dấu unschedulable cùng lúc. Workload đang chạy vẫn tiếp tục chạy — cordon không drain, nó chỉ chặn Pod mới được scheduler gán vào các node này.
Phase 3 — Drain blue pool theo batch: Đây là phase khác biệt lớn nhất so với surge — thay vì drain từng node tuần tự vô điều kiện, blue-green cho phép kiểm soát batch bằng BATCH_NODE_COUNT (số node tuyệt đối mỗi batch) hoặc BATCH_PERCENT (tỉ lệ 0-1, luôn làm tròn xuống tối thiểu 1 node), cùng BATCH_SOAK_DURATION — thời gian nghỉ giữa các batch (mặc định 0 giây). Việc drain theo batch có kiểm soát này cho phép bạn quan sát ảnh hưởng của từng lô nhỏ trước khi tiếp tục lô kế, thay vì để toàn bộ traffic dồn sang green pool trong một cú swap tức thời.
Phase 4 — Soak node pool: Sau khi drain hoàn tất, GKE giữ nguyên trạng thái này trong NODE_POOL_SOAK_DURATION (mặc định 3600 giây/1 giờ, tối đa 604.800 giây/7 ngày) trước khi xoá blue pool. Đây chính là "cửa sổ rollback" thực chất của blue-green — trong khoảng soak, blue pool vẫn tồn tại (đã cordon, đã drain, nhưng chưa xoá), nên về lý thuyết có thể phục hồi nếu green pool bộc lộ vấn đề nghiêm trọng.
Phase 5 — Delete blue pool: Node cũ bị xoá vĩnh viễn. Với Pod nào (vì lý do bất thường) vẫn còn sót lại trên blue pool tới thời điểm này, việc xoá node giới hạn terminationGracePeriodSeconds xuống tối đa 60 phút — bất kể giá trị khai báo trong Pod spec cao hơn.
Tổng hợp từ Node pool upgrade strategies.
Bài học kiến thức: soak duration là công cụ, không phải bảo hiểm miễn phí
Nhiều team đặt NODE_POOL_SOAK_DURATION ở mức mặc định (1 giờ) mà không cân nhắc: nếu vấn đề của green pool chỉ lộ ra dưới tải cao (ví dụ traffic peak chỉ xảy ra buổi tối, còn bạn upgrade vào buổi sáng), 1 giờ soak sẽ trôi qua mà không phát hiện được gì, và blue pool đã bị xoá trước khi vấn đề thật xuất hiện. Việc chọn soak duration cần gắn với chu kỳ traffic pattern thật của workload, không phải một con số mặc định an toàn cảm tính.
Autoscaled blue-green: biến thể tối ưu chi phí, đánh đổi khả năng rollback
Autoscaled blue-green thay đổi hai điểm cơ chế quan trọng so với blue-green tiêu chuẩn: green pool khởi tạo với 0 node (không phải full-size ngay), và cluster autoscaler chủ động scale down các node blue đang underutilized, bỏ qua giới hạn --min-nodes đã cấu hình cho pool đó. Đây là điểm cần đặc biệt lưu ý khi giám sát: nếu bạn có alert dựa trên "node count dưới min-nodes là bất thường", alert này sẽ gây báo động giả trong lúc autoscaled blue-green đang chạy — cơ chế này chủ động vi phạm ràng buộc min-nodes như một phần thiết kế bình thường, không phải sự cố.
Đánh đổi rõ ràng nhất: autoscaled blue-green không có khả năng rollback — chỉ có thể cancel (dừng ở trạng thái hiện tại) hoặc resume, không thể quay lại trạng thái blue hoàn chỉnh như biến thể tiêu chuẩn. Đây là lý do biến thể này phù hợp cho môi trường tối ưu chi phí (dev/staging, hoặc production đã có độ tin cậy cao về version đích qua nhiều lần canary trước đó), không phù hợp cho lần đầu upgrade một minor version chưa được kiểm định kỹ trên môi trường thấp hơn.
Constraints, trade-offs & failure modes
Failure mode: nhầm lẫn dấu hiệu "chậm do quota" với "chậm do PDB" trong lúc giám sát
Cả hai nguyên nhân phổ biến nhất khiến rollout chậm (quota Compute Engine giới hạn surge, và PDB chặn eviction) đều biểu hiện giống nhau ở tầng quan sát hời hợt: "upgrade đang chạy nhưng số node hoàn tất tăng rất chậm". Phân biệt đúng đòi hỏi nhìn vào hai nguồn khác nhau: quota thể hiện qua việc surge node mới không được tạo thêm (kiểm tra bằng gcloud compute operations list cho lỗi quota ở tầng Compute Engine); PDB thể hiện qua việc node cũ đã cordon nhưng không drain xong (kiểm tra bằng log "Cannot evict pod" đã nêu ở file 03). Gán sai nguyên nhân dẫn tới hành động sai: tăng quota không giúp gì nếu vấn đề thực chất là PDB, và ngược lại.
Trade-off: giám sát chặt (poll operation liên tục) so với chi phí API call
Trong một cửa sổ upgrade dài (đặc biệt với blue-green có soak 7 ngày), việc poll gcloud container operations describe liên tục với tần suất cao tạo áp lực không cần thiết lên API server đúng lúc nó đang chịu tải từ chính quá trình upgrade. Cách vận hành hợp lý hơn là thiết lập giám sát dựa trên Cloud Monitoring alert cho các điều kiện kết thúc quan trọng (operation DONE, node NotReady kéo dài bất thường) thay vì polling thủ công liên tục.
GCP-native implementation guidance
Theo dõi trạng thái node pool và operation liên quan trong thời gian thực:
gcloud container node-pools describe NODE_POOL_NAME \
--cluster=CLUSTER_NAME --location=LOCATION \
--format="yaml(status,statusMessage)"
kubectl get nodes -l cloud.google.com/gke-nodepool=NODE_POOL_NAME \
-o custom-columns=NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion,READY:.status.conditions[-1].statusKiểm tra chi tiết một node đang không đạt health check gate:
kubectl describe node NODE_NAMECấu hình blue-green upgrade với batch và soak duration tường minh:
gcloud container node-pools update NODE_POOL_NAME \
--cluster=CLUSTER_NAME --location=LOCATION \
--node-pool-soak-duration=7200s \
--standard-rollout-policy=batch-node-count=2,batch-soak-duration=300s