Prioritas menyatakan tingkat kepentingan sebuah Pod dibandingkan Pod lain saat sumber daya kluster tidak mencukupi. Tingkat tersebut ditetapkan melalui objek PriorityClass dan dirujuk Pod melalui kunci priorityClassName. Apabila Pod berprioritas tinggi tidak muat pada Node mana pun, scheduler menjalankan preemption, yaitu pengusiran Pod berprioritas lebih rendah untuk membuka ruang. Topik ini membahas penulisan PriorityClass, cara preemption memilih korban, serta batas mekanisme tersebut, dengan simulasi pemilihan korban dalam Python.
PriorityClass dan nilai prioritas#
PriorityClass adalah objek di tingkat kluster yang menetapkan angka prioritas pada kunci value. Nilai yang lebih besar berarti lebih penting. Pod merujuk kelas tersebut melalui kunci priorityClassName di spesifikasinya, sedangkan Pod tanpa rujukan memperoleh nilai nol atau nilai kelas yang ditandai globalDefault: true. Kubernetes membawa dua kelas bawaan untuk komponen sistem, yaitu system-cluster-critical dengan nilai 2000000000 dan system-Node-critical dengan nilai 2000001000, sehingga nilai kelas buatan pengguna sebaiknya diletakkan jauh di bawah angka tersebut.
Berkas berikut mendefinisikan dua PriorityClass untuk ranah analitik, yaitu prio-analitik dengan nilai 100 dan prio-laporan dengan nilai 500, kemudian menerapkannya pada Pod laporan-1. Kunci preemptionPolicy pada keduanya bernilai PreemptLowerPriority, yang berarti Pod berhak mengusiri Pod berprioritas lebih rendah. Nilai lainnya adalah Never, yang berarti Pod berprioritas tinggi hanya menunggu kekosongan kapasitas tanpa mengusiri Pod lain.
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: prio-analitik
value: 100
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: "Kelas untuk pekerjaan analitik yang boleh menunggu."
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: prio-laporan
value: 500
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: "Kelas untuk laporan terjadwal yang wajib selesai tepat waktu."
---
apiVersion: v1
kind: Pod
metadata:
name: laporan-1
spec:
priorityClassName: prio-laporan
containers:
- name: laporan
image: busybox:1.36
command: ["sleep", "3600"]
resources:
requests:
cpu: 2000m
memory: 2048Mi
limits:
cpu: 2000m
memory: 2048MiKelas prioritas umumnya diterapkan melalui template Pod pada workload controller, misalnya Deployment atau DaemonSet, sehingga seluruh Pod yang dibuatnya mewarisi prioritas yang sama. Nilai request pada template menentukan seberapa besar ruang yang harus dibuka saat preemption terjadi. Konsistensi rujukan pada satu lingkup beban kerja membuat keputusan preemption mudah dinilai saat kapasitas menipis.
Cara preemption memilih korban#
Preemption berjalan apabila Pod Pending berprioritas tinggi tidak muat pada Node mana pun. Scheduler mencari Node yang dapat memuat Pod tersebut setelah sejumlah Pod berprioritas lebih rendah diusiri, lalu menandai Pod yang harus diusiri sebagai korban. Kandidat korban hanya diambil dari Pod berprioritas lebih rendah, diawali Pod dengan prioritas terendah, sehingga jumlah korban ditekan serendah mungkin.
Sebelum preemption dijalankan, scheduler terlebih dahulu mengulang penilaian seluruh Node agar Pod yang tertahan benar-benar tidak dapat ditempatkan. Penilaian mencakup kapasitas, kendali penempatan, dan sebab-sebab lain yang membuat Pod menunggu. Dengan urutan tersebut, preemption menjadi langkah terakhir setelah seluruh opsi penempatan biasa habis.
Korban menjalani penghentian teratur beserta masa tenggangnya, dan Pod preemptor ditempatkan setelah korban benar-benar berhenti. Controller pemilik korban, misalnya Deployment, kemudian membuat Pod pengganti yang dicoba ditempatkan di Node lain, sehingga preemption memindahkan beban, bukan menghapusnya. Peristiwa semacam ini tercatat pada kluster dengan alasan Preempted beserta pesan yang menyebut Pod preemptor dan Node sasaran, sehingga dapat ditelusuri lewat kubectl.
# tidak-dijalankan
# menampilkan peristiwa preemption pada kluster
kubectl get events --field-selector reason=Preempted
# menampilkan PriorityClass yang tersedia
kubectl get priorityclassLAST SEEN TYPE REASON OBJECT MESSAGE
2m Normal Preempted pod/batch-a Preempted by pod "laporan-1" on node "node-a"
NAME VALUE GLOBAL-DEFAULT AGE PREEMPTIONPOLICY
prio-analitik 100 false 3d PreemptLowerPriority
prio-laporan 500 false 3d PreemptLowerPrioritySimulasi pemilihan korban#
Urutan pemilihan korban dapat ditiru dengan aritmetika sederhana. Skrip berikut menyimpan satu Node dengan tiga Pod terpasang, kemudian menilai empat Pod kandidat berprioritas 500 secara berurutan. Setiap skenario dinilai terhadap keadaan Node yang sama, dan kandidat korban diurutkan dari prioritas terendah. Nilai sumber daya dinyatakan dalam milicore dan Mi agar aritmetikanya mudah diikuti.
"""Simulasi preemption pod berprioritas tinggi pada satu node."""
# Kapasitas dinyatakan dalam milicore CPU dan Mi memori agar mudah dihitung.
node = {"nama": "node-a", "cpu": 4000, "mem": 8192}
terpasang = [
{"nama": "batch-a", "prio": 100, "cpu": 1500, "mem": 2048},
{"nama": "batch-b", "prio": 100, "cpu": 1000, "mem": 1024},
{"nama": "monitor-1", "prio": 1000, "cpu": 1000, "mem": 512},
]
def muat(pods):
"""Jumlah request seluruh pod pada daftar."""
return sum(p["cpu"] for p in pods), sum(p["mem"] for p in pods)
def muat_bersama(pods, pod_baru):
"""Benar bila request pod_baru muat di samping pod yang dipertahankan."""
cpu, mem = muat(pods)
return (cpu + pod_baru["cpu"] <= node["cpu"]
and mem + pod_baru["mem"] <= node["mem"])
def preempt(terpasang, pod_baru):
"""Memilih korban eviksi berprioritas terendah lebih dahulu hingga muat."""
kandidat = sorted((p for p in terpasang if p["prio"] < pod_baru["prio"]),
key=lambda p: (p["prio"], p["nama"]))
korban = []
dipertahankan = list(terpasang)
while not muat_bersama(dipertahankan, pod_baru):
if not kandidat:
return None
terpilih = kandidat.pop(0)
korban.append(terpilih)
dipertahankan.remove(terpilih)
return korban
cpu, mem = muat(terpasang)
print(f"Node {node['nama']}: allocatable {node['cpu']}m CPU dan {node['mem']} Mi memori")
print(f"Terpasang: {cpu}m CPU dan {mem} Mi memori ({len(terpasang)} pod)")
print()
# Setiap skenario dinilai terhadap keadaan node yang sama.
kandidat = [
{"nama": "cache-1", "prio": 500, "cpu": 500, "mem": 256},
{"nama": "laporan-1", "prio": 500, "cpu": 2000, "mem": 2048},
{"nama": "analitik-1", "prio": 500, "cpu": 3000, "mem": 2048},
{"nama": "backup-1", "prio": 500, "cpu": 1000, "mem": 8192},
]
for k in kandidat:
print(f"Kandidat {k['nama']} (prioritas {k['prio']}, "
f"request {k['cpu']}m CPU dan {k['mem']} Mi):")
if muat_bersama(terpasang, k):
print(" MUAT TANPA PREEMPTION, langsung ditempatkan")
print()
continue
korban = preempt(terpasang, k)
if korban is None:
print(" PREEMPTION GAGAL, pod berprioritas lebih rendah tidak cukup")
print()
continue
nama = ", ".join(p["nama"] for p in korban)
print(f" DITEMPATKAN setelah eviksi: {nama}")
print()Node node-a: allocatable 4000m CPU dan 8192 Mi memori
Terpasang: 3500m CPU dan 3584 Mi memori (3 pod)
Kandidat cache-1 (prioritas 500, request 500m CPU dan 256 Mi):
MUAT TANPA PREEMPTION, langsung ditempatkan
Kandidat laporan-1 (prioritas 500, request 2000m CPU dan 2048 Mi):
DITEMPATKAN setelah eviksi: batch-a
Kandidat analitik-1 (prioritas 500, request 3000m CPU dan 2048 Mi):
DITEMPATKAN setelah eviksi: batch-a, batch-b
Kandidat backup-1 (prioritas 500, request 1000m CPU dan 8192 Mi):
PREEMPTION GAGAL, pod berprioritas lebih rendah tidak cukupKeluaran tersebut menegaskan tiga perilaku. cache-1 muat langsung karena sisa kapasitas cukup, sehingga preemption tidak diperlukan. laporan-1 dan analitik-1 ditempatkan setelah korban diusiri, diawali batch-a sebagai Pod berprioritas terendah, sedangkan monitor-1 berprioritas 1000 tidak pernah tersentuh. backup-1 gagal karena memori yang dapat dibebaskan lebih kecil daripada request backup-1, sehingga Pod tersebut tetap Pending.
Urutan di antara Pod berprioritas sama pada simulasi ini diatur berdasarkan nama agar hasilnya deterministik. Scheduler yang sesungguhnya menimbang faktor lain pada prioritas yang sama, seperti jumlah sumber daya yang dapat dibebaskan, untuk memperkecil gangguan. Perbedaan urutan itu tidak mengubah aturan utamanya, yaitu Pod berprioritas lebih tinggi selalu dipertahankan.
Penggunaan prioritas yang terkendali#
Nilai prioritas hanya bermakna sebagai perbandingan relatif, sehingga kuncinya adalah menjaga sebagian kecil Pod berprioritas sangat tinggi. Apabila hampir semua Pod menuntut prioritas tinggi, preemption saling berdesakan dan jaminan yang dijanjikan tidak terlindungi. Praktik yang lazim adalah memberi nilai tinggi bagi layanan yang gangguannya paling terasa bagi pengguna, nilai sedang untuk laporan terjadwal, dan nilai rendah untuk pekerjaan batch yang boleh menunggu.
Preemption juga memiliki batas yang perlu diantisipasi. Masa tenggang korban menunda penempatan Pod preemptor, sehingga Pod berprioritas tinggi dapat menunggu beberapa saat sebelum berjalan. Scheduler menghormati PodDisruptionBudget sebaik mungkin, sehingga pelanggaran kecil masih mungkin terjadi apabila tidak ada kandidat korban lain. Pod korban yang dibuat ulang oleh controller pemiliknya juga bersaing kembali untuk kapasitas, sehingga preemption bukan pengganti perencanaan kapasitas.
Ringkasan#
- PriorityClass menetapkan angka prioritas di tingkat kluster, dan Pod merujuknya melalui kunci
priorityClassName. - Preemption berjalan apabila Pod berprioritas tinggi tidak muat, dengan korban dipilih dari Pod berprioritas lebih rendah diawali yang terendah.
- Kunci
preemptionPolicybernilai Never membuat Pod hanya menunggu kapasitas kosong tanpa mengusiri Pod lain. - Korban diusiri secara teratur dengan masa tenggang, dan controller pemiliknya dapat membuat Pod pengganti di Node lain.
Referensi#
- Pod Priority and Preemption — Kubernetes Docs — Objek PriorityClass, kunci priorityClassName, dan algoritma pemilihan korban preemption.
- Scheduling, Preemption and Eviction — Kubernetes Docs — Peta konsep penjadwalan, preemption, dan eviction di kluster Kubernetes.