Skip to content

Node Auto-Provisioning — Tự Động Tạo Node Pool

Giới hạn mà NAP sinh ra để giải quyết

Cluster Autoscaler (file 5) làm một việc duy nhất: thay đổi số lượng node trong các node pool đã tồn tại. Nó không tạo pool mới. Điều này tạo ra một giới hạn cụ thể: để CA scale-up được cho một loại Pod, phải có sẵn một node pool mà template node của nó thỏa mọi ràng buộc của Pod.

Hệ quả trong thực tế: nếu một Pod cần GPU loại mới, một machine family chưa có pool, hay một cấu hình memory/CPU đặc biệt mà không pool nào hiện có khớp, CA sẽ mô phỏng và thấy không pool nào giúp được — Pod kẹt Pending vĩnh viễn, và CA ghi noScaleUp (file 7). Trên một platform phục vụ nhiều đội với nhu cầu hardware đa dạng, việc phải pre-create đủ mọi loại pool cho mọi nhu cầu là bất khả thi và lãng phí (nhiều pool min=0 vẫn tốn quản lý).

Node Auto-Provisioning (NAP) xóa giới hạn này: nó cho phép GKE tự động tạo node pool mới khi có Pod Pending mà không pool nào hiện có phục vụ được, và tự động xóa pool khi không còn cần. NAP biến CA từ "resize trong khuôn khổ pool định sẵn" thành "tự định hình cả tập node pool theo nhu cầu thật".

NAP làm gì, ở mức cơ chế

Theo tài liệu GKE (Node auto-provisioning), NAP tự động tạo node pool cho Pod đang Pendingxóa pool rỗng. Luồng:

  1. Có Pod Pending mà CA xác định không node group hiện có nào (sau scale-up) phục vụ được.
  2. NAP phân tích yêu cầu của Pod — requests, nodeSelector, affinity, taints/tolerations, GPU/accelerator, và ComputeClass nếu có.
  3. NAP chọn machine type và cấu hình node pool phù hợp, tạo pool mới trong giới hạn resourceLimits toàn cluster.
  4. Node mới join, Pod được lập lịch.
  5. Khi pool không còn Pod (và đủ điều kiện scale-down), NAP xóa cả pool.

Điểm mấu chốt: NAP và CA là một loop liên tục — NAP quyết định "có cần pool mới không và loại gì", CA quyết định "resize pool đó như thế nào". Trên GKE chúng do Google quản lý cùng nhau ở control plane.

Chọn machine type: NAP suy ra từ Pod

NAP không chọn máy ngẫu nhiên. Nó suy ra cấu hình tối ưu từ (Node auto-provisioning):

  • Pod spec: requests CPU/memory quyết định kích thước node tối thiểu; yêu cầu accelerator (nvidia.com/gpu...) quyết định cần máy có GPU; nodeSelector/affinity (cloud.google.com/machine-family...) thu hẹp lựa chọn.
  • ComputeClass: nếu Pod tham chiếu một ComputeClass (Chương 8), NAP dùng danh sách priorities trong đó để chọn machine family theo thứ tự ưu tiên — ví dụ thử n4 trước, fallback n2.

Ví dụ cấu trúc priority trong ComputeClass:

yaml
priorities:
- machineFamily: n4
- machineFamily: n2

Cơ chế này cho phép NAP vừa linh hoạt (tự chọn máy) vừa kiểm soát được (bạn quy định dải máy chấp nhận qua ComputeClass thay vì để NAP chọn tùy ý).

resourceLimits: dây cương bắt buộc của NAP

Đây là phần quan trọng nhất về mặt an toàn chi phí. NAP có thể tạo node — nghĩa là nó có thể tiêu tiền một cách tự động. Không có giới hạn, một lỗi cấu hình (ví dụ một Job tạo hàng nghìn Pod yêu cầu máy lớn) có thể khiến NAP tạo một lượng node khổng lồ. Vì thế NAP bắt buộcresourceLimits toàn cluster (Node auto-provisioning):

yaml
resourceLimits:
  - resourceType: 'cpu'
    minimum: 4
    maximum: 10
  - resourceType: 'memory'
    maximum: 64
  - resourceType: 'nvidia-tesla-t4'
    maximum: 4

