Scheduler กับ Resource Requests
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”Scheduler วาง Pod ลงบน Node ที่มี capacity ว่างพอจะรองรับ requests ของตัวเอง ส่วน limits คือเพดานที่จำกัดว่า Pod ใช้ resource ได้มากแค่ไหนตอนที่รันอยู่จริง
requests คือพื้นล่าง limits คือเพดานบน
หัวข้อที่มีชื่อว่า “requests คือพื้นล่าง limits คือเพดานบน”ทุก container ใน Pod ประกาศ resources.requests และ resources.limits สำหรับ CPU และ memory ได้ และตัวเลขสองตัวนี้ทำหน้าที่ต่างกันโดยสิ้นเชิง
requestsคือขั้นต่ำที่ container ต้องการแน่ ๆ scheduler จะนับ Node เป็นตัวเลือกก็ต่อเมื่อ Node นั้นมี capacity ที่ ยังไม่ถูกจอง พอจะครอบคลุม requests ของทุก container — ไม่สนใจ limits เลยตอนตัดสินใจว่า Pod ไปลงที่ไหนได้limitsคือเพดานแข็งที่ถูกบังคับใช้ตอน runtime หลังจาก Pod รันไปแล้ว CPU เป็น resource ที่บีบอัดได้ ดังนั้นการใช้เกิน limit ของ CPU จะถูก throttle ไม่ใช่ถูกฆ่า ส่วน memory บีบอัดไม่ได้ container ที่ใช้ memory เกิน limit จะโดน OOMKilled แล้ว restart
apiVersion: v1kind: Podmetadata: name: payments-workerspec: containers: - name: worker image: payments-worker:1.4.0 resources: requests: cpu: "500m" memory: "256Mi" limits: cpu: "500m" memory: "256Mi"# See which node a Pod landed on, and check the node's spare capacitykubectl get pod payments-worker -o jsonpath='{.spec.nodeName}{"\n"}'kubectl describe node <node-name> | grep -A5 "Allocated resources"เพราะ scheduler มองแค่ requests เท่านั้น Pod ที่ไม่ตั้ง requests เลยจึงมองไม่เห็นในสายตาของ capacity planning คือถูก schedule ไปที่ไหนก็ได้ และยังแย่ง resource จน Pod ข้าง ๆ ที่ตั้ง requests ไว้ถูกต้องอาจอดได้ด้วย
QoS class ตัดสินว่าใครโดน evict ก่อน
หัวข้อที่มีชื่อว่า “QoS class ตัดสินว่าใครโดน evict ก่อน”Kubernetes คำนวณ Quality of Service (QoS) class ให้ทุก Pod อัตโนมัติจาก requests และ limits ของตัวเอง — คุณไม่ต้องตั้งค่า field นี้เอง
- Guaranteed — ทุก container มี
requestsเท่ากับlimitsทั้ง CPU และ memory - Burstable — มีอย่างน้อยหนึ่ง container ตั้ง requests ไว้ แต่ค่าต่างจาก limits หรือมีแค่บาง resource ที่ตั้งทั้งคู่
- BestEffort — ไม่มี container ไหนตั้ง requests หรือ limits เลย
# QoS class is computed and reported on the Pod statuskubectl get pod payments-worker -o jsonpath='{.status.qosClass}{"\n"}'QoS class จะมีผลก็ต่อเมื่อ Node เริ่มขาด resource เมื่อ kubelet ต้องเรียก memory คืนภายใต้ pressure จะ evict Pod ระดับ BestEffort ก่อน ตามด้วย Burstable และจะแตะ Pod ระดับ Guaranteed ก็ต่อเมื่อไม่เหลือทางเลือกอื่นแล้วเท่านั้น นี่คือจุดที่การตั้ง requests และ limits อย่างระมัดระวังส่งผลตรง ๆ และเห็นชัดว่า workload ไหนของคุณจะรอดเมื่อมี noisy neighbor
Scheduler เลือก Node อย่างไร filter score bind
หัวข้อที่มีชื่อว่า “Scheduler เลือก Node อย่างไร filter score bind”สำหรับทุก Pod ที่ยังไม่ถูก schedule scheduler จะรันการตัดสินใจสองขั้นตอนก่อนจะฟันธง
- Filtering — ตัด Node ที่รัน Pod นี้ไม่ได้เด็ดขาดออกไปทั้งหมด ไม่ว่าจะเป็น CPU/memory ว่างไม่พอครอบคลุม requests, มี taint ที่ Pod ไม่ tolerate หรือมี affinity/anti-affinity rule ที่ตัดออก ที่เหลือคือชุด Node ที่ เป็นไปได้
- Scoring — จัดอันดับ Node ที่เป็นไปได้ด้วยชุด scoring plugin (กระจายข้าม zone, อัด density แบบ bin-pack และอื่น ๆ) แล้วเลือกตัวที่คะแนนสูงสุด
เมื่อ Node ตัวใดชนะ scheduler จะ bind Pod เข้ากับ Node นั้นโดยเขียน spec.nodeName แล้ว kubelet บน Node นั้นจะรับช่วงต่อและสั่ง start container
flowchart LR pod["Pod spec\n(requests + limits)"] --> filter["Filter:\nwhich Nodes CAN run it"] filter --> score["Score:\nrank the feasible Nodes"] score --> bind["Bind:\nPod assigned to winning Node"] bind --> node["Node"]