OOMKilled Thực Sự Có Nghĩa Là Gì
Container của bạn đã cố sử dụng nhiều bộ nhớ hơn giới hạn được cấu hình. Kernel Linux kích hoạt OOM killer, chấm dứt tiến trình, và Kubernetes báo cáo lý do thoát là OOMKilled.
Đây không phải là crash. Ứng dụng không ném ra exception hay gặp bug — kernel đã kill nó từ bên ngoài. Bạn sẽ thấy điều này trong kubectl describe pod:
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Started: Mon, 20 Mar 2026 10:12:00 +0000
Finished: Mon, 20 Mar 2026 10:14:33 +0000
Exit code 137 = 128 + 9 (SIGKILL). Đó là dấu hiệu nhận biết rõ ràng nhất.
Chẩn Đoán Trước Khi Làm Bất Cứ Điều Gì
Đừng chỉ tăng limit rồi hy vọng mọi thứ sẽ ổn. Trước tiên, hãy tìm hiểu container của bạn thực sự cần bao nhiêu bộ nhớ.
Kiểm tra trạng thái pod hiện tại
kubectl describe pod <pod-name> -n <namespace>
Cuộn xuống phần Limits và Requests. Sau đó tìm khối Last State — đó là nơi OOMKilled hiển thị.
Kiểm tra mức sử dụng bộ nhớ trực tiếp
# Mức sử dụng trên tất cả pod trong namespace
kubectl top pods -n <namespace>
# Theo từng container (hữu ích khi pod có sidecar)
kubectl top pod <pod-name> --containers -n <namespace>
Lấy số liệu bộ nhớ lịch sử
Nếu bạn có Prometheus, truy vấn working set để xem mức sử dụng đỉnh theo thời gian:
container_memory_working_set_bytes{pod="<pod-name>", container="<container-name>"}
Giả sử container của bạn đạt đỉnh 420Mi nhưng limit chỉ là 256Mi. Đó là chênh lệch 164Mi. Giờ bạn đã có con số cụ thể để làm việc, thay vì chỉ đoán mò.
Cách Sửa 1: Tăng Memory Limit
Hầu hết trường hợp, đây là cách khắc phục. Chỉnh sửa deployment và tăng limits.memory:
kubectl edit deployment <deployment-name> -n <namespace>
Hoặc cập nhật trực tiếp trong manifest:
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi" # trước là 256Mi, tăng gấp đôi
cpu: "500m"
kubectl apply -f deployment.yaml
Nguyên tắc chung: đặt limit bằng 1.5–2 lần mức đỉnh working set quan sát được. Nếu ứng dụng đạt đỉnh 420Mi, hãy thử 640Mi. Đừng nhảy thẳng lên 4Gi — điều đó chỉ che giấu vấn đề thực sự cho đến khi node hết bộ nhớ.
Cách Sửa 2: Bỏ Limit Hoàn Toàn (Hiểu Rõ Rủi Ro)
Một số workload có bộ nhớ biến động thực sự lớn — batch job, ML inference, xử lý video. Với những trường hợp đó, chỉ giữ request mà không đặt limit có thể hợp lý:
resources:
requests:
memory: "256Mi"
cpu: "250m"
# không có khối limits
Không có limit, container có thể dùng bất kỳ bộ nhớ nào còn trống trên node. Tuy nhiên rủi ro là có thật: một memory leak trong một pod có thể làm cạn kiệt bộ nhớ của tất cả pod khác trên node đó. Chỉ đi theo hướng này nếu việc lập kế hoạch dung lượng node của bạn vững chắc và bạn đang tích cực giám sát mức sử dụng.
Cách Sửa 3: Tìm và Sửa Memory Leak
Đã tăng limit hai lần mà vẫn bị OOMKilled? Ứng dụng của bạn đang bị leak. Bắt đầu với log từ container đã bị kill:
# Log từ instance trước (đã bị kill)
kubectl logs <pod-name> -n <namespace> --previous
Nơi cần kiểm tra phụ thuộc vào runtime:
- Java/JVM: quên đặt
-Xmxkhiến JVM tăng trưởng cho đến khi kernel kill nó. Đặt kích thước heap rõ ràng:-Xms256m -Xmx384m. Cũng giới hạn metaspace:-XX:MaxMetaspaceSize=128m. - Node.js: giới hạn V8 heap mặc định là ~1.5GB trên 64-bit — quá cao so với hầu hết container. Thêm
--max-old-space-size=384vào lệnh khởi động. - Python: DataFrame lớn, cache không giới hạn, hoặc tham chiếu vòng mà GC không thể thu dọn. Kiểm tra bằng
tracemallochoặcmemory-profiler.
Người dùng JVM hay gặp bẫy kinh điển: họ đặt container limit là 512Mi và -Xmx là 512Mi, rồi thắc mắc tại sao container bị kill. JVM cần bộ nhớ ngoài heap — metaspace, thread stack, GC overhead — thường từ 100–150Mi. Nếu heap của bạn là 512Mi, container limit phải ít nhất 650–700Mi.
# Cài đặt JVM an toàn cho container 512Mi
ENV JAVA_OPTS="-Xms128m -Xmx384m -XX:MaxMetaspaceSize=128m"
Cách Sửa 4: Để VPA Cho Bạn Biết Con Số Phù Hợp
Không chắc nên đặt limit bao nhiêu? Chạy VPA ở chế độ khuyến nghị. Nó theo dõi mức sử dụng thực tế và đề xuất các giá trị — mà không tự động áp dụng bất cứ điều gì:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Off" # chỉ khuyến nghị, sẽ không restart pod
kubectl apply -f vpa.yaml
# Kiểm tra lại sau một hoặc hai giờ
kubectl describe vpa my-app-vpa -n <namespace>
VPA sẽ hiển thị requests và limits được khuyến nghị dựa trên những gì nó quan sát được. Dùng những con số đó làm baseline của bạn.
Xác Nhận Kết Quả Sửa Chữa
Theo dõi quá trình rollout:
kubectl rollout status deployment/<deployment-name> -n <namespace>
Trên pod mới, xác nhận OOMKilled đã biến mất:
kubectl describe pod <new-pod-name> -n <namespace> | grep -A5 "Last State"
Last State trống hoặc exit bình thường nghĩa là bạn đã ổn. Sau đó theo dõi bộ nhớ trong giờ tiếp theo để chắc chắn nó không tăng dần:
watch kubectl top pods -n <namespace>
Phòng Ngừa
- Luôn đặt cả requests lẫn limits. Pod không có limit có thể tiêu thụ toàn bộ bộ nhớ node và kích hoạt OOMKill dây chuyền trên các workload không liên quan đến vấn đề.
- Cảnh báo trước khi bị kill. Trong Prometheus, kích hoạt alert khi working set vượt quá 80% limit trong hơn 5 phút. Điều đó cho bạn thời gian để xử lý.
- Dùng LimitRange để bắt các team quên đặt limit:
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
spec:
limits:
- default:
memory: 256Mi
defaultRequest:
memory: 128Mi
type: Container
- Load test trước khi deploy. Chạy ứng dụng dưới traffic thực tế và ghi lại mức đỉnh bộ nhớ. Đặt limit từ dữ liệu đo lường, không phải cảm tính.
- Theo dõi xu hướng, không chỉ đỉnh. Bộ nhớ tăng đột biến rồi trở về bình thường là bình thường. Bộ nhớ tăng đều đặn trong 6–12 giờ mà không ổn định là dấu hiệu leak.