Các flag gcloud tương ứng: --min-cpu, --max-cpu, --min-memory, --max-memory, --min-accelerator, --max-accelerator.

Đây là trần cứng cho tổng tài nguyên NAP được phép cấp toàn cluster. maximum là dây cương chi phí — đặt nó dựa trên ngân sách thật và công suất tối đa hợp lý, không phải "cho rộng để khỏi vướng". Đặc biệt với accelerator (GPU/TPU): luôn đặt maximum cho từng loại, nếu không một workload GPU cấu hình sai có thể yêu cầu NAP tạo node GPU đắt đỏ không giới hạn. (Liên quan: no.scale.up.nap.pod.gpu.no.limit.defined ở file 7 — NAP từ chối tạo node GPU nếu chưa định nghĩa limit GPU.)

Cấu hình mặc định cho node pool NAP tạo

Vì pool do NAP tạo tự động, bạn cần định nghĩa trước các thuộc tính mặc định cho chúng — nếu không node mới có thể thiếu hardening hoặc cấu hình bạn yêu cầu. Theo tài liệu GKE, các mặc định gồm (Node auto-provisioning):

Quản lý (auto-repair / auto-upgrade)

yaml
management:
  autoRepair: true
  autoUpgrade: true

Lưu ý quan trọng: tắt auto-upgrade khi dùng release channel có thể khiến GKE không tạo được node pool mới. NAP gắn chặt với release channel; giữ auto-upgrade bật là mặc định an toàn.

Node image

yaml
imageType: 'cos_containerd'   # hoặc 'ubuntu_containerd'

Service account và scopes

yaml
serviceAccount: SERVICE_ACCOUNT@PROJECT.iam.gserviceaccount.com
scopes: https://www.googleapis.com/auth/devstorage.read_only,...

Đây là điểm hardening quan trọng: định nghĩa một service account tối thiểu quyền cho node NAP, không để mặc định Compute Engine default SA (quá nhiều quyền). Pool NAP tạo sẽ kế thừa SA này.

Bảo mật node (Shielded)

yaml
shieldedInstanceConfig:
  enableSecureBoot: true
  enableIntegrityMonitoring: true

Mã hóa boot disk (CMEK)

yaml
bootDiskKmsKey: projects/KEY_PROJECT_ID/locations/KEY_LOCATION/keyRings/KEY_RING/cryptoKeys/KEY_NAME

Zone

yaml
autoprovisioningLocations:
  - us-central1-a
  - us-central1-b

Toàn bộ khối cấu hình này định nghĩa "khuôn" cho mọi node pool NAP sinh ra. Trong môi trường có yêu cầu compliance (PCI, HIPAA), khối này là nơi đảm bảo node tự tạo vẫn tuân thủ — Shielded, CMEK, SA tối thiểu quyền phải được đặt ở đây, không thể vá sau khi pool đã tạo.

Tích hợp với custom ComputeClass

Hướng hiện đại và được khuyến nghị để dùng NAP là qua custom ComputeClass (Chương 8), thay vì chỉ cấu hình NAP ở cấp cluster. ComputeClass cho phép định nghĩa "khuôn" pool ở cấp workload, sát với nhu cầu thật hơn.

Bật auto-creation cho một ComputeClass (Node auto-provisioning):

yaml
spec:
  nodePoolAutoCreation:
    enabled: true
  nodePoolConfig:
    imageType: IMAGE_TYPE
    serviceAccount: EMAIL
    autoRepair: true
    autoUpgrade: true
  priorities:
  - machineFamily: n4
  - machineFamily: n2

Điểm mới quan trọng: từ GKE 1.33.3-gke.1136000+, ComputeClass có thể dùng NAP mà không cần bật node auto-provisioning ở cấp cluster. Nghĩa là bạn có thể cấp quyền tự tạo pool cho từng ComputeClass cụ thể (ví dụ chỉ workload AI được tự tạo pool GPU) mà không mở NAP toàn cluster — kiểm soát mịn hơn nhiều.

Tuy nhiên có hạn chế: node pool gắn ComputeClass không hỗ trợ surge upgrade hay blue-green upgrade. Và một số cấu hình (CMEK, node integrity, secure boot) vẫn cần đi qua file cấu hình NAP cấp cluster chứ không khai báo được trực tiếp trong ComputeClass.

