Bạn apply một Deployment, kubectl get pod báo Pod Pending, còn kubectl describe pod có sự kiện FailedScheduling báo 0/3 nodes are available: 3 Insufficient cpu. Nếu biểu đồ CPU của node trông còn rảnh thì đó là hai thang đo khác nhau: scheduler cộng request, không đo mức dùng. Tài liệu này hướng dẫn tính tay phần CPU còn trống từ kubectl describe node, giải thích vì sao scheduler từ chối pod dù mức dùng thật có thể thấp, và đọc đúng hai thông báo dễ gây hiểu lầm là "No preemption victims found" và NotTriggerScaleUp. Ví dụ về Pod Pending dựa trên cluster LKE của Linode nhưng số liệu chỉ là minh hoạ; cách tính áp dụng cho mọi cluster Kubernetes.
Nếu muốn biết vì sao hệ thống chuyển sang Kubernetes trước, xem Website AI trên VPS: vì sao chúng tôi tính chuyện lên Kubernetes. Bài này chỉ tập trung vào phép tính chỗ trống.
Triệu chứng của Pod Pending vì Insufficient cpu
Một Pod Pending đứng yên ở trạng thái Pending, cột NODE trống. Phần Events của kubectl describe pod thường có ba loại dòng (sự kiện FailedScheduling có thể lặp lại nhiều lần):
Warning FailedScheduling default-scheduler 0/3 nodes are available: 3 Insufficient cpu.
preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod.
Normal NotTriggerScaleUp cluster-autoscaler pod didn't trigger scale-up: ...Khi Pod Pending, dòng đầu nói rằng cả ba node đều bị loại vì không đủ CPU để đặt chỗ. Chữ "đặt chỗ" là mấu chốt. Scheduler không nhìn mức CPU đang dùng, nó chỉ cộng các requests đã khai báo. Nếu đồ thị CPU thấp mà pod vẫn Pending, đó là hai thang đo khác nhau.
Đọc describe node để chẩn đoán Pod Pending: Capacity, Allocatable và Allocated
Chạy kubectl describe node <ten-node> và tìm bốn khối. Ví dụ rút gọn cho một node 1 vCPU:
Capacity:
cpu: 1
Allocatable:
cpu: 1
Non-terminated Pods:
Namespace Name CPU Requests CPU Limits
default ingress-controller 100m (10%) 0 (0%)
default app-queue 500m (50%) 500m (50%)
kube-system calico-node 250m (25%) 0 (0%)
Allocated resources:
cpu 850m (85%) 500m (50%)- Capacity là tổng tài nguyên máy. Allocatable là phần dành cho pod, đã trừ phần Kubernetes và hệ điều hành giữ lại (kube-reserved, system-reserved). Nếu nhà cung cấp không đặt phần giữ lại cho CPU thì Allocatable bằng Capacity, như trong ví dụ; còn memory thường có Allocatable nhỏ hơn Capacity. Scheduler dùng Allocatable. Xem thêm trong tài liệu reserve compute resources.
- Non-terminated Pods liệt kê từng pod đang chiếm chỗ, kể cả pod hệ thống như CNI, DNS, kube-proxy.
- Allocated resources là tổng cột Requests và Limits. Phần trăm ở đây là phần trăm so với Allocatable, và cột Requests là cột scheduler quan tâm. Nó không phải mức CPU đang chạy thật.
Các con số trên chỉ là ví dụ minh hoạ; hãy chạy lệnh trên cluster của bạn. Hãy chụp describe node trước khi sửa, vì sau khi thêm node hay hạ request nó không còn phản ánh lúc pod Pending.
Tính chỗ trống từng node khi Pod Pending
Công thức đơn giản: chỗ trống = Allocatable - tổng CPU Requests của mọi pod Non-terminated. Dùng ví dụ ba node 1 vCPU, mỗi node có calico-node đặt 250m:
| Node | Pod đang chiếm | Tổng request | Còn trống |
|---|---|---|---|
| node-1 | calico 250m, ingress 100m, app-queue 500m | 850m | 150m |
| node-2 | calico 250m, coredns 100m x 2, app 500m | 950m | 50m |
| node-3 | calico 250m, coredns 100m, app 500m | 850m | 150m |
Pod Pending xin 500m. Không node nào còn trống từ 500m, nên cả ba bị loại và bạn có đúng dòng 3 Insufficient cpu. Mức dùng thật có thể thấp hơn nhiều so với phần trăm request mà Pod Pending vẫn không được xếp lịch, vì request là phần đã hứa chứ không phải phần đang dùng. Hãy đối chiếu bằng kubectl top node (cần metrics-server).
Tổng chỗ trống của ba node là 350m nhưng không cộng lại được, vì một pod phải nằm trọn trong một node. Memory được tính tương tự CPU, và scheduler còn kiểm tra số pod tối đa, taint, affinity và các ràng buộc khác, nên "Insufficient cpu" chỉ là một trong các lý do có thể gặp.
Request bằng limit: một nguyên nhân Pod Pending hay bị bỏ sót
Một chi tiết dễ gây Pod Pending: nếu container chỉ khai limits mà không khai requests, Kubernetes tự đặt request bằng limit. Manifest sau trông "chỉ giới hạn 500m" nhưng thực tế đặt chỗ 500m:
resources:
limits:
cpu: "500m"
memory: "512Mi"
# kết quả: requests.cpu = 500m, requests.memory = 512Mi, QoS = GuaranteedKhi cả CPU và RAM đều có request bằng limit, pod thuộc lớp QoS Guaranteed. Một hệ quả thực tế: hạ limit của một workload khác (ví dụ queue worker từ 500m xuống 100m) giải phóng 400m request, và Pod Pending lọt vào được. Điều này chỉ đúng khi workload đó chỉ khai limits; nếu đã có requests riêng thì phải hạ requests mới giải phóng được chỗ. LimitRange cũng có thể đặt request mặc định khác. Cách khai rõ ràng, tránh phụ thuộc quy tắc ngầm:
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"Dù xếp lịch được, hãy chắc rằng workload bị hạ vẫn đủ CPU thật, đo bằng kubectl top pod trước khi hạ.
"No preemption victims found" trong Pod Pending nghĩa là gì
Khi Pod Pending vì không node nào vừa, scheduler thử preemption: đuổi bớt pod có priority thấp hơn để lấy chỗ. Thông báo "No preemption victims found for incoming pod" nghĩa là không có pod nào đủ điều kiện để đuổi. Preemption chỉ đuổi pod có priority thấp hơn pod mới. Pod mới thường có priority 0, pod ứng dụng khác cũng 0 và pod hệ thống như CNI hay DNS còn cao hơn (system-cluster-critical, system-node-critical), nên không có nạn nhân hợp lệ. Preemption cũng không giúp nếu đuổi đi mà vẫn không đủ chỗ. Đây là hành vi bình thường, không phải một lỗi riêng; muốn có thứ bậc thì khai PriorityClass cho workload quan trọng. Xem pod priority và preemption.
NotTriggerScaleUp: vì sao Pod Pending mà cluster không tự thêm node
Dòng NotTriggerScaleUp đến từ cluster-autoscaler: nó đã xem xét pod và quyết định không thêm node. Thông báo này đôi khi bị cắt ở "pod didn't trigger scale-up:" và không ghi lý do. Khi đó chưa có kết luận, dưới đây chỉ là các giả thuyết cần tự kiểm chứng:
- Node pool chưa bật autoscaling, hoặc đã chạm số node tối đa của pool.
- Pod xin nhiều hơn Allocatable của loại node trong pool, nên thêm node cùng loại cũng không vừa. Ví dụ request 800m không bao giờ vừa node 1 vCPU, vì 1000m trừ 250m của CNI chỉ còn 750m.
- Pod có ràng buộc (nodeSelector, affinity, volume theo vùng) khiến node mới không phù hợp.
Để kiểm tra, đọc kỹ phần sau dấu hai chấm của sự kiện (thường liệt kê lý do theo từng node group) và xem cấu hình autoscaler của node pool ở trang quản trị nhà cung cấp.
Xử lý Pod Pending: hạ request hay thêm node lớn hơn
Ví dụ một pod Elasticsearch request 800m CPU và 1,5Gi RAM, trên node 1 vCPU/2GB. CPU chỉ còn tối đa khoảng 750m sau khi trừ CNI và RAM cũng thiếu, nên pod sẽ không bao giờ vừa node loại này, thêm node cùng loại vẫn Pending. Thêm node 2 vCPU/4GB thì xếp được. Nếu event chỉ nêu lý do cho một phần số node, chưa nên kết luận cho node còn lại; hãy đọc describe node của nó. Nguyên tắc chọn, nhớ tính cả memory:
- Tính lại phép trừ: Allocatable của loại node trừ phần pod hệ thống phải lớn hơn request của pod.
- Nếu request đang cao hơn nhu cầu thật, hạ request (đo bằng
kubectl topdưới tải thật). - Nếu request hợp lý nhưng node nhỏ, thêm node lớn hơn thay vì thêm nhiều node nhỏ.
Một lỗi hay gặp là thử nhiều mức request liên tiếp, thấy Pending cho đến khi mức đủ nhỏ rồi kết luận là may rủi. Thực ra ranh giới tính được bằng phép trừ ở trên, chỉ cần biết node đó có coredns hay ingress hay không. Đừng hạ limits.memory xuống thấp hơn đỉnh dùng thật chỉ để pod xếp lịch được, vì bạn sẽ đổi Pending thành OOMKilled.
Sau khi thêm node: lỗi attach volume
Với Pod Pending có PVC, thêm node xong có thể gặp tiếp FailedAttachVolume. Hai thông báo hay gặp, kèm cách hiểu:
CSINode ... does not contain driver: node mới chưa đăng ký driver CSI. Thường tạm thời, chờ vài phút rồi kiểm tra lại.Volume with id ... not found: PV cũ trỏ tới volume đã bị xoá ở nhà cung cấp. Đây là suy luận từ thông báo; hãy đối chiếuvolumeHandlecủa PV với volume thực.
Một cách xử lý thường được ghi lại là thêm node, xoá PV và PVC cũ rồi cài lại thành phần đó. Đây là thao tác phá huỷ, nên cần thận trọng: kiểm tra persistentVolumeReclaimPolicy bằng kubectl get pv trước; chỉ xoá khi đã xác nhận volume thực sự không còn ở nhà cung cấp (đối chiếu volumeHandle) và dữ liệu có thể dựng lại hoặc đã sao lưu (ví dụ dữ liệu Prometheus); chỉ xoá đúng PV và PVC của thành phần đó. Cách tra cứu volume trên node nằm ở bài PVC trên node Kubernetes: tìm mount CSI, đo dung lượng và gỡ PVC kẹt Terminating.
Checklist chẩn đoán Pod Pending
- Với mỗi Pod Pending, chạy
kubectl describe podvà đọc dòng FailedScheduling, tách "Insufficient cpu" và "Insufficient memory". kubectl get pod -A -o wideđể biết node nào có gì.- Với từng node:
kubectl describe node, lấy Allocatable trừ tổng request. - So sánh với request của pod. Nếu lớn hơn Allocatable của loại node, thêm node cùng loại là vô ích.
- Đối chiếu
kubectl top nodeđể biết mức dùng thật, không suy từ phần trăm request. - Đọc lý do đầy đủ của NotTriggerScaleUp, kiểm tra autoscaler của node pool.
- Sau khi thêm node, kiểm tra attach volume nếu pod có PVC.
Tài liệu chính thức về quản lý tài nguyên cho container nêu rõ cách scheduler dùng request. Với website Laravel đóng gói thành image (xem Docker hoá Laravel: một image all-in-one cho Website AI), hãy đặt request theo nhu cầu của cả container chứ không chỉ riêng PHP.
Bài tiếp theo
Node đầy RAM: web chậm dù CPU thấp, overcommit limit và node NotReady. Bài sau xem chiều ngược lại của Pod Pending: pod đã chạy được nhưng web vẫn chậm vì RAM của node bị đặt quá mức.



