Request dan limit adalah dua batas sumber daya yang dideklarasikan pada setiap container di dalam Pod. Request menyatakan jumlah CPU dan memori yang dijamin bagi container, sedangkan limit menyatakan batas tertinggi yang boleh dikonsumsi saat container berjalan. Topik ini membahas penulisan kedua batas tersebut, satuan yang dipahami Kubernetes, peran request dalam keputusan penjadwalan, serta akibat yang muncul ketika konsumsi melebihi limit. Perhitungan satuan dan uji kapasitas Node disimulasikan dengan Python sehingga setiap hasil dapat dihitung ulang tanpa kluster.
Deklarasi request dan limit pada manifest#
Request dan limit ditulis pada kunci resources di dalam spesifikasi setiap container, dengan subkunci requests dan limits untuk CPU serta memori. Nilai CPU dinyatakan dalam core, sedangkan nilai memori dinyatakan dalam byte beserta akhiran satuan. Kedua batas bersifat opsional, tetapi beban kerja nyata hampir selalu menetapkannya karena kedua nilai menentukan kelas Quality of Service (QoS) Pod dan mutu penempatannya. API server menolak manifest apabila nilai request pada satu sumber daya lebih besar daripada limitnya.
Manifest berikut mendeklarasikan Pod web-1 dengan request 250m CPU dan 128Mi memori, serta limit 500m CPU dan 256Mi memori. Pola ini lazim untuk aplikasi web karena request yang lebih rendah membuat Pod mudah ditempatkan, sedangkan limit yang lebih tinggi memberi ruang untuk menghadapi lonjakan beban. Nilai pada kedua kunci bersifat per container, sehingga total kebutuhan Pod adalah jumlah seluruh container di dalamnya. Kedua kunci dituliskan untuk CPU dan memori sekaligus agar perilaku Pod mudah dinalar saat kapasitas dinilai maupun saat container berjalan.
apiVersion: v1
kind: Pod
metadata:
name: web-1
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
resources:
requests:
cpu: 250m
memory: 128Mi
limits:
cpu: 500m
memory: 256MiSatuan CPU dan memori#
Kubernetes menggunakan dua sistem satuan yang berbeda untuk CPU dan memori. Satu core CPU setara 1000 milicore, ditulis 1000m, sehingga nilai 250m berarti seperempat core dan nilai 1 berarti satu core utuh. Nilai memori dapat ditulis dengan akhiran biner Ki, Mi, dan Gi yang berpangkat 1024, atau akhiran desimal K, M, dan G yang berpangkat 1000. Karena pangkatnya berbeda, 1Gi sama dengan 1073741824 byte, sedangkan 1G hanya sama dengan 1000000000 byte.
Skrip Python berikut mengonversi beberapa nilai CPU dan memori ke satuan dasarnya. Skrip yang sama menguji penempatan dua Pod kandidat pada Node dengan kapasitas 2 CPU dan 4 Gi memori. Uji dilakukan dengan menjumlahkan request Pod terpasang ditambah request kandidat, lalu membandingkannya dengan kapasitas Node.
"""Simulasi konversi satuan sumber daya dan pemeriksaan kapasitas node."""
# --- konversi satuan CPU ---
# CPU dinyatakan dalam core, akhiran m berarti milicore dengan 1000m = 1 core.
cpu_contoh = ["100m", "250m", "500m", "1", "1500m"]
print("Konversi CPU:")
for nilai in cpu_contoh:
if nilai.endswith("m"):
core = int(nilai[:-1]) / 1000
else:
core = float(nilai)
print(f" {nilai:>6} = {core} core")
# --- konversi satuan memori ---
# Akhiran biner (Ki, Mi, Gi) berpangkat 1024, akhiran desimal (K, M, G) berpangkat 1000.
FAKTOR = {"Ki": 1024, "Mi": 1024**2, "Gi": 1024**3,
"K": 1000, "M": 1000**2, "G": 1000**3}
mem_contoh = ["128Mi", "256Mi", "1Gi", "500M", "1G"]
print("Konversi memori:")
for nilai in mem_contoh:
for akhiran, faktor in FAKTOR.items():
if nilai.endswith(akhiran):
byte = int(nilai[:-len(akhiran)]) * faktor
break
print(f" {nilai:>6} = {byte} byte")
print()
print("Perbandingan akhiran biner dan desimal:")
print(f" 1Gi = {1024**3} byte")
print(f" 1G = {1000**3} byte")
print(f" Selisih = {1024**3 - 1000**3} byte")
# --- pemeriksaan kapasitas node ---
# Scheduler menempatkan pod hanya jika jumlah request pod terpasang ditambah
# request pod kandidat tidak melebihi allocatable node.
node = {"nama": "node-a", "cpu": 2, "mem": 4 * FAKTOR["Gi"]}
terpasang = [
{"nama": "api-1", "cpu": 0.25, "mem": 256 * FAKTOR["Mi"]},
{"nama": "api-2", "cpu": 0.25, "mem": 256 * FAKTOR["Mi"]},
{"nama": "batch", "cpu": 1.0, "mem": 2 * FAKTOR["Gi"]},
]
def jumlah(pods, kunci):
return sum(p[kunci] for p in pods)
cpu_terpakai = jumlah(terpasang, "cpu")
mem_terpakai = jumlah(terpasang, "mem")
print()
print(f"Node {node['nama']}: allocatable {node['cpu']} CPU, "
f"{node['mem']} byte ({node['mem'] / FAKTOR['Gi']} Gi)")
print(f"Terpasang: {cpu_terpakai} CPU, {mem_terpakai // FAKTOR['Mi']} Mi dari {len(terpasang)} pod")
kandidat = [
{"nama": "web-1", "cpu": 0.5, "mem": 1 * FAKTOR["Gi"]},
{"nama": "web-2", "cpu": 0.5, "mem": 1 * FAKTOR["Gi"]},
]
for k in kandidat:
cpu_baru = cpu_terpakai + k["cpu"]
mem_baru = mem_terpakai + k["mem"]
cukup_cpu = cpu_baru <= node["cpu"]
cukup_mem = mem_baru <= node["mem"]
if cukup_cpu and cukup_mem:
print(f"Kandidat {k['nama']}: LOLOS "
f"(request {k['cpu']} CPU dan {k['mem'] // FAKTOR['Mi']} Mi termuat)")
terpasang.append(k)
cpu_terpakai = cpu_baru
mem_terpakai = mem_baru
else:
alasan = []
if not cukup_cpu:
alasan.append(f"CPU {cpu_baru} > allocatable {node['cpu']}")
if not cukup_mem:
alasan.append("memori tidak cukup")
print(f"Kandidat {k['nama']}: PENDING ({', '.join(alasan)})")Konversi CPU:
100m = 0.1 core
250m = 0.25 core
500m = 0.5 core
1 = 1.0 core
1500m = 1.5 core
Konversi memori:
128Mi = 134217728 byte
256Mi = 268435456 byte
1Gi = 1073741824 byte
500M = 500000000 byte
1G = 1000000000 byte
Perbandingan akhiran biner dan desimal:
1Gi = 1073741824 byte
1G = 1000000000 byte
Selisih = 73741824 byte
Node node-a: allocatable 2 CPU, 4294967296 byte (4.0 Gi)
Terpasang: 1.5 CPU, 2560 Mi dari 3 pod
Kandidat web-1: LOLOS (request 0.5 CPU dan 1024 Mi termuat)
Kandidat web-2: PENDING (CPU 2.5 > allocatable 2, memori tidak cukup)Keluaran tersebut memperlihatkan dua hal. Konversi satuan menegaskan bahwa 100m hanya bernilai sepersepuluh core dan bahwa 128Mi sama dengan 134217728 byte, sehingga nilai yang tampak mirip dapat berbeda jauh setelah dikonversi. Uji kapasitas meloloskan web-1 karena jumlah request menjadi 2.0 CPU dan 3584 Mi, sedangkan web-2 tertolak karena jumlah request CPU mencapai 2.5 dan memori 4608 Mi, keduanya melampaui kapasitas Node.
Request sebagai dasar keputusan penjadwalan#
scheduler adalah komponen control plane yang memilih Node bagi setiap Pod baru. Node yang dinilai harus memiliki sisa kapasitas yang mencukupi. Scheduler menjumlahkan request seluruh Pod yang sudah ditempatkan pada Node, menambahkan request Pod kandidat, kemudian membandingkan hasilnya dengan nilai allocatable Node. allocatable adalah kapasitas Node dikurangi kebutuhan komponen sistem seperti kubelet, runtime container, dan agen pemantauan. Limit tidak ikut dinilai pada tahap ini karena limit adalah batas runtime, bukan jaminan kapasitas.
Apabila tidak ada Node yang memenuhi, Pod tetap berstatus Pending dan penempatan dicoba kembali setiap kali keadaan kluster berubah. Keadaan tersebut sering membingungkan karena Node yang terlihat kosong dapat menolak Pod, padahal sisa kapasitas requestnya memang tidak cukup. Peristiwa FailedScheduling pada Pod biasanya menyebut sumber daya yang kurang, sehingga penelusurannya dimulai dari perbandingan request dengan allocatable. Diagram berikut merangkum perbedaan peran kedua batas tersebut pada CPU dan memori.
Perilaku limit saat runtime#
Limit ditegakkan kernel melalui cgroup, yaitu mekanisme pembatasan sumber daya proses pada Linux, sejak container mulai berjalan. Akibat konsumsi di atas limit berbeda antara CPU dan memori karena sifat kedua sumber daya itu berbeda. CPU bersifat dapat dikompresi, sehingga konsumsi di atas limit hanya membuat proses menunggu giliran waktu proses dan aplikasi terasa lambat. Pembatasan semacam ini dikenal sebagai throttle.
| Sumber daya | Sifat | Konsumsi di atas limit |
|---|---|---|
| CPU | dapat dikompresi | dibatasi (throttle), container tetap berjalan |
| Memori | tidak dapat dikompresi | proses diakhiri dengan status OOMKilled |
Memori bersifat kebalikan dari CPU karena tidak dapat dikompresi. Apabila konsumsi melebihi limit, kernel mengakhiri proses di dalam container dan statusnya menjadi OOMKilled, yaitu keadaan ketika proses diakhiri oleh kernel karena memori yang tersedia habis. Container yang diakhiri kemudian dijalankan ulang menurut kunci restartPolicy dengan nilai bawaan Always. Pod yang terus-menerus melampaui limit memori berpindah dari status OOMKilled ke CrashLoopBackOff setelah beberapa kegagalan beruntun.
# tidak-dijalankan
# menampilkan request dan limit container dalam format JSON
kubectl get pod web-1 -o jsonpath='{.spec.containers[0].resources}'
# menampilkan status pod yang prosesnya diakhiri kernel
kubectl get pod web-1{"limits":{"cpu":"500m","memory":"256Mi"},"requests":{"cpu":"250m","memory":"128Mi"}}
NAME READY STATUS RESTARTS AGE
web-1 0/1 OOMKilled 1 2mKesalahan umum#
Kesalahan pertama adalah menetapkan request yang jauh lebih besar daripada kebutuhan nyata. Pod yang tidak muat tetap berstatus Pending meskipun Node terlihat kosong, karena keputusan penjadwalan hanya menghitung request, bukan konsumsi aktual. Perbaikannya adalah mengukur konsumsi aplikasi selama beban normal, menyesuaikan request dengan data tersebut, kemudian memantau ulang setelah manifest diterapkan.
Kesalahan kedua adalah menetapkan limit memori tanpa ruang untuk lonjakan beban. Aplikasi yang konsumsinya naik sesaat, misalnya saat cache berkembang, langsung diakhiri kernel ketika menyentuh limit, sehingga Pod berpindah-pindah antara status OOMKilled dan Running. Memberi jarak antara request dan limit memori menyeimbangkan kedua kebutuhan tersebut, yaitu jaminan penempatan yang stabil dan ruang yang cukup untuk menyerap lonjakan.
Kesalahan ketiga adalah membiarkan Pod yang membutuhkan isolasi berjalan tanpa limit sama sekali. Pod tanpa limit memori dapat menghabiskan memori Node dan memicu pengusiran Pod lain, sedangkan Pod tanpa limit CPU dapat berebut waktu proses dengan komponen sistem. Batas atas diperlukan bukan hanya untuk melindungi Pod itu sendiri, tetapi juga untuk menjaga kapasitas yang tersisa bagi Pod lain pada Node yang sama.
Ringkasan#
- Request menyatakan jaminan CPU dan memori yang dihitung scheduler saat penempatan, sedangkan limit membatasi konsumsi sumber daya saat container berjalan.
- CPU ditulis dalam core dengan akhiran m untuk milicore, sedangkan memori menggunakan akhiran biner Mi dan Gi berpangkat 1024 atau akhiran desimal M dan G berpangkat 1000.
- Pod ditempatkan hanya apabila jumlah request seluruh Pod di Node ditambah request Pod kandidat tidak melebihi allocatable, sehingga Pod berstatus Pending bila tidak ada Node yang memenuhi.
- Konsumsi CPU di atas limit dibatasi (throttle) dengan container tetap berjalan, sedangkan konsumsi memori di atas limit mengakhiri proses dengan status OOMKilled.
Referensi#
- Resource Management for Pods and Containers — Kubernetes Docs — Semantik request dan limit, satuan CPU serta memori, dan perbedaan OOMKilled dengan throttle.
- Pod — Kubernetes Docs — Referensi API kunci resources pada spesifikasi container beserta format nilainya.