NAP với GPU, TPU và Spot

Phần lớn giá trị thực tế của NAP nằm ở workload accelerator và Spot — đúng những loại mà việc pre-create pool là khó nhất.

GPU và TPU

Workload AI/ML yêu cầu accelerator đa dạng (T4, L4, A100, H100, TPU v5e...), và nhu cầu thay đổi theo từng job. Pre-create một pool cho mỗi loại GPU là lãng phí (pool min=0 vẫn tốn quản lý) và cứng nhắc. NAP giải quyết bằng cách tạo node GPU đúng loại khi có Pod yêu cầu.

Nhưng có một điều kiện cứng: phải định nghĩa limit cho từng loại accelerator trong resourceLimits. Nếu không, NAP từ chối tạo node GPU và ghi no.scale.up.nap.pod.gpu.no.limit.defined (file 7). Đây là cơ chế an toàn cố ý — node GPU đắt gấp nhiều lần node thường, nên Google bắt buộc bạn khai báo trần rõ ràng trước khi cho NAP cấp tự động:

yaml
resourceLimits:
  - resourceType: 'nvidia-l4'
    maximum: 8
  - resourceType: 'nvidia-tesla-a100'
    maximum: 16

NAP cũng xử lý phần cài driver GPU và taint node GPU (nvidia.com/gpu, Chương 8) cho pool nó tạo, nên Pod chỉ cần khai báo nvidia.com/gpu trong limits và toleration phù hợp. Với TPU, NAP suy ra topology TPU từ Pod spec và tạo node pool TPU tương ứng.

Spot qua NAP

NAP có thể tạo node pool Spot khi Pod chấp nhận Spot (qua toleration cho taint Spot, Chương 6). Kết hợp với ComputeClass priorities, đây là pattern tối ưu chi phí mạnh: ưu tiên NAP tạo pool Spot trước, fallback sang on-demand khi Spot không đủ công suất. Nhớ rằng node Spot dùng location_policy: ANY mặc định (file 5) — ưu tiên cấp được hơn cân bằng zone, đúng cho bản chất Spot.

Hệ quả vận hành: với fleet AI/ML chạy job không gấp, NAP + Spot + ComputeClass priorities cho phép một platform tự động lấy công suất GPU rẻ nhất khả dụng, mà không cần platform team theo dõi thủ công loại máy nào đang có giá tốt ở zone nào.

NAP hay pre-created pool: khung quyết định

NAP không phải lúc nào cũng đúng. Pre-created node pool (CA resize trong khuôn) vẫn hợp lý trong nhiều trường hợp. Khung quyết định:

Tình huốngLựa chọnLý do
Nhu cầu hardware ổn định, biết trướcPre-created poolĐơn giản, dự đoán được, không cần NAP
Nhu cầu đa dạng, thay đổi liên tục (platform đa đội)NAP + ComputeClassKhông thể pre-create đủ mọi biến thể
GPU/TPU nhiều loại, dùng không đềuNAP (có limit per-type)Tránh pool GPU zombie min=0
Workload critical cần kiểm soát chặt từng nodePre-created poolKiểm soát thủ công cấu hình, upgrade strategy
Cần surge/blue-green upgradePre-created poolPool gắn ComputeClass không hỗ trợ surge/blue-green

Nguyên tắc chung: NAP cho sự đa dạng và biến động; pre-created pool cho sự ổn định và kiểm soát. Nhiều cluster production dùng cả hai: pre-created pool cho workload nền (system, web service ổn định), NAP cho workload bùng nổ/đa dạng (batch, AI, đội mới onboard). NAP không loại trừ pre-created pool — chúng cùng tồn tại trong một cluster.

