Vấn đề: Khi các Pod rơi vào trạng thái "lơ lửng"
Bạn vừa thực hiện một deployment hoặc mở rộng (scale up) một dịch vụ, nhưng các pod mới không khởi động. Thay vào đó, chúng nằm ở trạng thái Pending vô thời hạn. Khi bạn kiểm tra log bằng lệnh describe, bạn thấy một thông báo gây khó chịu từ scheduler:
$ kubectl describe pod my-app-7f45b6d-abcde
...
Events:
Type Warning
Reason FailedScheduling
Message 0/3 nodes are available: 1 Insufficient cpu, 2 Insufficient memory. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod.
Đây là cách Kubernetes scheduler thông báo rằng nó đã kiểm tra mọi node trong cluster của bạn và không tìm thấy chỗ trống nào. Thậm chí nó đã tìm các pod có độ ưu tiên thấp hơn để loại bỏ (preemption), nhưng điều đó cũng không giải phóng đủ không gian. Về cơ bản, cluster của bạn đã đạt đến giới hạn dung lượng trên "giấy tờ".
Logic: Requests và Thực tế
Điểm mấu chốt là: Kubernetes scheduler đưa ra quyết định dựa trên Requests, chứ không phải mức độ sử dụng thực tế (real-time usage). Bạn có thể nhìn vào dashboard Grafana và thấy các node đang rảnh rỗi ở mức 15% CPU, nhưng scheduler vẫn báo lỗi "Insufficient cpu."
Khi bạn định nghĩa một pod, bạn chỉ định resources.requests. Scheduler sẽ cộng dồn các request của mọi pod đang chạy trên một node. Nếu dung lượng "Allocatable" (có thể phân bổ) còn lại nhỏ hơn request của pod mới, node đó sẽ bị loại. Ví dụ, nếu một node có 8000m CPU và các pod hiện tại đã request 7500m, một pod mới request 1000m sẽ không thể lập lịch (schedule) thành công, ngay cả khi mức sử dụng CPU thực tế gần như bằng không.
Phân tích thông báo lỗi:
- 1 Insufficient cpu: Một node đã bị các pod khác chiếm dụng gần như toàn bộ dung lượng CPU đã đăng ký.
- 2 Insufficient memory: Hai node không có đủ RAM chưa đăng ký (unreserved) để đáp ứng yêu cầu tối thiểu của pod.
- No preemption victims found:
PriorityClasscủa pod không đủ cao để thay thế các workload hiện có.
Bước 1: Chẩn đoán dung lượng Cluster
Trước khi thay đổi cấu hình, hãy xem các node đang ghi nhận trạng thái như thế nào. Chạy lệnh sau để so sánh tài nguyên đã phân bổ (allocated) với tổng dung lượng:
kubectl describe nodes | grep -A 7 "Allocated resources"
Hãy tập trung vào cột Requests. Nếu bạn thấy tỷ lệ phần trăm gần 95% hoặc 100%, bạn đã gặp phải nút thắt cổ chai. Trong khi kubectl top nodes hiển thị các chỉ số thực tế, scheduler lại bỏ qua những con số đó và ưu tiên các giá trị request đã được đặt trước.
Cách khắc phục nhanh: Điều chỉnh kích thước Resource Requests hợp lý
Nhiều đội ngũ thiết lập request cao "cho chắc ăn", dẫn đến tình trạng phân mảnh tài nguyên nghiêm trọng. Nếu pod của bạn thực sự không cần cả một nhân (core) để khởi động, việc giảm request có thể giải quyết vấn đề lập lịch ngay lập tức.
Hãy xem xét đoạn mã deployment phổ biến sau:
resources:
requests:
cpu: "1000m" # 1 Nhân thực - Có thực sự cần thiết khi khởi động không?
memory: "2Gi"
limits:
cpu: "2000m"
memory: "4Gi"
Hãy thử giảm requests trong khi vẫn giữ limits ở mức cao. Điều này giúp scheduler linh hoạt hơn trong khi vẫn cho phép pod tăng cường tài nguyên (burst) khi cần thiết:
resources:
requests:
cpu: "250m"
memory: "512Mi"
Giải pháp lâu dài A: Kích hoạt Cluster Autoscaler
Nếu các request của bạn đã chính xác và các node thực sự đã đầy, bạn cần thêm phần cứng. Nếu bạn sử dụng các dịch vụ quản lý như EKS, GKE hoặc AKS, bạn nên kích hoạt Cluster Autoscaler.
Khi autoscaler phát hiện một pod ở trạng thái Unschedulable do giới hạn tài nguyên, nó sẽ kích hoạt việc cấp phát một node mới. Khi instance mới gia nhập cluster, scheduler sẽ tự động đặt pod đang chờ xử lý vào đó. Quá trình này thường mất từ 2–5 phút tùy thuộc vào nhà cung cấp dịch vụ đám mây của bạn.
Giải pháp lâu dài B: Sử dụng Priority và Preemption
Liệu API production của bạn có đang bị chặn bởi một job chạy ngầm có độ ưu tiên thấp? Bạn có thể sử dụng PriorityClasses để thông báo cho Kubernetes biết pod nào quan trọng nhất. Điều này cho phép scheduler trục xuất các pod ít quan trọng hơn để nhường chỗ cho các pod quan trọng.
- Định nghĩa một PriorityClass:
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: critical-api
value: 1000000
globalDefault: false
description: "Use this for core production services."
- Áp dụng vào cấu hình Pod (Pod spec):
spec:
priorityClassName: critical-api
containers:
...
Kiểm tra Node Selectors và Taints
Đôi khi vấn đề không nằm ở tổng dung lượng, mà ở các ràng buộc hẹp. Nếu bạn sử dụng nodeSelector, affinity, hoặc tolerations, scheduler có thể chỉ đang tìm kiếm trên một nhóm nhỏ các node. Ví dụ, nếu bạn ghim một pod vào instance-type: m5.large và những node cụ thể đó đã đầy, pod sẽ tiếp tục ở trạng thái pending ngay cả khi bạn có mười node t3.medium đang trống.
Xác minh: Xác nhận kết quả khắc phục
Sau khi bạn mở rộng cluster hoặc giảm request, hãy theo dõi các event của pod. Bạn cần tìm thấy event Scheduled thành công:
$ kubectl get events --watch
...
Normal Scheduled 10s default-scheduler Successfully assigned default/my-app-abcde to ip-10-0-1-52.ec2.internal
Một cảnh báo cuối cùng: nếu pod của bạn khởi động nhưng lập tức bị crash với trạng thái OOMKilled, có khả năng bạn đã cắt giảm memory request quá mức. Luôn theo dõi hành vi của pod trong vài phút sau khi nó chuyển sang trạng thái Running.

