Hai nhóm field trong container spec quyết định Pod sống ổn định hay không: resources (scheduler đặt Pod ở đâu, kernel giới hạn bao nhiêu) và probes (kubelet khi nào gửi traffic, khi nào restart container).
Requests và limits
requests |
limits |
|
|---|---|---|
| Ai dùng | Scheduler: chỉ đặt Pod lên node còn đủ allocatable trừ tổng requests | Kernel (cgroup) trên node lúc chạy |
| CPU | Trọng số chia CPU khi tranh chấp | Vượt thì bị throttle (chậm đi, không chết) |
| Memory | Cơ sở để xếp hạng khi node thiếu RAM | Vượt thì container bị OOMKilled (exit code 137) |
| Đơn vị | CPU: 500m = 0.5 core; Memory: 128Mi, 1Gi (hệ 2), 128M là hệ 10 |
|
| Thiếu field | Chỉ có limits thì requests mặc định bằng limits |
Không có limit thì dùng tới hết node (hoặc theo LimitRange của namespace) |
Thực tế hay dùng: đặt requests CPU và memory sát mức dùng thường ngày (xem kubectl top), đặt limits.memory, cân nhắc bỏ limits.cpu để tránh throttle khi app có burst ngắn (nhiều team chọn cách này; cluster nhiều tenant thì vẫn giữ limit CPU).
QoS class
Kubernetes tự gán QoS class cho Pod dựa trên resources của mọi container trong Pod:
| QoS class | Điều kiện | Khi node thiếu memory |
|---|---|---|
Guaranteed |
Mọi container có cả CPU và memory, requests == limits |
Bị đuổi (evict) sau cùng |
Burstable |
Không đạt Guaranteed nhưng ít nhất một container có request hoặc limit | Pod dùng vượt requests bị đuổi trước |
BestEffort |
Không container nào có requests/limits | Bị đuổi đầu tiên |
kubectl get pod <pod> -o jsonpath='{.status.qosClass}'
kubectl get pods -o custom-columns=NAME:.metadata.name,QOS:.status.qosClass
kubectl describe node <node> | grep -A10 "Allocated resources" # tổng requests/limits trên nodeCó hai cơ chế khác nhau: OOMKilled do container vượt limits.memory của chính nó (restart theo restartPolicy), còn Evicted do kubelet giải phóng tài nguyên khi node bị memory/disk pressure (Pod bị xoá, controller tạo Pod mới).
Probe
flowchart LR S["Container start"] --> SP{"startupProbe đạt?"} SP -->|"Chưa, quá failureThreshold"| R["Restart container"] SP -->|Đạt| L["liveness + readiness chạy định kỳ"] L --> RD{"readiness fail?"} RD -->|Có| EP["Bỏ khỏi EndpointSlice, không nhận traffic"] L --> LV{"liveness fail liên tiếp?"} LV -->|Có| R
| Probe | Fail thì sao | Dùng để |
|---|---|---|
startupProbe |
Quá failureThreshold thì restart; trong lúc chạy, liveness/readiness bị tạm hoãn |
App khởi động chậm (JVM, migrate, nạp model) |
readinessProbe |
Pod 0/1 Ready, bị rút khỏi Service; không restart |
App tạm chưa phục vụ được (warm-up, mất kết nối DB) |
livenessProbe |
Restart container | App treo (deadlock), không tự hồi phục |
Kiểu probe: httpGet (2xx, 3xx là đạt), tcpSocket, exec (exit 0 là đạt), grpc (gRPC health protocol).
| Field | Mặc định | Ghi chú |
|---|---|---|
initialDelaySeconds |
0 | Nên dùng startupProbe thay vì delay lớn |
periodSeconds |
10 | |
timeoutSeconds |
1 | Endpoint chậm hơn 1 giây là fail |
failureThreshold |
3 | Thời gian chịu đựng ≈ periodSeconds × failureThreshold |
successThreshold |
1 | Liveness và startup bắt buộc là 1 |
Ví dụ
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 2
selector:
matchLabels: { app: api }
template:
metadata:
labels: { app: api }
spec:
containers:
- name: api
image: registry.example.com/api:1.4.2
ports: [{ name: http, containerPort: 8000 }]
resources:
requests: { cpu: 200m, memory: 256Mi }
limits: { memory: 512Mi }
startupProbe:
httpGet: { path: /healthz, port: http }
periodSeconds: 5
failureThreshold: 30 # cho tối đa 150 giây để khởi động
readinessProbe:
httpGet: { path: /ready, port: http }
periodSeconds: 5
timeoutSeconds: 2
livenessProbe:
httpGet: { path: /healthz, port: http }
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3Áp mặc định cho namespace để Pod không thành BestEffort khi dev quên khai báo:
apiVersion: v1
kind: LimitRange
metadata:
name: defaults
namespace: app
spec:
limits:
- type: Container
defaultRequest: { cpu: 100m, memory: 128Mi }
default: { memory: 256Mi }Sai lầm hay gặp
| Sai lầm | Hậu quả |
|---|---|
| Liveness gọi tới DB/dependency bên ngoài | DB chậm là toàn bộ Pod restart dây chuyền. Liveness chỉ nên kiểm tra chính process |
| Liveness và readiness dùng chung endpoint, cùng ngưỡng | Pod bị restart thay vì chỉ rút khỏi Service |
timeoutSeconds: 1 mặc định với endpoint nặng |
Fail ngẫu nhiên khi CPU bị throttle |
limits.memory sát mức dùng của JVM/Node |
OOMKilled khi GC/burst. Đặt heap theo container (-XX:MaxRAMPercentage) |
| Không requests | Scheduler xếp quá nhiều Pod lên một node, rồi bị evict hàng loạt |
Khi nào dùng
- Mọi workload production: luôn có
requestsCPU/memory,limits.memory,readinessProbe. - livenessProbe: chỉ thêm khi app thật sự có thể treo mà không tự thoát; app crash thì kubelet đã tự restart.
- startupProbe: khi thời gian khởi động dao động lớn.
- Guaranteed: workload nhạy cảm (database, ingress controller) cần ít bị evict nhất.
- Pod không lên hoặc restart liên tục: tra Kubernetes – lệnh troubleshooting.