Giới hạn quy mô và vận hành

  • Ngưỡng 200 node pool: cluster vượt 200 node pool tổng cộng sẽ gặp độ trễ autoscaling tăng lên (Node auto-provisioning). NAP có thể tạo nhiều pool, nhưng "tạo pool cho mọi biến thể nhỏ" sẽ làm phình số pool và chậm cả loop. Thiết kế ComputeClass/Pod sao cho NAP gom về một số lượng pool hợp lý.
  • Kế thừa mọi giới hạn của Cluster Autoscaler: NAP dùng chung loop với CA, nên mọi điều chặn scale-down (PDB, local storage, kube-system không PDB — file 5) đều áp dụng cho pool NAP tạo. Một pool NAP sẽ không tự xóa nếu có Pod chặn.
  • Không thay thế việc thiết kế resource đúng: NAP cấp node theo requests. Request sai vẫn dẫn tới pool sai kích thước. NAP không sửa được lỗi requests.
  • Độ trễ tạo pool lớn hơn resize: tạo một node pool hoàn toàn mới (provision MIG, cấu hình, node đầu tiên join) chậm hơn resize một pool có sẵn. Với workload nhạy độ trễ scale-up, một pool pre-created (dù min=0) đôi khi phản ứng nhanh hơn để NAP tạo pool từ đầu — cân nhắc khi loại workload đó xuất hiện thường xuyên và đều đặn.

NAP trên Autopilot

Trên cluster Autopilot, NAP về bản chất là cơ chế nền luôn bật — Autopilot không có khái niệm node pool do người dùng quản lý, nên mọi việc cấp node đều do GKE tự động dựa trên Pod và ComputeClass. Bạn không cấu hình resourceLimits thủ công như Standard; thay vào đó kiểm soát qua ComputeClass và resource request của Pod. Điều này nghĩa là phần lớn nội dung file này (resourceLimits, khuôn mặc định, service account) áp dụng cho Standard cluster có bật NAP; trên Autopilot, Google quản lý các quyết định đó và bạn tương tác chủ yếu qua ComputeClass (Chương 8) và Pod spec. Hiểu cơ chế NAP vẫn quan trọng trên Autopilot vì nó giải thích vì sao Pod được lập lịch lên loại node nào và vì sao đôi khi Pod chờ node mới.

Quan sát NAP: phân biệt với scale-up thường

Vì NAP và CA chia sẻ loop và log visibility (file 7), điều quan trọng là phân biệt được trong log đâu là "resize pool có sẵn" (CA thuần) và đâu là "tạo pool mới" (NAP). Các event nodePoolCreatednodePoolDeleted trong log container.googleapis.com/cluster-autoscaler-visibility chính là dấu vết của NAP — chúng chỉ phát khi NAP tạo hoặc xóa cả một pool, khác với scaleUp/scaleDown (chỉ thay đổi số node trong pool có sẵn).

Khi debug "Pod GPU kẹt Pending dù đã bật NAP", quy trình:

  1. Đọc noScaleUp cho Pod đó. Nếu thấy no.scale.up.nap.pod.gpu.no.limit.defined → chưa khai báo limit GPU trong resourceLimits.
  2. Nếu thấy no.scale.up.nap.pod.zonal.resources.exceeded → zone hết công suất GPU loại đó hoặc đụng quota Compute Engine; đổi zone (autoprovisioningLocations) hoặc xin tăng quota.
  3. Nếu không có event NAP nào → kiểm tra Pod có thật sự yêu cầu cấu hình mà không pool nào hiện có phục vụ được không; nếu một pool có sẵn khớp, CA sẽ resize pool đó thay vì NAP tạo pool mới.

Việc theo dõi tần suất nodePoolCreated/nodePoolDeleted cũng là tín hiệu sức khỏe: nếu NAP liên tục tạo rồi xóa pool cùng loại (churn pool), đó là dấu hiệu cấu hình chưa tối ưu — có thể nên pre-create pool cho loại workload đó thay vì để NAP tạo đi tạo lại, vừa giảm độ trễ vừa giảm churn.

Real-world: platform đa đội với nhu cầu hardware đa dạng

Xét một platform engineering team phục vụ nhiều đội: đội web (general-purpose), đội data (memory-optimized), đội ML (GPU). Không có NAP, platform team phải pre-create và quản lý mọi pool cho mọi nhu cầu, dự đoán trước machine family nào sẽ cần — bất khả thi khi nhu cầu thay đổi liên tục.

Với NAP + ComputeClass:

  • Mỗi đội định nghĩa (hoặc được cấp) một ComputeClass mô tả dải machine family chấp nhận và priority (Spot trước, on-demand sau chẳng hạn).
  • Workload tham chiếu ComputeClass; khi Pending mà không pool nào khớp, NAP tự tạo pool đúng loại.
  • resourceLimits cấp cluster (và limit GPU per-type) làm dây cương chi phí tổng.
  • Pool rỗng tự xóa — không còn "pool zombie" min=0 tốn quản lý.

