Satu kluster biasanya digunakan bersama oleh beberapa tim, dan tanpa pembatasan satu tim yang boros dapat menghabiskan kapasitas seluruh Node. Topik ini membahas Namespace sebagai lingkup konsumsi per tim, ResourceQuota untuk membatasi total sumber daya satu Namespace, dan LimitRange untuk mengisi nilai default serta pagar kecil per container. Setelah membacanya, pembagian kluster menjadi wilayah yang adil antartim dapat dirancang dengan dua objek kecil.
Namespace sebagai lingkup konsumsi#
Namespace membagi satu kluster fisik menjadi beberapa lingkup virtual. Keunikan nama objek hanya dituntut di dalam namespace-nya sendiri, sehingga dua tim dapat masing-masing memiliki Service bernama catatan-api tanpa saling bertabrakan. Seluruh penghitungan kuota juga dilakukan per namespace, dan kebijakan hak akses dapat dibatasi per namespace melalui RBAC, sehingga Namespace menjadi unit pembatasan yang paling alami untuk satu tim.
Dua namespace yang digunakan sebagai contoh sepanjang topik ini berbunyi: aplikasi-catatan untuk beban kerja aplikasi catatan dan operasi untuk perkakas operasional seperti agen pemantauan. Pemisahan ini membuat total konsumsi tiap tim terbaca secara terpisah melalui perintah yang sama. Nama namespace ditulis dalam huruf kecil dengan tanda hubung karena mengikuti aturan penamaan DNS objek Kubernetes.
# tidak-dijalankan
# membuat namespace untuk aplikasi catatan
kubectl create namespace aplikasi-catatan
# membuat namespace untuk perkakas operasional
kubectl create namespace operasiNamespace hanya memisahkan lingkup, tetapi belum membatasi apa pun. Tanpa objek kuota, sebuah namespace masih boleh menghabiskan seluruh kapasitas kluster, sehingga pembatasan nyata datang dari ResourceQuota pada bagian berikutnya.
ResourceQuota membatasi total sumber daya#
ResourceQuota menetapkan batas keras bernama hard untuk total sumber daya satu namespace. Bidang yang paling sering diatur terbagi menjadi tiga kelompok, yaitu total request CPU dan memori, total limit CPU dan memori, serta jumlah objek seperti Pod, Service, dan ConfigMap. Apabila pembuatan objek baru membuat total namespace melebihi batas, API server menolak pembuatannya dengan pesan error yang menyebut kuota yang terlampaui.
apiVersion: v1
kind: ResourceQuota
metadata:
name: kuota-aplikasi-catatan
namespace: aplikasi-catatan
spec:
hard:
requests.cpu: "2"
requests.memory: 2Gi
limits.cpu: "4"
limits.memory: 4Gi
pods: "20"
services: "5"
configmaps: "10"Manifest di atas membatasi namespace aplikasi-catatan pada total request dua core CPU dan 2Gi memori, dengan jumlah maksimum 20 Pod. Ada satu perilaku yang perlu diketahui sejak awal: apabila kuota komputasi aktif di sebuah namespace, Pod tanpa nilai request akan ditolak oleh sistem kuota, kecuali nilai default sudah tersedia. Perilaku tersebut menjadikan LimitRange sebagai pendamping yang wajib, sebagaimana dibahas pada bagian berikutnya.
Bentuk penolakannya terlihat pada keluaran berikut, yang berasal dari percobaan membuat Pod dengan request melebihi sisa kuota.
# tidak-dijalankan
# mencoba membuat Pod yang request-nya melebihi sisa kuota
kubectl apply -f pod-berat.yaml -n aplikasi-catatanError from server (Forbidden): error when creating "pod-berat.yaml": pods "pekerja-arsip" is forbidden: exceeded quota: kuota-aplikasi-catatan, requested: requests.cpu=1, used: requests.cpu=1100m, limited: requests.cpu=2LimitRange mengisi nilai default#
LimitRange beroperasi pada tingkat container atau Pod, bukan pada total namespace. Tugas utamanya ada dua: mengisi nilai default saat Pod tidak menetapkan resources, dan menegakkan pagar minimum serta maksimum untuk setiap container. Bidang default menjadi nilai limit, bidang defaultRequest menjadi nilai request, sedangkan min dan max menolak container yang nilainya keluar dari pagar. Penegakan dilakukan saat objek diterima API server, dan objek yang sudah ada tidak diubah oleh LimitRange yang baru.
apiVersion: v1
kind: LimitRange
metadata:
name: default-aplikasi-catatan
namespace: aplikasi-catatan
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 256Mi
defaultRequest:
cpu: 100m
memory: 128Mi
max:
cpu: "2"
memory: 1Gi
min:
cpu: 50m
memory: 64MiDengan LimitRange di atas, setiap container di namespace aplikasi-catatan yang tidak menetapkan resources menerima request 100m dan 128Mi secara otomatis, sehingga tidak lagi ditolak oleh ResourceQuota. Nilai default ini juga masuk ke dalam perhitungan total kuota, sehingga sisa kuota mengecil tanpa perubahan pada manifest aplikasi.
Memvalidasi kuota dengan perhitungan sederhana#
Keputusan penolakan kuota dapat disimulasikan sebelum manifest disebarkan, yaitu dengan menjumlahkan request seluruh Pod dan membandingkannya dengan nilai hard. Berkas resourcequota.yaml di atas menjadi sumber batas, sedangkan daftar Pod contoh ditulis langsung di dalam skrip. Perhitungan yang sama dilakukan API server saat pembuatan objek, hanya tanpa ringkasan yang mudah dibaca.
import yaml
# membaca kuota dari manifest resourcequota.yaml di bundle
kuota = yaml.safe_load(open("resourcequota.yaml", encoding="utf-8"))
hard = kuota["spec"]["hard"]
hard_cpu = int(float(hard["requests.cpu"]) * 1000) # milicore
def ke_mib(nilai):
# mengonversi string seperti "2Gi" menjadi MiB
teks = str(nilai)
if teks.endswith("Gi"):
return int(float(teks[:-2]) * 1024)
if teks.endswith("Mi"):
return int(teks[:-2])
raise ValueError(f"satuan memori tidak dikenal: {teks}")
hard_mem = ke_mib(hard["requests.memory"])
# daftar Pod contoh di namespace aplikasi-catatan, berupa request tiap Pod
pod = [
{"nama": "catatan-api-1", "cpu_m": 250, "mem_mib": 256},
{"nama": "catatan-api-2", "cpu_m": 250, "mem_mib": 256},
{"nama": "catatan-web-1", "cpu_m": 100, "mem_mib": 128},
{"nama": "pekerja-laporan-1", "cpu_m": 500, "mem_mib": 512},
]
cpu_total = sum(p["cpu_m"] for p in pod)
mem_total = sum(p["mem_mib"] for p in pod)
print(f"Kuota namespace {kuota['metadata']['namespace']}:")
print(f" requests.cpu: {hard_cpu}m, requests.memory: {hard_mem}Mi")
print(f"Pod berjalan ({len(pod)} Pod): total request CPU {cpu_total}m, memori {mem_total}Mi")
def nilai_cpu(cpu, hard):
return "masih dalam kuota" if cpu <= hard else "MELEBIHI kuota, pembuatan Pod akan ditolak"
print(f" CPU : {cpu_total}m dari {hard_cpu}m -> {nilai_cpu(cpu_total, hard_cpu)}")
print(f" memori: {mem_total}Mi dari {hard_mem}Mi -> {nilai_cpu(mem_total, hard_mem)}")
# menyimulasikan penambahan satu Pod baru
baru = {"nama": "pekerja-arsip-1", "cpu_m": 1000, "mem_mib": 512}
cpu_baru = cpu_total + baru["cpu_m"]
mem_baru = mem_total + baru["mem_mib"]
print(f"Penambahan {baru['nama']} (request {baru['cpu_m']}m, {baru['mem_mib']}Mi):")
print(f" CPU : {cpu_baru}m dari {hard_cpu}m -> {nilai_cpu(cpu_baru, hard_cpu)}")
print(f" memori: {mem_baru}Mi dari {hard_mem}Mi -> {nilai_cpu(mem_baru, hard_mem)}")Kuota namespace aplikasi-catatan:
requests.cpu: 2000m, requests.memory: 2048Mi
Pod berjalan (4 Pod): total request CPU 1100m, memori 1152Mi
CPU : 1100m dari 2000m -> masih dalam kuota
memori: 1152Mi dari 2048Mi -> masih dalam kuota
Penambahan pekerja-arsip-1 (request 1000m, 512Mi):
CPU : 2100m dari 2000m -> MELEBIHI kuota, pembuatan Pod akan ditolak
memori: 1664Mi dari 2048Mi -> masih dalam kuotaKeluaran menunjukkan kondisi berjalan yang masih lega, tetapi penambahan satu Pod dengan request 1000m membuat total CPU menjadi 2100m dan melewati batas 2000m. Pada kluster sungguhan, API server akan menolak Pod tersebut dengan pesan exceeded quota sebagaimana contoh di bagian ResourceQuota, sedangkan memori masih menyisakan ruang.
Membandingkan ResourceQuota dan LimitRange#
Kedua objek sering ditulis berpasangan karena saling menutup celah. ResourceQuota menjaga total namespace agar satu tim tidak menghabiskan kluster, sedangkan LimitRange menjaga tiap container agar nilainya wajar dan tidak ada Pod yang lolos tanpa nilai request. Tabel berikut merangkum perbedaannya.
| Aspek | ResourceQuota | LimitRange |
|---|---|---|
| Lingkup | total satu namespace | satu container atau satu Pod |
| Tujuan utama | membatasi konsumsi agregat per tim | mengisi default dan menegakkan pagar per objek |
| Bidang yang diatur | request dan limit CPU-memori, jumlah objek | default, defaultRequest, min, max |
| Akibat pelanggaran | pembuatan objek ditolak dengan pesan exceeded quota | pembuatan Pod ditolak atau diberi nilai default |
| Peran terhadap Pod tanpa resources | menolak Pod apabila kuota komputasi aktif | mengisi request dan limit dari nilai default |
Dari tabel tersebut terlihat bahwa keduanya tidak saling menggantikan. Kuota tanpa LimitRange membuat setiap manifest harus menulis resources secara eksplisit, sedangkan LimitRange tanpa kuota menjaga agar nilai tiap container tetap wajar tetapi tidak membatasi jumlah Pod yang boleh dibuat. Praktik yang lazim berbunyi: pasang keduanya pada setiap namespace tim sejak awal.
Ringkasan#
- Namespace membagi kluster menjadi lingkup per tim, dan seluruh penghitungan kuota dilakukan per namespace.
- ResourceQuota menetapkan batas keras untuk total request, total limit, dan jumlah objek, serta menolak pembuatan objek yang melebihinya.
- LimitRange mengisi nilai default request dan limit untuk container yang tidak menetapkannya, sekaligus menegakkan batas minimum dan maksimum.
- Kuota dan LimitRange saling melengkapi, dan keputusan penolakan kuota dapat disimulasikan dengan menjumlahkan request Pod lalu membandingkannya dengan nilai hard.
Referensi#
- Resource Quotas — Kubernetes — Dokumentasi resmi ResourceQuota beserta daftar bidang yang dapat dibatasi.
- Limit Ranges — Kubernetes — Dokumentasi resmi LimitRange untuk nilai default dan batas per container.
- Namespaces — Kubernetes — Dokumentasi resmi konsep Namespace dan penggunaannya untuk pembagian lingkup.