Phần 53 nêu ba lớp QoS và thứ tự bị đuổi. Bài này đo con số kernel thật sự dùng để quyết định thứ tự đó.

oom_score_adj theo lớp QoS và công thức cho Burstable

Con số thật: oom_score_adj

Đọc /proc/1/oom_score_adj bên trong từng container:

Pod Cấu hình oom_score_adj
g-guar requests = limits = 300Mi -997
g-burst requests = 100Mi 994
g-best không khai gì 1000

Kernel tính điểm cho mỗi tiến trình khi hết bộ nhớ, và oom_score_adj cộng thẳng vào điểm đó. Điểm cao nhất chết trước.

Guaranteed mang số âm — gần như không bao giờ được chọn. BestEffort mang 1000, giá trị cao nhất có thể.

Đây không phải quy ước mềm của Kubernetes. Nó là con số kernel Linux thật sự dùng, và Kubernetes chỉ đặt nó khi tạo container.

Burstable: xin càng nhiều, càng khó bị giết

requests = 50Mi    ->  oom_score_adj = 997
requests = 100Mi   ->  oom_score_adj = 994
requests = 8Gi     ->  oom_score_adj = 488

Công thức:

oom_score_adj = 1000 - (1000 × requests.memory / RAM node)

Kiểm chứng trên node 16.747.192.320 byte:

1000 - 1000 ×    52.428.800 / 16.747.192.320  =  997
1000 - 1000 ×   104.857.600 / 16.747.192.320  =  994
1000 - 1000 × 8.589.934.592 / 16.747.192.320  =  488

Cả ba khớp chính xác con số đo được.

Nghĩa là: requests.memory không chỉ dùng để bộ lập lịch chọn node (phần 47). Nó trực tiếp hạ điểm chết của pod trong kernel — pod xin nhiều được bảo vệ nhiều hơn, tỉ lệ với phần nó xin trên tổng RAM node.

Điều này cũng giải thích tại sao Burstable xin rất nhiều (8Gi trên node 16 GB) có điểm 488 — thấp hơn cả một số cấu hình khác, dù nó không phải Guaranteed.

Ba lớp, và cách rơi vào từng lớp

Guaranteedmọi container trong pod khai cả requestslimits cho cả CPU lẫn bộ nhớ, và requests = limits.

Điều kiện chặt hơn nhiều người nghĩ. Chỉ cần một container trong pod thiếu limits.cpu là cả pod tụt xuống Burstable.

Burstable — có khai ít nhất một requests hoặc limits, nhưng không đủ điều kiện Guaranteed.

BestEffort — không khai gì cả.

kubectl get pod <ten> -o jsonpath='{.status.qosClass}'

Hệ quả thực dụng

Pod quan trọng  ->  requests = limits  ->  Guaranteed  ->  -997
Pod phụ trợ     ->  requests thấp      ->  Burstable   ->  gần 1000
Không khai gì   ->  BestEffort         ->  1000, chết đầu tiên

Với dịch vụ then chốt, đặt requests = limits là cách rẻ nhất để nó gần như không bao giờ bị chọn làm nạn nhân. Cái giá: bạn mất khả năng dùng vượt — limits bằng requests nghĩa là không có dư địa.

Với dịch vụ phụ — công việc nền, xử lý theo lô — BestEffort là hợp lý. Chúng nên là thứ chết trước.

Cái không nên có là BestEffort do vô tình. Phần 53 đã nêu: đó là pod không ai khai requests, thường vì manifest được viết vội, và nó nhận vé chết trước mà không ai chọn cho nó.

LimitRange (phần 53) chặn chuyện này ở mức namespace bằng cách gán giá trị mặc định.

Hai cơ chế dùng chung một thứ hạng

Thứ hạng QoS được dùng ở hai chỗ khác nhau, và phân biệt chúng quan trọng:

Kernel OOM killer — khi cgroup của node hết bộ nhớ. Dùng oom_score_adj như trên. Nhanh, không có cảnh báo, và pod bị giết tại chỗ (phần 54).

kubelet eviction — khi node chạm ngưỡng evictionHard. kubelet chọn pod theo lớp QoS và mức dùng vượt requests, rồi đuổi pod đi bằng cách xoá nó. Chậm hơn, có event Evicted, và pod được tạo lại ở node khác.

kubelet cố gắng đuổi trước khi kernel phải giết — đó là điểm khác biệt giữa một pod chuyển nhà và một pod bị bắn.

Cùng thứ hạng, hai đường thoát khác nhau.

Đo mà không cần làm cạn node

Ép một node 16 GB cạn bộ nhớ để quan sát thứ tự đuổi là tốn kém và khó lặp lại. Đọc oom_score_adj cho cùng thông tin, trong một giây, và lặp lại được:

for p in $(kubectl get pods -o jsonpath='{.items[*].metadata.name}'); do
  echo "$p  $(kubectl get pod $p -o jsonpath='{.status.qosClass}')  \
$(kubectl exec $p -- cat /proc/1/oom_score_adj 2>/dev/null)"
done

Đây là một mẫu chung đáng nhớ khi đo: nếu hiện tượng khó dựng, hãy tìm con số quyết định hiện tượng đó. Kết quả chắc chắn hơn và không phải phá hỏng môi trường.

Thử ba mươi giây

Xếp hạng pod của bạn theo mức độ dễ bị giết:

kubectl get pods -A -o custom-columns=\
NS:.metadata.namespace,TEN:.metadata.name,QOS:.status.qosClass --no-headers \
  | sort -k3

Nhóm BestEffort ở đầu danh sách là nhóm chết trước. Nếu có dịch vụ quan trọng nằm trong đó, đó là một dòng resources bị quên — và nó chỉ lộ ra vào lần node cạn RAM tiếp theo.

Phần sau đo Service và kube-proxy: một ClusterIP mà không tiến trình nào lắng nghe.