Kết quả: platform team định nghĩa chính sách (ComputeClass, limits, hardening defaults) thay vì hạ tầng cụ thể (từng pool). Đây là sự chuyển dịch từ vận hành thủ công sang vận hành khai báo, đúng tinh thần platform engineering.

Cấu hình NAP cấp cluster: walkthrough đầy đủ

Khi dùng NAP ở cấp cluster (thay vì chỉ qua ComputeClass), bạn cung cấp một file cấu hình định nghĩa toàn bộ "khuôn" cho mọi pool NAP sinh ra. Đây là cách bật và cấu hình:

bash
gcloud container clusters update CLUSTER_NAME \
  --location=LOCATION \
  --enable-autoprovisioning \
  --autoprovisioning-config-file=nap-config.yaml

Với nap-config.yaml gộp các phần đã nêu thành một khuôn hoàn chỉnh:

yaml
resourceLimits:
  - resourceType: 'cpu'
    minimum: 4
    maximum: 200
  - resourceType: 'memory'
    minimum: 16
    maximum: 800
  - resourceType: 'nvidia-l4'
    maximum: 8
management:
  autoRepair: true
  autoUpgrade: true
shieldedInstanceConfig:
  enableSecureBoot: true
  enableIntegrityMonitoring: true
serviceAccount: gke-nap-nodes@PROJECT.iam.gserviceaccount.com
scopes:
  - https://www.googleapis.com/auth/cloud-platform
autoprovisioningLocations:
  - us-central1-a
  - us-central1-b
  - us-central1-c

Đọc khuôn này như một "policy hạ tầng": nó nói "bất kỳ node pool nào GKE tự tạo đều phải có Shielded bật, dùng service account tối thiểu quyền này, trải qua ba zone, trong giới hạn tài nguyên tổng này". Mọi quyết định compliance và bảo mật cho node tự tạo nằm ở đây — không có cơ hội vá sau khi pool đã sinh ra. Đây là lý do file cấu hình NAP nên được quản lý qua IaC (Terraform/Config Connector) và review kỹ như một artifact bảo mật, không phải chạy gcloud thủ công một lần rồi quên.

Vì sao service account tối thiểu quyền là điểm quan trọng nhất

Node GKE chạy với một service account quyết định mọi thứ Pod trên node đó có thể làm với Google Cloud API (trừ khi dùng Workload Identity, Chương 13). Nếu node NAP kế thừa Compute Engine default SA (thường có role Editor rộng), một Pod bị xâm nhập trên node NAP có blast radius khổng lồ. Định nghĩa một SA riêng cho node NAP với chỉ các quyền tối thiểu (đọc Artifact Registry, ghi log/metric) là kiểm soát bảo mật cơ bản — và vì node NAP tạo tự động, nó phải được khai báo trong khuôn, không thể sửa từng node sau.

Anti-patterns thường gặp

  • Bật NAP mà không đặt resourceLimits chặt (đặc biệt GPU). Mở cửa cho chi phí chạy không kiểm soát. Luôn đặt maximum theo ngân sách thật và limit cho từng loại accelerator.
  • Để node NAP dùng default Compute Engine SA. Node tự tạo kế thừa SA quá quyền. Định nghĩa SA tối thiểu quyền trong cấu hình mặc định.
  • Quên hardening trong khuôn mặc định. Node NAP sinh ra thiếu Shielded/CMEK nếu không khai báo trước — vá sau không được. Đặt từ đầu.
  • Để NAP tạo quá nhiều pool nhỏ. Pod/ComputeClass quá phân mảnh khiến số pool phình, vượt ngưỡng 200, chậm autoscaling. Gom nhu cầu về số biến thể hợp lý.
  • Tắt auto-upgrade trên cluster dùng release channel + NAP. Có thể khiến NAP không tạo được pool mới. Giữ auto-upgrade bật.
  • Kỳ vọng NAP sửa lỗi requests. NAP cấp node theo requests; requests sai → node sai. Right-size trước (file 3).

References