Resource requests, limits và QoS class
Mục tiêu học tập
Sau bài này, bạn phải làm được những việc sau, và làm được dưới áp lực thời gian của phòng thi:
- Phân biệt rõ
requestsvàlimits, biết cái nào scheduler dùng, cái nào kubelet dùng lúc runtime. - Viết đúng YAML
resourcescho container, kể cả khi đề chỉ cho một câu mô tả bằng tiếng Anh. - Xác định QoS class của một pod chỉ bằng cách đọc YAML, không cần chạy cluster.
- Giải thích được hai triệu chứng quen thuộc nhất: pod
Pendingvì thiếu tài nguyên và pod bịOOMKilled/Evicted. - Dùng
kubectl describe node,kubectl describe pod,kubectl set resourcesđể sửa nhanh trong đề thi.
Ôn tập nhanh
Bài trước bạn đã dựng DaemonSet, Job và CronJob. Điểm chung quan trọng: cả ba đều nhúng một pod template giống hệt Deployment, nên mọi thứ trong bài này áp dụng y nguyên cho spec.template.spec.containers[].resources.
Một lưu ý nối tiếp bài trước: DaemonSet chạy một pod trên mỗi node, nên nếu bạn đặt requests quá cao, pod DaemonSet sẽ Pending ở những node nhỏ. Đây là lỗi rất hay gặp khi làm đề CKA.
[!NOTE] Bài này chỉ nói về tài nguyên của container. Việc ép pod chạy đúng node theo nhãn hay theo taint sẽ học ở bài sau, đừng trộn hai thứ vào nhau khi làm bài thi.
Lý thuyết
requests và limits là hai cơ chế khác nhau
Nhiều bạn mới học gộp hai thứ này làm một, rồi làm sai cả loạt câu. Hãy nhớ hai câu:
requestslà lời khai báo với scheduler: "hãy xếp tôi vào node nào còn trống ít nhất chừng này". Scheduler chỉ nhìn con số này, nó không biết container thực sự dùng bao nhiêu.limitslà mệnh lệnh cho kubelet và cgroup: "được dùng tối đa chừng này, vượt là bị xử lý".
Hệ quả thực tế: một pod có thể Pending dù cả cluster còn rất nhiều CPU rảnh, chỉ vì không node đơn lẻ nào còn đủ chỗ theo requests.
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
CPU và memory hành xử rất khác nhau khi vượt limit
Đây là chỗ đề thi hay hỏi và cũng là chỗ dễ mất điểm nhất.
| Tài nguyên | Bản chất | Vượt limit thì sao? |
|---|---|---|
| CPU | Nén được (compressible) | Container bị throttle, chạy chậm lại, không bị kill |
| Memory | Không nén được | Container bị kill, reason OOMKilled, exit code 137, rồi restart theo restartPolicy |
Đơn vị cần nhớ: CPU 1 = 1000m, nên 250m là một phần tư core. Memory dùng Mi/Gi (bội số 1024), đừng viết M/G khi đề cho Mi.
==Khi memory vượt limit, container bị kill ngay chứ không bị làm chậm.==
Ba QoS class
Kubernetes tự gán QoS class cho mỗi pod dựa trên resources của các container. Bạn không đặt field này, nó là read-only ở status.qosClass.
flowchart TD
Start["Đọc YAML của Pod"] --> A{"Mọi container có cả requests và limits cho CPU và memory?"}
A -->|Không| B{"Có container nào có ít nhất một requests hoặc limits?"}
B -->|Có| BU["🟡<br/>Burstable"]
B -->|Không| BE["🔴<br/>BestEffort"]
A -->|Có| C{"requests bằng limits ở mọi container?"}
C -->|Có| G["🟢<br/>Guaranteed"]
C -->|Không| BU
| QoS class | Điều kiện chính xác | Vị trí khi evict |
|---|---|---|
| Guaranteed | Mọi container đặt cả requests và limits cho cả CPU và memory, và hai giá trị bằng nhau |
Bị evict cuối cùng |
| Burstable | Không đạt Guaranteed, nhưng có ít nhất một container đặt requests hoặc limits |
Bị evict ở giữa |
| BestEffort | Không container nào đặt requests hay limits |
Bị evict đầu tiên |
Mẹo nhớ: Guaranteed là all-or-nothing. Thiếu một field memory ở một container là rớt xuống Burstable ngay.
Vì sao pod bị Evicted
Khi node bị áp lực tài nguyên (MemoryPressure, DiskPressure), kubelet phải đuổi bớt pod. Thứ tự xử lý:
- Pod
BestEffort. - Pod
Burstableđang dùng vượtrequestscủa nó (vượt càng nhiều càng bị đuổi sớm). - Pod
Guaranteed.
Pod bị đuổi có status.phase: Failed, reason: Evicted, và sự kiện trên pod ghi The node was low on resource: memory.
Phân biệt cho chắc, vì đề rất thích gài hai khái niệm này:
OOMKilled: do limit của container bị vượt. Xảy ra ở cấp container.Evicted: do node hết tài nguyên. Xảy ra ở cấp pod, và eviction dựa trên mức dùng so vớirequests.
Vì sao pod Pending vì tài nguyên
Scheduler duyệt từng node, trừ dần requests của các pod đã chạy. Nếu không node nào còn đủ, pod nằm Pending với event:
0/3 nodes are available: 3 Insufficient cpu.
Ba cách sửa, xếp theo thứ tự nên thử trong phòng thi:
- Giảm
requestsxuống mức hợp lý. - Giảm số replica nếu đề cho phép.
- Thêm node hoặc tăng tài nguyên node (hiếm khi đề yêu cầu vì tốn thời gian).
Ví dụ có hướng dẫn
Ví dụ 1: Đọc YAML và gọi tên QoS class
1a. Không có resources:
containers:
- name: app
image: nginx
Không container nào đặt gì, nên đây là BestEffort.
1b. Có cả hai nhưng khác nhau:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
Đủ bốn field nhưng requests khác limits, nên là Burstable.
1c. Bốn field trùng khớp:
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 200m
memory: 256Mi
Đây là Guaranteed.
1d. Bẫy kinh điển, chỉ CPU bằng nhau:
resources:
requests:
cpu: 200m
limits:
cpu: 200m
memory: 256Mi
Thiếu requests.memory, nên không phải Guaranteed. Kết quả là Burstable. Trong phòng thi, gặp câu hỏi QoS thì luôn đếm đủ bốn field trước khi trả lời.
Ví dụ 2: Pod Pending vì requests quá cao
Đề bài kiểu: "Deployment web có 3 replicas, mỗi pod requests.cpu: 2, toàn bộ node chỉ còn 1 CPU trống. Sửa để pod chạy được."
Bước 1, xác nhận triệu chứng:
kubectl get pods
# web-7d9f-abcde 0/1 Pending 0 45s
kubectl describe pod web-7d9f-abcde | tail -n 5
# Events:
# Warning FailedScheduling default-scheduler 0/3 nodes are available: 3 Insufficient cpu.
Bước 2, sửa bằng lệnh imperative, không cần mở editor:
kubectl set resources deployment web --requests=cpu=200m
Bước 3, kiểm tra rollout và trạng thái:
kubectl rollout status deployment web
kubectl get pods -o wide
Lưu ý: kubectl set resources sẽ kích hoạt rolling update. Nếu đề yêu cầu không downtime thì đó lại là điều tốt.
Ví dụ 3: Container bị OOMKilled
kubectl describe pod api-0 | grep -A3 "Last State"
# Last State: Terminated
# Reason: OOMKilled
# Exit Code: 137
Nghĩa là container đã chạm limits.memory: 128Mi. Cách sửa:
kubectl set resources deployment api --limits=memory=512Mi --requests=memory=256Mi
Ghi nhớ: 137 = 128 + 9 = bị kill bởi SIGKILL. Gặp exit code này thì nghĩ ngay tới memory limit, đừng nghĩ tới CPU.
Luyện tập có hướng dẫn
Làm từng bài, tự viết lệnh ra giấy trước, rồi mở đáp án để so. Đây chính là nhịp độ bạn sẽ làm trong phòng thi.
Bài 1. Viết YAML một pod tên guaranteed-pod, image nginx, đạt đúng QoS class Guaranteed với CPU 250m và memory 64Mi.
Xem đáp án
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-pod
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
cpu: 250m
memory: 64Mi
limits:
cpu: 250m
memory: 64Mi
Kiểm tra: kubectl get pod guaranteed-pod -o jsonpath='{.status.qosClass}' phải trả về Guaranteed.
Bài 2. Cluster có 3 node, mỗi node còn trống 500m CPU. Bạn tạo Deployment 5 replicas với requests.cpu: 300m cho mỗi pod. Hỏi có bao nhiêu pod chạy được, bao nhiêu Pending, và vì sao?
Xem đáp án
Mỗi node chỉ chứa được 1 pod: pod thứ hai cần thêm 300m, tổng 600m > 500m trống.
Vậy 3 pod chạy được, 2 pod Pending. Event sẽ là 0/3 nodes are available: 3 Insufficient cpu. Cách sửa nhanh nhất: giảm requests.cpu xuống 150m hoặc giảm replicas xuống 3.
Bài 3. Một Deployment tên worker đang là BestEffort. Hãy đưa nó về Burstable bằng một lệnh kubectl, không dùng file YAML.
Xem đáp án
kubectl set resources deployment worker --requests=cpu=100m,memory=128Mi
Chỉ cần đặt requests, không cần limits, là đủ rời khỏi BestEffort. Nếu muốn chắc chắn, thêm --limits=cpu=200m,memory=256Mi, khi đó vẫn là Burstable vì requests khác limits.
Bài 4. Tạo nhanh một pod tên probe bằng lệnh imperative, image nginx, requests là 100m CPU và 128Mi, limits là 200m CPU và 256Mi. Sau đó in ra QoS class.
Xem đáp án
kubectl run probe --image=nginx \
--requests=cpu=100m,memory=128Mi \
--limits=cpu=200m,memory=256Mi
kubectl get pod probe -o jsonpath='{.status.qosClass}'
# Burstable
Mẹo thi: cặp flag --requests và --limits nhận chuỗi dạng cpu=...,memory=..., không có dấu cách quanh dấu =.
Bài 5. Node worker-2 báo MemoryPressure. Bạn thấy nhiều pod Evicted nhưng không pod nào OOMKilled. Hãy nêu hai lệnh để xác nhận nguyên nhân và một cách giảm rủi ro cho các pod quan trọng.
Xem đáp án
Hai lệnh xác nhận:
kubectl describe node worker-2 | grep -A5 "Conditions"
kubectl get pods -A --field-selector status.phase=Failed -o wide
Hoặc xem sự kiện của một pod cụ thể: kubectl describe pod <name> | grep -i evict, sẽ thấy The node was low on resource: memory.
Cách giảm rủi ro: đặt requests bằng limits cho cả CPU và memory để đưa các pod quan trọng lên Guaranteed, và đặt requests sát với nhu cầu thật để eviction không nhắm vào chúng trước.
Góc chuyên gia
Vài thứ mình ước mình biết trước lần thi đầu:
- Đếm đủ bốn field trước khi kết luận QoS. Câu hỏi QoS gần như luôn có một bẫy nhỏ: hoặc thiếu
requests.memory, hoặcrequestskháclimitsở đúng một container. limitskhông được nhỏ hơnrequests. API server từ chối thẳng với lỗimust be less than or equal to. Nếu bạn gặp lỗi này khikubectl apply, đừng đi tìm lỗi ở chỗ khác.requestscao "cho chắc" là cách tự tạo pod Pending. Trong lab thì không sao, trong đề thi thì mất điểm. Hãy đặtrequestsbằng mức app cần khi chạy bình thường, cònlimitsmới là mức trần.kubectl describe nodelà vũ khí chính. Nhìn mụcAllocated resourcesđể biết node còn bao nhiêu theorequests, nhanh hơn nhiều so với đoán:
kubectl describe node <node-name> | grep -A8 "Allocated resources"
LimitRangecó thể phá vỡ kế hoạch của bạn. Nếu namespace cóLimitRange, pod không đặtresourcesvẫn có thể được inject default và trở thành Burstable. Gặp trường hợp "YAML của tôi trống mà QoS không phải BestEffort", kiểm tra ngay:
kubectl get limitrange -n <namespace>
ResourceQuotakhácPending. Vượt quota thì pod bị từ chối ngay khi create, thông báoexceeded quota. PodPendinglà chuyện của scheduler, không phải quota. Đọc kỹ thông báo lỗi để không sửa sai hướng.- Một lệnh xem nhanh toàn namespace, rất hữu ích khi đề yêu cầu kiểm tra nhiều pod:
kubectl get pods -o custom-columns=NAME:.metadata.name,QOS:.status.qosClass,CPU_REQ:.spec.containers[*].resources.requests.cpu
- Exit code 137 chỉ nói "bị SIGKILL". Nó thường là OOMKilled, nhưng cũng có khi là
kubectl deletehoặc probe fail. Luôn đọc thêmReasontrongLast Statetrước khi kết luận.
[!WARNING] Đừng dành quá 3 phút cho một bài tài nguyên. Nếu sửa
requestsmà pod vẫn Pending, đọc lại event: có thể nguyên nhân thật lànodeSelector,taint, hoặc PVC chưa bind, chứ không phải CPU. Ghi lại bài đó và quay lại sau.
Cloze để tự kiểm tra
Scheduler chọn node dựa trên {{c1::requests}} của mọi container trong pod.
Khi container vượt CPU limit, nó bị {{c2::throttle}} chứ không bị kill.
Khi container vượt memory limit, nó bị kill với reason {{c3::OOMKilled}}.
Pod không đặt bất kỳ requests hay limits nào thuộc QoS class {{c4::BestEffort}}.
Pod có mọi container đặt requests bằng limits cho cả CPU và memory thuộc QoS class {{c5::Guaranteed}}.
Khi node bị áp lực tài nguyên, pod {{c6::BestEffort}} bị evict đầu tiên.
Flashcard ôn tập
[
{ "front": "requests dùng để làm gì?", "back": "Là con số scheduler dùng để chọn node còn đủ tài nguyên." },
{ "front": "limits dùng để làm gì?", "back": "Là mức trần kubelet và cgroup áp lên container lúc chạy." },
{ "front": "Vượt CPU limit thì chuyện gì xảy ra?", "back": "Container bị throttle, chạy chậm lại, không bị kill." },
{ "front": "Vượt memory limit thì chuyện gì xảy ra?", "back": "Container bị kill với reason OOMKilled, exit code 137, rồi restart." },
{ "front": "Ba QoS class của Kubernetes là gì?", "back": "Guaranteed, Burstable và BestEffort." },
{ "front": "Điều kiện để một pod đạt Guaranteed?", "back": "Mọi container đặt cả requests và limits cho cả CPU và memory, và requests bằng limits." },
{ "front": "Pod BestEffort được định nghĩa thế nào?", "back": "Không container nào đặt requests hay limits." },
{ "front": "Thứ tự evict khi node bị áp lực tài nguyên?", "back": "BestEffort trước, rồi Burstable vượt requests, Guaranteed sau cùng." },
{ "front": "Event 'Insufficient cpu' nghĩa là gì?", "back": "Không node nào còn đủ CPU theo requests, pod nằm Pending." },
{ "front": "Lệnh in ra QoS class của pod?", "back": "kubectl get pod <name> -o jsonpath='{.status.qosClass}'" },
{ "front": "1 CPU bằng bao nhiêu millicores?", "back": "1000m." },
{ "front": "Lệnh đổi requests và limits cho Deployment?", "back": "kubectl set resources deployment <name> --requests=... --limits=..." },
{ "front": "Xem node còn bao nhiêu tài nguyên theo requests ở đâu?", "back": "kubectl describe node <name>, mục Allocated resources." },
{ "front": "Khác nhau giữa OOMKilled và Evicted?", "back": "OOMKilled do vượt limit của container, Evicted do node hết tài nguyên." }
]
Tóm tắt và bước tiếp theo
Bốn điều cần mang ra khỏi bài này:
requestslà chuyện của scheduler,limitslà chuyện của runtime.- CPU vượt limit thì bị throttle, memory vượt limit thì bị
OOMKilled. - QoS class xét trên mọi container: Guaranteed cần đủ bốn field và bằng nhau, Burstable là có ít nhất một field, BestEffort là không có gì.
Pendingvì tài nguyên thì đọc eventInsufficient cpu/memoryrồi giảmrequests, đừng đoán mò.
Bài sau sẽ nối tiếp tự nhiên: khi pod đã đủ tài nguyên nhưng vẫn chưa rơi vào node bạn muốn, lúc đó mới dùng nodeSelector, affinity, và taint/toleration. Bạn sẽ thấy nhiều đề CKA trộn hai chủ đề này trong cùng một câu, nên nắm chắc bài hôm nay trước đã.