Priority và preemption

K
Kai··5 min read

Bài 34 dựng một pod không vừa node nào và thấy event preemption: ... Preemption is not helpful for scheduling. Lúc đó "không giúp được" vì mọi pod cùng mức ưu tiên — chẳng có ai để đá ra. Bài này làm cho preemption helpful: gán priority khác nhau cho pod, rồi khi node hết chỗ, pod ưu tiên cao được quyền trục xuất (preempt) pod ưu tiên thấp để giành chỗ. Đây là cơ chế đảm bảo workload quan trọng (control plane, dịch vụ tier-1) luôn có chỗ chạy ngay cả khi cluster đầy pod rác.

PriorityClass: gán mức ưu tiên

Priority không phải con số gõ thẳng vào pod, mà qua một object cụm tên PriorityClass. Tài liệu: "A PriorityClass is a non-namespaced object that defines mapping from a priority class name to an integer value ... The higher the value, the higher the priority." Dải giá trị: "from -2147483648 to 1000000000 inclusive. Larger numbers are reserved for built-in PriorityClasses" — và cụm sẵn có hai lớp system-cluster-critical, system-node-critical cho pod hệ thống (chính cái CoreDNS của ta đặt ở Bài 15). Tạo hai lớp:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata: {name: low-prio}
value: 100
description: "workload thường, có thể bị trục xuất"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata: {name: high-prio}
value: 1000000
description: "workload quan trọng, được ưu tiên"

Pod tham chiếu qua priorityClassName, và admission controller điền giá trị số vào spec.priority:

kubectl get pod low-1 -o jsonpath='{.spec.priority}'
# 100

Priority có hai tác dụng. Thứ nhất, sắp hàng đợi: "the scheduler orders pending Pods by their priority and a pending Pod is placed ahead of other pending Pods with lower priority in the scheduling queue." Thứ hai — và mạnh hơn — là preemption.

Lấp đầy cluster bằng pod ưu tiên thấp

Mỗi worker có Allocatable cpu = 2 (Bài 32), CoreDNS chiếm ~100m, còn ~1900m. Tạo hai pod ưu tiên thấp, mỗi pod xin 1500m — mỗi node chứa đúng một:

# low-1, low-2: priorityClassName: low-prio, requests cpu 1500m
kubectl get pods -l tier=low -o wide
low-1   Running   worker-1
low-2   Running   worker-0

Giờ mỗi node còn ~400m trống — không đủ cho một pod 1500m nữa. Cluster coi như "đầy" với loại pod lớn. Đây là tình huống Bài 34: pod 1500m mới sẽ Pending... trừ khi nó ưu tiên cao hơn.

Preemption: pod ưu tiên cao đá pod thấp ra

Thả một pod high-prio xin 1500m. Tài liệu mô tả logic: "If no Node is found that satisfies all the specified requirements of the Pod, preemption logic is triggered ... Preemption logic tries to find a Node where removal of one or more Pods with lower priority than P would enable P to be scheduled. If such a Node is found, one or more lower priority Pods get evicted."

# important: priorityClassName: high-prio, requests cpu 1500m

Chụp ngay sau khi thả:

kubectl get pods -l 'tier in (low,high)' -o wide
kubectl get pod important -o jsonpath='nominatedNodeName={.status.nominatedNodeName}'
important   Pending       <none>
low-1       Running       worker-1
low-2       Terminating   worker-0      # <- nạn nhân bị trục xuất

nominatedNodeName=worker-0

Preemption đã kích hoạt: scheduler chọn worker-0, đặt nominatedNodeName=worker-0 cho important, và trục xuất low-2 đang chạy ở đó. Tài liệu giải thích nominatedNodeName: "When Pod P preempts one or more Pods on Node N, nominatedNodeName field of Pod P's status is set to the name of Node N." — một "lời hứa" tạm về node, dù "Pod P is not necessarily scheduled to the nominated Node" nếu có node khác trống ra trước. Nạn nhân được tắt êm, không bị giết thô: "the victims get their graceful termination period." Bằng chứng nằm ở event của low-2:

kubectl get events --field-selector involvedObject.name=low-2 | grep Preempted
Normal  Preempted  Preempted by pod 7d4ecd4f-... on node worker-0

Sau khi low-2 thoát hết grace period, important chiếm chỗ trống:

kubectl get pods -l 'tier in (low,high)' -o wide
kubectl get pod important -o jsonpath='nodeName={.spec.nodeName} priority={.spec.priority}'
important   Running   worker-0
low-1       Running   worker-1       # <- không bị đụng

nodeName=worker-0 priority=1000000

important (priority 1000000) giờ Running trên worker-0; low-1 (priority 100, ở worker-1) không bị đụng vì chỉ cần trục xuất một pool là đủ chỗ. Đó là bước PostFilter của Bài 34, nhưng lần này preemption helpful: có pod ưu tiên thấp hơn để đá ra. So sánh hai event nói lên tất cả: Bài 34 (cùng priority) → Preemption is not helpful; ở đây (có chênh lệch priority) → Preempted by pod ... on node worker-0.

Khi không muốn trục xuất: preemptionPolicy Never

Đôi khi ta muốn pod được ưu tiên xếp trước trong hàng đợi nhưng không đá ai ra — ví dụ một job khoa học muốn chen lên đầu hàng nhưng không nỡ hủy công việc đang chạy. Tài liệu: "Pods with preemptionPolicy: Never will be placed in the scheduling queue ahead of lower-priority pods, but they cannot preempt other pods." (mặc định là PreemptLowerPriority). Khai trên PriorityClass:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata: {name: high-priority-nonpreempting}
value: 1000000
preemptionPolicy: Never        # ưu tiên xếp trước, nhưng KHÔNG trục xuất ai

Pod loại này nếu không có chỗ thì chỉ chờ (Pending), không đá pod nào — tách bạch hai khía cạnh của priority: "được xét trước" và "được giành chỗ".

🧹 Dọn dẹp

kubectl delete pod important low-1 --now
kubectl delete priorityclass low-prio high-prio

low-2 đã bị preemption xóa. Xóa nốt pod còn lại và hai PriorityClass (object cụm, không thuộc namespace). Cụm về lại hai pod CoreDNS. Manifest ở github.com/nghiadaulau/kubernetes-from-scratch, thư mục 37-priority-preemption.

Tổng kết

PriorityClass gán mức ưu tiên (số nguyên, cao hơn = ưu tiên hơn; built-in system-cluster-critical/system-node-critical cho pod hệ thống), pod tham chiếu qua priorityClassName và admission điền spec.priority. Priority làm hai việc: xếp pod ưu tiên cao trước trong hàng đợi, và cho phép preemption — khi pod ưu tiên cao không có chỗ, scheduler trục xuất pod ưu tiên thấp để giành chỗ (ta thấy important đặt nominatedNodeName=worker-0 rồi đá low-2 — event Preempted — và chiếm chỗ, trong khi low-1 ưu tiên thấp ở node khác vẫn yên). Nạn nhân được graceful termination. preemptionPolicy: Never tách "xếp trước" khỏi "giành chỗ" — ưu tiên trong hàng đợi nhưng không đá ai. Đây là PostFilter của Bài 34 khi chênh lệch priority để khai thác.

Bài 38 khép Part VII với mặt còn lại: node-pressure eviction — khi node cạn tài nguyên thật (không phải lúc xếp lịch), kubelet chủ động đuổi pod theo ngưỡng và thứ tự QoS (Bài 22), khác hẳn preemption (do scheduler, vì priority) và OOM kill (do kernel, vì vượt limit).

Related Posts