Lỗi Xảy Ra
Bạn kiểm tra cluster và thấy các pod bị kẹt ở trạng thái Evicted. Chạy kubectl describe pod sẽ cho bạn thấy thông báo như sau:
Status: Failed
Reason: Evicted
Message: The node was low on resource: memory. Threshold quantity: 100Mi, available: 95Mi.
Kubelet đã kill pod của bạn để bảo vệ sự ổn định của node. Đây không giống với OOMKilled — trường hợp đó xảy ra khi container vượt quá giới hạn bộ nhớ của chính nó. Eviction là kubelet hành động trước khi mọi thứ trở nên tệ hơn. Khi bộ nhớ trống trên node giảm xuống dưới một ngưỡng cấu hình sẵn (mặc định là 100Mi), kubelet bắt đầu xóa pod một cách chủ động.
Tại Sao Điều Này Xảy Ra
Kubelet giám sát bộ nhớ khả dụng so với các ngưỡng eviction của nó. Khi memory.available giảm xuống dưới 100Mi, nó bắt đầu evict pod theo thứ tự: pod BestEffort bị xóa trước, tiếp đến là Burstable, và pod Guaranteed bị xóa sau cùng. Pod của bạn không may nằm trong tầm ngắm.
Những nguyên nhân thường gặp dẫn đến tình trạng này:
- Pod không có
requestshaylimits— scheduler không biết chúng thực sự cần bao nhiêu bộ nhớ - Memory leak trong ứng dụng của bạn hoặc một trong các dependency của nó
- Node thực sự quá nhỏ so với khối lượng công việc đang chạy
- Một pod "hàng xóm ồn ào" tăng vọt vượt quá giới hạn ngay trước khi bị OOMKill
- Quá nhiều pod nhồi nhét vào một node vì resource requests bị thiếu
Bước 1: Xác Định Pod Nào Bị Evict và Tại Sao
Bắt đầu bằng cách liệt kê tất cả các pod bị evict trên mọi namespace:
kubectl get pods -A --field-selector=status.phase=Failed | grep Evicted
Xem toàn bộ thông báo eviction cho một pod cụ thể:
kubectl describe pod <pod-name> -n <namespace>
Sau đó kiểm tra node đang chịu áp lực:
kubectl describe node <node-name> | grep -A 10 'Conditions:'
Bạn cần tìm MemoryPressure: True. Cũng hãy chạy lệnh này để xem mức tiêu thụ tài nguyên hiện tại ở cấp độ node:
kubectl top nodes
Bước 2: Dọn Dẹp Các Pod Bị Evict
Các pod bị evict không tự biến mất. Chúng nằm ở trạng thái Failed và chiếm dụng các slot API object. Hãy xóa chúng đi:
# Xóa tất cả pod bị evict trong một namespace
kubectl delete pods -n <namespace> --field-selector=status.phase=Failed
# Hoặc trên tất cả namespace
kubectl delete pods -A --field-selector=status.phase=Failed
Bước 3: Thiết Lập Resource Requests và Limits
Không có block resources? Hãy sửa ngay. Scheduler xem các pod không có request là không tốn chi phí và vui vẻ xếp chồng chúng lên các node đã bận. Những pod đó cũng là đối tượng đầu tiên bị evict.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
image: my-app:latest
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
Đặt requests bằng mức ứng dụng của bạn sử dụng trong tải thông thường — kiểm tra bằng kubectl top pod trong vài ngày. Đặt limits ở mức trần bạn chấp nhận được. Cấu hình sai đây là nguyên nhân lớn nhất dẫn đến vấn đề eviction.
Bước 4: Kiểm Tra Dung Lượng Node So Với Tài Nguyên Đã Phân Bổ
Xem thực tế đã phân bổ bao nhiêu trên node gặp sự cố:
kubectl describe node <node-name> | grep -A 8 'Allocated resources'
Ví dụ đầu ra:
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 1850m (46%) 3200m (80%)
memory 6Gi (95%) 8Gi (127%)
Memory requests ở mức 95% với limits ở 127%? Node đó đang bị overcommit. Bất kỳ đợt tăng đột biến nào cũng sẽ kích hoạt eviction. Bạn cần nâng cấp node, thêm node vào cluster, hoặc di chuyển một số pod sang nơi khác.
Bước 5: Tìm Pod Ngốn Nhiều Bộ Nhớ Nhất
Sắp xếp pod theo mức tiêu thụ bộ nhớ để tìm cái đang dùng nhiều nhất:
kubectl top pods -A --sort-by=memory | head -20
Một pod liên tục chạy gần hoặc vượt quá request của nó thì hoặc là được cấu hình với giá trị quá thấp, hoặc có memory leak. Đối chiếu mức sử dụng thực tế với cấu hình hiện tại:
kubectl top pod <pod-name> -n <namespace>
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.containers[*].resources}'
Bước 6: Bảo Vệ Pod Quan Trọng Bằng QoS
Kubernetes xác định lớp Quality of Service từ cấu hình tài nguyên của bạn. Để đạt trạng thái Guaranteed — bị evict sau cùng — hãy đặt requests bằng với limits:
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "500m"
Với những thứ thực sự quan trọng, hãy kết hợp thêm PriorityClass:
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
preemptionPolicy: PreemptLowerPriority
globalDefault: false
---
# Trong Deployment spec của bạn:
spec:
template:
spec:
priorityClassName: high-priority
Bước 7: Nâng Cấp Hoặc Thêm Node (Nếu Cần)
Đôi khi node chỉ đơn giản là quá nhỏ. Thêm dung lượng trên các cluster được quản lý:
# EKS — cập nhật node group
eksctl scale nodegroup --cluster=<cluster> --nodes=5 --name=<nodegroup>
# GKE — resize node pool
gcloud container clusters resize <cluster> --node-pool=<pool> --num-nodes=5
# Hoặc để cluster autoscaler tự xử lý
kubectl get deployment cluster-autoscaler -n kube-system
Xác Nhận Đã Sửa Xong
# Xác nhận không còn MemoryPressure trên các node
kubectl get nodes
# STATUS phải là Ready, không phải MemoryPressure
# Kiểm tra điều kiện của node
kubectl describe node <node-name> | grep MemoryPressure
# Kết quả mong đợi: MemoryPressure False
# Theo dõi pod vẫn đang Running
kubectl get pods -n <namespace> -w
# Xác nhận không còn pod bị evict
kubectl get pods -A --field-selector=status.phase=Failed
Ngăn Chặn Tái Diễn
- Luôn thiết lập resource requests — không có ngoại lệ. Kể cả pod nhỏ. Dùng LimitRange để áp dụng mức tối thiểu trên toàn cluster để không có gì lọt qua.
- Chạy VPA ở chế độ đề xuất trước. Vertical Pod Autoscaler sẽ theo dõi pod của bạn trong vài ngày và đề xuất các giá trị request thực tế trước khi bạn chốt lại.
- Cảnh báo về MemoryPressure trước khi nó gây hại. Bạn không muốn phát hiện ra eviction từ một deployment bị lỗi lúc 2 giờ sáng.
- Thêm PodDisruptionBudgets cho các service stateful hoặc quan trọng để eviction tuân thủ số lượng replica tối thiểu khả dụng.
- Áp dụng resource specs ở cấp namespace bằng LimitRange — điều này chặn việc tạo pod nếu requests không được chỉ định:
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: my-namespace
spec:
limits:
- default:
memory: 512Mi
cpu: 500m
defaultRequest:
memory: 256Mi
cpu: 250m
type: Container

