Kenapa Kubernetes Jadi Sumber Pemborosan Cloud Terbesar di 2026?
Kubernetes sudah jadi standar de facto untuk orkestrasi container di hampir semua perusahaan teknologi. Tapi di balik semua fleksibilitas dan skalabilitas yang ditawarkan, ada kenyataan pahit yang jarang dibicarakan: mayoritas organisasi membuang 20-40% dari total pengeluaran Kubernetes mereka. Angka yang bikin geleng-geleng kepala.
Menurut laporan Cast AI Kubernetes Cost Benchmark 2025, rata-rata utilisasi CPU di seluruh klaster Kubernetes hanya 10% — turun dari 13% tahun sebelumnya. Utilisasi memori pun cuma berada di angka 23%. Artinya? Perusahaan Anda kemungkinan besar membayar 2 hingga 8 kali lipat dari resource yang benar-benar dipakai.
Ini bukan sekadar statistik.
Studi dari Wozz terhadap 3.042 klaster produksi di lebih dari 600 perusahaan pada Januari 2026 menemukan bahwa 68% pod meminta memori 3-8 kali lebih banyak dari yang sebenarnya digunakan. Jujur, waktu pertama kali lihat angka ini, saya kira ada yang salah datanya. Ternyata tidak.
Kenapa ini bisa terjadi? Akar masalahnya terletak pada budaya engineering yang terlalu konservatif. Dalam survei, 64% engineer mengaku menambahkan headroom resource 2-4 kali lipat setelah mengalami satu kali insiden OOM (Out of Memory). Bisa dipahami sih — mereka lebih khawatir aplikasi mati daripada biaya membengkak. Ditambah lagi, tim platform seringkali nggak punya visibilitas real-time atas tren utilisasi.
Di artikel sebelumnya, kita sudah membahas 12 strategi optimasi biaya cloud secara umum. Nah, kali ini kita akan menyelam lebih dalam ke dunia Kubernetes — mulai dari teknik right-sizing pod yang presisi, konfigurasi autoscaling multi-layer, sampai implementasi FinOps khusus container dengan tools seperti Kubecost dan OpenCost. Panduan ini dirancang untuk membantu Anda memangkas 40-70% biaya Kubernetes tanpa mengorbankan performa.
Memahami Struktur Biaya Kubernetes
Biaya Compute: Ujung Tombak Pengeluaran
Biaya compute — yaitu node (VM) yang menjalankan pod Anda — biasanya menyumbang 60-80% dari total pengeluaran Kubernetes. Biaya ini ditentukan oleh jumlah dan tipe instance yang digunakan. Masalahnya, Kubernetes scheduler mengalokasikan pod berdasarkan resource requests, bukan utilisasi aktual.
Jadi kalau pod Anda meminta 4 vCPU tapi cuma pakai 0.5 vCPU, scheduler tetap akan mereservasi 4 vCPU di node tersebut. Boros? Sangat.
Ini menciptakan fenomena yang disebut stranded resources — CPU dan memori yang sudah direservasi tapi tidak benar-benar digunakan oleh workload apapun. Data industri menunjukkan 40% CPU dan 57% memori yang di-provision di klaster Kubernetes tidak pernah di-request oleh pod manapun. Bayangkan, hampir separuh dari kapasitas node Anda benar-benar menganggur.
Biaya Non-Compute yang Sering Diabaikan
Selain compute, ada beberapa komponen biaya yang seringkali luput dari perhatian (dan ini yang biasanya bikin kaget saat audit):
- Persistent Volume (PV) Storage: Disk yang di-attach ke pod sering di-provision dengan ukuran berlebihan. Volume 100GB yang cuma terisi 10GB tetap ditagih penuh.
- Load Balancer: Setiap Service bertipe LoadBalancer membuat cloud load balancer terpisah dengan biaya per jam sendiri. Di lingkungan dengan banyak microservices, biaya ini bisa melonjak cepat.
- Egress (Transfer Data Keluar): Transfer data antar availability zone dan antar region dikenakan biaya signifikan yang sering nggak terpantau.
- Logging dan Monitoring: Ingestion log yang tidak difilter ke layanan seperti CloudWatch, Datadog, atau Elastic bisa menghabiskan ribuan dolar per bulan. Serius.
- Orphaned Resources: PV yang sudah detach, snapshot lama, IP statis yang tidak dipakai — semua ini terus ditagih sampai dihapus manual.
Tim yang hanya fokus pada optimasi compute sering kaget menemukan bahwa biaya non-compute ini bisa melebihi waste dari overprovisioning node. Jadi pastikan audit Anda mencakup semua komponen biaya ini, bukan cuma compute.
Right-Sizing Pod: Fondasi Penghematan Kubernetes
Memahami Resource Requests dan Limits
Di Kubernetes, setiap container dalam pod bisa memiliki dua parameter resource: requests dan limits. Requests adalah jumlah resource minimum yang dijamin untuk container tersebut — ini yang dipakai scheduler untuk menentukan di node mana pod akan dijalankan. Sementara limits adalah batas maksimum resource yang boleh digunakan container.
Masalah terbesar yang ditemukan di hampir semua audit Kubernetes? 80-90% pod memiliki CPU dan memory requests yang jauh di atas kebutuhan aktual. Engineer biasanya meng-copy-paste nilai dari deployment lain atau memasang angka besar "untuk jaga-jaga." Siapa yang nggak pernah begitu, kan?
Akibatnya, bin-packing node menjadi sangat tidak efisien — banyak pod yang mereservasi resource besar tapi cuma pakai sebagian kecil.
Cara Menentukan Request yang Tepat
Langkah pertama: kumpulkan data utilisasi aktual. Jangan menebak — gunakan data. Berikut query Prometheus yang bisa dipakai untuk menganalisis penggunaan resource per container selama 7 hari terakhir:
# Query PromQL: Rata-rata penggunaan CPU per container (7 hari)
avg_over_time(
rate(container_cpu_usage_seconds_total{
namespace!="kube-system",
container!=""
}[5m])[7d:]
) by (namespace, pod, container)
# Query PromQL: P95 penggunaan memori per container (7 hari)
quantile_over_time(0.95,
container_memory_working_set_bytes{
namespace!="kube-system",
container!=""
}[7d:]
) by (namespace, pod, container)
Dari data ini, gunakan pendekatan berikut untuk menentukan request yang tepat:
- CPU Request: Gunakan nilai P95 dari penggunaan CPU aktual, ditambah buffer 10-15%. CPU di Kubernetes bersifat compressible — kalau container melampaui limit, dia hanya akan di-throttle, bukan di-kill. Jadi buffer-nya bisa lebih kecil.
- Memory Request: Gunakan nilai P99 dari penggunaan memori aktual, ditambah buffer 20-25%. Memori bersifat incompressible — kalau container melebihi limit, dia akan langsung di-OOMKill. Makanya buffer untuk memori harus lebih besar.
- Memory Limit: Set di 120-150% dari request. Ini memberikan headroom untuk spike tanpa menyebabkan OOMKill, tapi tetap mencegah container dari memakan semua memori di node.
Script Otomatis untuk Audit Resource
Berikut script Python yang bisa Anda jalankan untuk membandingkan resource requests dengan utilisasi aktual di seluruh namespace. Script ini cukup straightforward — tinggal jalankan dan lihat hasilnya:
#!/usr/bin/env python3
"""
Kubernetes Resource Audit Script
Membandingkan resource requests dengan utilisasi aktual
"""
import subprocess
import json
from datetime import datetime
def get_pod_resources():
"""Ambil resource requests dari semua pod yang running"""
cmd = [
"kubectl", "get", "pods", "--all-namespaces",
"-o", "json", "--field-selector=status.phase=Running"
]
result = subprocess.run(cmd, capture_output=True, text=True)
pods = json.loads(result.stdout)
resources = []
for pod in pods["items"]:
ns = pod["metadata"]["namespace"]
name = pod["metadata"]["name"]
for container in pod["spec"].get("containers", []):
requests = container.get("resources", {}).get("requests", {})
limits = container.get("resources", {}).get("limits", {})
resources.append({
"namespace": ns,
"pod": name,
"container": container["name"],
"cpu_request": requests.get("cpu", "not set"),
"memory_request": requests.get("memory", "not set"),
"cpu_limit": limits.get("cpu", "not set"),
"memory_limit": limits.get("memory", "not set"),
})
return resources
def get_actual_usage():
"""Ambil metrik utilisasi aktual dari metrics-server"""
cmd = [
"kubectl", "top", "pods", "--all-namespaces",
"--containers", "--no-headers"
]
result = subprocess.run(cmd, capture_output=True, text=True)
usage = {}
for line in result.stdout.strip().split("\n"):
parts = line.split()
if len(parts) >= 5:
key = f"{parts[0]}/{parts[1]}/{parts[2]}"
usage[key] = {
"cpu_usage": parts[3],
"memory_usage": parts[4]
}
return usage
def parse_cpu(cpu_str):
"""Konversi CPU string ke millicores"""
if cpu_str == "not set":
return 0
if cpu_str.endswith("m"):
return int(cpu_str[:-1])
return int(float(cpu_str) * 1000)
def parse_memory(mem_str):
"""Konversi memory string ke MiB"""
if mem_str == "not set":
return 0
units = {"Ki": 1/1024, "Mi": 1, "Gi": 1024, "Ti": 1048576}
for suffix, multiplier in units.items():
if mem_str.endswith(suffix):
return float(mem_str[:-len(suffix)]) * multiplier
return float(mem_str) / (1024*1024)
def main():
print(f"=== Kubernetes Resource Audit - {datetime.now()} ===\n")
resources = get_pod_resources()
usage = get_actual_usage()
overprovisioned = []
for r in resources:
key = f"{r['namespace']}/{r['pod']}/{r['container']}"
if key in usage:
cpu_req = parse_cpu(r["cpu_request"])
cpu_used = parse_cpu(usage[key]["cpu_usage"])
mem_req = parse_memory(r["memory_request"])
mem_used = parse_memory(usage[key]["memory_usage"])
if cpu_req > 0 and cpu_used / cpu_req < 0.3:
overprovisioned.append({
"namespace": r["namespace"],
"pod": r["pod"],
"type": "CPU",
"requested": f"{cpu_req}m",
"used": f"{cpu_used}m",
"utilization": f"{(cpu_used/cpu_req)*100:.1f}%"
})
if mem_req > 0 and mem_used / mem_req < 0.4:
overprovisioned.append({
"namespace": r["namespace"],
"pod": r["pod"],
"type": "Memory",
"requested": f"{mem_req:.0f}Mi",
"used": f"{mem_used:.0f}Mi",
"utilization": f"{(mem_used/mem_req)*100:.1f}%"
})
print(f"Total pod yang diaudit: {len(resources)}")
print(f"Resource yang overprovisioned: {len(overprovisioned)}\n")
for item in sorted(overprovisioned, key=lambda x: x["utilization"]):
print(f" [{item['type']}] {item['namespace']}/{item['pod']}")
print(f" Request: {item['requested']} | Actual: {item['used']} | Util: {item['utilization']}")
if __name__ == "__main__":
main()
Pengurangan request sebesar 5-15% per pod mungkin terdengar kecil. Tapi efek kompounding-nya luar biasa — dengan bin-packing yang lebih efisien, Anda bisa menjalankan workload yang sama di node yang lebih sedikit, menghasilkan penghematan klaster sebesar 30-60%.
Autoscaling Multi-Layer: HPA, VPA, dan Karpenter
Kubernetes menawarkan beberapa mekanisme autoscaling yang bekerja di layer berbeda. Kunci optimasi biaya adalah menggunakan kombinasi yang tepat dan meng-tune konfigurasinya secara agresif — terutama untuk scale-down. Banyak yang fokus ke scale-up, padahal di situ justru uang terbakar: saat scale-down terlalu lambat.
Horizontal Pod Autoscaler (HPA): Skalabilitas Horizontal
HPA secara otomatis menambah atau mengurangi jumlah replika pod berdasarkan metrik yang diamati — seperti utilisasi CPU, memori, atau custom metrics dari Prometheus. HPA paling cocok untuk workload stateless yang bisa di-scale horizontal: web server, API gateway, dan microservices.
# HPA dengan konfigurasi scale-down yang agresif
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-gateway-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-gateway
minReplicas: 2
maxReplicas: 50
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 75
# Konfigurasi behavior untuk scale-down lebih cepat
behavior:
scaleDown:
stabilizationWindowSeconds: 120 # Tunggu 2 menit sebelum scale-down
policies:
- type: Percent
value: 25 # Kurangi max 25% replika per periode
periodSeconds: 60
- type: Pods
value: 2 # Atau kurangi max 2 pod per periode
periodSeconds: 60
selectPolicy: Min # Pilih pengurangan yang lebih kecil (lebih aman)
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Percent
value: 100 # Bisa double replika saat scale-up
periodSeconds: 30
Kesalahan konfigurasi HPA yang paling umum dan paling mahal? stabilizationWindowSeconds yang terlalu besar untuk scale-down. Default Kubernetes adalah 300 detik (5 menit), tapi banyak tim yang set ke 600 detik atau lebih karena takut flapping.
Akibatnya, setelah spike traffic selesai, pod berlebih tetap berjalan selama 10+ menit. Dan kalau spike terjadi beberapa kali sehari, itu bisa jadi jam-jam tambahan dari pod yang nggak diperlukan. Uang terbuang begitu saja.
Vertical Pod Autoscaler (VPA): Right-Sizing Otomatis
VPA menganalisis penggunaan resource historis dan secara otomatis menyesuaikan CPU dan memory requests untuk pod. VPA sangat berguna untuk workload yang sulit di-scale horizontal — misalnya database, message broker, dan ML inference.
# VPA dalam mode Recommendation (paling aman untuk memulai)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: ml-inference-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: ml-inference
updatePolicy:
updateMode: "Off" # Hanya memberikan rekomendasi, tidak auto-apply
resourcePolicy:
containerPolicies:
- containerName: inference-server
minAllowed:
cpu: 500m
memory: 512Mi
maxAllowed:
cpu: 8
memory: 16Gi
controlledResources: ["cpu", "memory"]
Peringatan penting: Jangan pernah menjalankan VPA dan HPA bersamaan pada metrik yang sama (CPU/memori). Ini salah satu anti-pattern paling terkenal di Kubernetes — menciptakan feedback loop yang bikin pusing. VPA menaikkan request, HPA melihat utilisasi turun lalu mengurangi replika, VPA melihat utilisasi naik lalu menaikkan request lagi. Hasilnya: ketidakstabilan dan biaya yang justru meningkat.
Kombinasi yang aman: gunakan HPA dengan custom/external metrics (misalnya request per second dari Prometheus) dan VPA hanya untuk memory request. Atau gunakan VPA dalam mode "Off" (rekomendasi saja) lalu terapkan rekomendasi secara manual atau melalui CI/CD pipeline.
Karpenter: Provisioning Node Just-In-Time
Karpenter adalah autoscaler level node yang dikembangkan oleh AWS sebagai pengganti Cluster Autoscaler tradisional. Perbedaan utamanya? Sementara Cluster Autoscaler bekerja dengan node group yang sudah didefinisikan sebelumnya, Karpenter bisa memilih tipe instance yang paling optimal secara real-time berdasarkan kebutuhan aktual pod yang pending.
# Konfigurasi NodePool Karpenter untuk optimasi biaya
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: cost-optimized
spec:
template:
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64", "arm64"] # Izinkan ARM untuk hemat biaya
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"] # Prioritas spot instances
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"] # Compute, general, memory optimized
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["5"] # Hanya generasi 6 ke atas
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
# Konfigurasi konsolidasi untuk menghilangkan node idle
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 60s # Konsolidasi setelah 60 detik idle
limits:
cpu: "200" # Batas total CPU di NodePool ini
memory: 400Gi # Batas total memori
weight: 100 # Prioritas lebih tinggi
---
# EC2NodeClass untuk konfigurasi node
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
amiSelectorTerms:
- alias: al2023@latest
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: my-cluster
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: my-cluster
instanceStorePolicy: RAID0 # Gunakan instance store kalau tersedia
Karpenter memberikan keuntungan signifikan dibanding Cluster Autoscaler:
- Provisioning lebih cepat: Karpenter langsung memanggil API cloud provider (misalnya EC2 CreateFleet) tanpa melalui abstraksi node group, sehingga node baru bisa tersedia dalam hitungan detik.
- Pemilihan instance optimal: Karpenter mengevaluasi ratusan tipe instance dan memilih yang paling cost-effective untuk workload yang sedang pending. Ini termasuk otomatis memilih antara Spot dan On-Demand, serta antara arsitektur x86 dan ARM.
- Konsolidasi node: Fitur ini secara otomatis mendeteksi node yang underutilized, memindahkan pod-nya ke node lain, dan mematikan node yang sudah kosong. Di sinilah penghematan nyata terjadi.
Menggabungkan HPA + VPA + Karpenter
Arsitektur autoscaling yang optimal menggunakan ketiga tools ini di layer yang berbeda:
- VPA (mode Off/Recommendation): Secara berkala menganalisis utilisasi dan memberikan rekomendasi right-sizing. Tim engineering review dan terapkan melalui CI/CD.
- HPA: Menambah/mengurangi replika pod berdasarkan traffic atau queue depth. Ini menangani variasi beban kerja harian.
- Karpenter: Menyediakan/menghapus node berdasarkan kebutuhan scheduling. Ketika HPA menambah pod dan scheduler nggak menemukan node dengan kapasitas cukup, Karpenter langsung provision node baru. Ketika pod berkurang, Karpenter konsolidasi dan hapus node yang nggak dipakai.
Alur kerjanya begini: spike traffic masuk → HPA menambah replika pod → scheduler butuh ruang → Karpenter provision node baru (spot kalau memungkinkan) → traffic turun → HPA kurangi replika → Karpenter konsolidasi pod dan matikan node kosong. Siklus ini berjalan otomatis 24/7. Anda cukup set up sekali, lalu biarkan sistem bekerja.
Memanfaatkan Spot Instance untuk Kubernetes
Spot instance (atau preemptible VM di GCP, spot VM di Azure) menawarkan diskon 70-90% dibanding harga on-demand. Di 2026, dengan mekanisme penanganan interruption yang semakin matang, spot instances jadi salah satu lever penghematan terbesar untuk workload Kubernetes. Kalau Anda belum pakai spot, honestly, Anda melewatkan potensi saving yang cukup besar.
Workload yang Cocok untuk Spot
- Batch processing dan ETL: Job yang bisa di-retry tanpa side effect
- CI/CD pipeline: Build dan test runner yang toleran terhadap restarts
- ML training: Training job dengan checkpointing
- Stateless microservices: API yang sudah didesain dengan graceful shutdown
- Event-driven workload: Consumer yang memproses message queue
Workload yang TIDAK Boleh di Spot
- Database dan stateful workload: Risiko data corruption saat interruption
- Workload latency-critical: Service yang nggak boleh ada downtime sama sekali
- Long-running job tanpa checkpointing: Akan kehilangan seluruh progress saat interrupted
Strategi Spot di Karpenter
# NodePool terpisah untuk workload spot-friendly
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: spot-workloads
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"] # Hanya spot instances
- key: kubernetes.io/arch
operator: In
values: ["amd64", "arm64"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
taints:
- key: spot
value: "true"
effect: NoSchedule # Hanya pod dengan tolerasi spot
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 30s
limits:
cpu: "500"
---
# Deployment dengan tolerasi spot dan spread topology
apiVersion: apps/v1
kind: Deployment
metadata:
name: batch-processor
spec:
replicas: 10
template:
spec:
tolerations:
- key: spot
operator: Equal
value: "true"
effect: NoSchedule
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: batch-processor
terminationGracePeriodSeconds: 120 # Waktu graceful shutdown
containers:
- name: processor
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 30 && /app/drain.sh"]
Tips penting: selalu diversifikasi tipe instance saat menggunakan spot. Semakin banyak tipe instance yang Anda izinkan, semakin kecil kemungkinan semua instance Anda ter-interrupt bersamaan. Karpenter secara otomatis menangani ini dengan memilih dari pool instance yang luas — jadi biarkan dia bekerja dan jangan terlalu membatasi pilihan instance type-nya.
FinOps untuk Kubernetes: Visibilitas dan Akuntabilitas Biaya
Showback vs Chargeback
Tantangan terbesar FinOps di Kubernetes adalah bahwa container menambahkan layer abstraksi lagi di atas virtualisasi cloud. Satu node bisa menjalankan puluhan pod dari tim yang berbeda, membuat alokasi biaya jauh lebih kompleks dibanding VM tradisional.
Ada dua pendekatan untuk mengatasi ini:
- Showback: Menampilkan informasi biaya ke setiap tim tanpa menagih mereka. Tujuannya menciptakan awareness — ketika engineer melihat berapa biaya service mereka, mereka secara alami mulai mengoptimasi. Ini pendekatan yang lebih mudah diimplementasi dan nggak memerlukan perubahan proses keuangan.
- Chargeback: Benar-benar menagih biaya ke unit bisnis atau tim berdasarkan konsumsi aktual mereka. Lebih efektif untuk akuntabilitas, tapi memerlukan model alokasi yang akurat dan buy-in dari leadership.
Rekomendasi dari pengalaman: mulai dengan showback dulu. Banyak organisasi menemukan bahwa showback saja sudah cukup untuk mendorong pengurangan biaya yang signifikan. Migrasi ke chargeback ketika biaya cloud sudah menjadi bagian signifikan dari cost structure produk Anda.
Strategi Labeling untuk Alokasi Biaya
Label Kubernetes adalah mekanisme utama untuk mengalokasikan biaya ke pemiliknya. Tanpa label yang konsisten, tools cost monitoring apapun nggak akan bisa memberikan data yang berguna. Ini fondasi yang harus benar dari awal.
# Template namespace dengan label wajib
apiVersion: v1
kind: Namespace
metadata:
name: payment-service
labels:
team: fintech-backend
cost-center: cc-1234
environment: production
product: payment-gateway
---
# Kyverno policy untuk enforce labeling
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-cost-labels
spec:
validationFailureAction: Enforce
rules:
- name: check-labels
match:
any:
- resources:
kinds:
- Deployment
- StatefulSet
- DaemonSet
validate:
message: "Wajib memiliki label: team, cost-center, environment"
pattern:
metadata:
labels:
team: "?*"
cost-center: "?*"
environment: "production|staging|development"
Empat label wajib minimum yang harus ada di setiap workload:
- team: Tim yang bertanggung jawab atas workload ini
- cost-center: Kode pusat biaya untuk integrasi dengan sistem keuangan
- environment: production, staging, atau development
- product/service: Nama produk atau layanan yang dilayani
Gunakan admission controller seperti Kyverno atau OPA Gatekeeper untuk menolak deployment yang nggak punya label wajib. Percayalah, ini jauh lebih efektif daripada mengandalkan dokumentasi atau proses review manual yang sering dilupakan.
Mengimplementasikan Kubecost dan OpenCost
Kubecost dan OpenCost adalah tools cost monitoring paling populer di ekosistem Kubernetes. OpenCost adalah proyek open-source CNCF (saat ini dalam tahap incubating) yang menyediakan engine cost allocation, sementara Kubecost adalah produk enterprise yang dibangun di atas engine OpenCost dengan fitur tambahan seperti multi-cluster view, cost forecasting, dan anomaly detection.
Instalasi OpenCost
# Instalasi OpenCost via Helm
helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm repo update
# Install dengan konfigurasi dasar
helm install opencost opencost/opencost \
--namespace opencost \
--create-namespace \
--set opencost.prometheus.internal.enabled=true
# Verifikasi instalasi
kubectl get pods -n opencost
kubectl port-forward -n opencost svc/opencost 9090:9090
Fitur menarik di 2025-2026: OpenCost sekarang mendukung mode "Promless" — bisa berjalan tanpa Prometheus untuk deployment yang lebih ringan. OpenCost juga sudah menyertakan MCP (Model Context Protocol) server yang memungkinkan AI agent mengakses data cost allocation melalui interface standar. Cukup keren untuk integrasi otomasi.
Instalasi Kubecost
# Instalasi Kubecost via Helm
helm repo add kubecost https://kubecost.github.io/cost-analyzer/
helm repo update
helm install kubecost kubecost/cost-analyzer \
--namespace kubecost \
--create-namespace \
--set kubecostToken="YOUR_TOKEN" \
--set prometheus.nodeExporter.enabled=true \
--set prometheus.serviceAccounts.nodeExporter.create=true
# Akses dashboard
kubectl port-forward -n kubecost svc/kubecost-cost-analyzer 9090:9090
Query Biaya per Namespace dengan kubectl-cost
# Install kubectl-cost plugin
kubectl krew install cost
# Lihat biaya per namespace (30 hari terakhir)
kubectl cost namespace --window 30d --show-cpu --show-memory --show-efficiency
# Output contoh:
# +-----------------+-------+--------+---------+----------+------------+
# | NAMESPACE | CPU | MEMORY | TOTAL | CPU EFF | MEM EFF |
# +-----------------+-------+--------+---------+----------+------------+
# | production | $245 | $189 | $434 | 67% | 54% |
# | staging | $89 | $67 | $156 | 23% | 18% |
# | development | $45 | $34 | $79 | 12% | 9% |
# | monitoring | $34 | $45 | $79 | 78% | 82% |
# +-----------------+-------+--------+---------+----------+------------+
# Lihat biaya per deployment dalam namespace tertentu
kubectl cost deployment --namespace production --window 7d
# Identifikasi pod termahal
kubectl cost pod --window 7d --sort-by total --top 20
Dari contoh output di atas, terlihat bahwa namespace staging hanya memiliki efisiensi CPU 23% dan memori 18%. Ini kandidat utama untuk right-sizing — atau bahkan bisa dijadwalkan untuk shutdown di luar jam kerja (yang akan kita bahas selanjutnya).
Menghilangkan Waste: Membersihkan Resource Zombie
Klaster Kubernetes punya kecenderungan mengakumulasi "sampah" seiring waktu: resource yang sudah nggak dipakai tapi masih berjalan dan terus ditagih. Ini seperti langganan streaming yang lupa di-cancel — kecil per item, tapi kalau dijumlahkan bisa bikin kaget.
Identifikasi Resource Zombie
#!/bin/bash
# Script untuk mendeteksi resource zombie di Kubernetes
echo "=== RESOURCE ZOMBIE AUDIT ==="
echo ""
# 1. Pod dalam CrashLoopBackOff (tetap konsumsi resource tapi tidak produktif)
echo "--- Pod dalam CrashLoopBackOff ---"
kubectl get pods --all-namespaces --field-selector=status.phase!=Running \
-o custom-columns="NAMESPACE:.metadata.namespace,NAME:.metadata.name,STATUS:.status.phase,RESTARTS:.status.containerStatuses[0].restartCount" \
2>/dev/null | grep -i crash
echo ""
# 2. PVC yang tidak di-mount ke pod manapun
echo "--- PVC yang Tidak Digunakan ---"
ALL_PVC=$(kubectl get pvc --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}')
USED_PVC=$(kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{range .spec.volumes[*]}{.persistentVolumeClaim.claimName}{"\n"}{end}{end}' | sort -u)
for pvc in $ALL_PVC; do
pvc_name=$(echo $pvc | cut -d'/' -f2)
if ! echo "$USED_PVC" | grep -q "^${pvc_name}$"; then
echo " UNUSED: $pvc"
fi
done
echo ""
# 3. Service bertipe LoadBalancer yang mungkin tidak diperlukan
echo "--- Service bertipe LoadBalancer (cek apakah masih diperlukan) ---"
kubectl get svc --all-namespaces \
-o custom-columns="NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,EXTERNAL-IP:.status.loadBalancer.ingress[0].hostname" \
| grep LoadBalancer
echo ""
# 4. Job dan CronJob yang sudah selesai tapi tidak di-cleanup
echo "--- Completed Jobs (kandidat cleanup) ---"
kubectl get jobs --all-namespaces --field-selector=status.successful=1 \
-o custom-columns="NAMESPACE:.metadata.namespace,NAME:.metadata.name,COMPLETIONS:.status.succeeded,AGE:.metadata.creationTimestamp"
Otomatisasi Cleanup dengan CronJob
# CronJob untuk membersihkan completed jobs yang lebih dari 24 jam
apiVersion: batch/v1
kind: CronJob
metadata:
name: cleanup-completed-jobs
namespace: kube-system
spec:
schedule: "0 2 * * *" # Setiap hari jam 2 pagi
jobTemplate:
spec:
template:
spec:
serviceAccountName: job-cleaner
containers:
- name: cleaner
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
# Hapus completed jobs yang lebih dari 24 jam
kubectl get jobs --all-namespaces \
--field-selector=status.successful=1 \
-o json | \
jq -r '.items[] |
select(
(now - (.status.completionTime | fromdateiso8601)) > 86400
) |
"\(.metadata.namespace) \(.metadata.name)"' | \
while read ns name; do
echo "Deleting job $ns/$name"
kubectl delete job -n "$ns" "$name"
done
restartPolicy: OnFailure
Menjadwalkan Shutdown Lingkungan Non-Produksi
Ini salah satu quick win terbesar yang sering diabaikan. Coba pikir: kalau tim Anda bekerja 8 jam sehari, 5 hari seminggu, maka lingkungan dev/staging aktif hanya 24% dari waktu total. Artinya, Anda membayar 76% untuk resource yang nggak ada yang pakai. Tujuh puluh enam persen!
# Menggunakan KEDA ScaledObject untuk scale-to-zero di luar jam kerja
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: staging-api-scaler
namespace: staging
spec:
scaleTargetRef:
name: staging-api
minReplicaCount: 0 # Bisa scale ke 0
maxReplicaCount: 5
triggers:
- type: cron
metadata:
timezone: Asia/Jakarta
start: "0 8 * * 1-5" # Scale up: Senin-Jumat jam 8 pagi
end: "0 20 * * 1-5" # Scale down: Senin-Jumat jam 8 malam
desiredReplicas: "3"
Menurut data industri, penerapan jadwal shutdown untuk lingkungan non-produksi bisa mengurangi waste sebesar 20-25% dalam 90 hari pertama. Ini penghematan yang langsung terasa tanpa perlu perubahan arsitektur apapun. Tinggal pasang, langsung hemat.
Governance dan Policy Automation
Optimasi biaya bukan soal satu kali cleanup lalu selesai — ini harus jadi bagian dari proses engineering sehari-hari. Policy automation memastikan bahwa konfigurasi yang boros nggak pernah sampai ke production.
Contoh Policy dengan OPA Gatekeeper
# ConstraintTemplate: Melarang resource request yang terlalu besar
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8smaxresources
spec:
crd:
spec:
names:
kind: K8sMaxResources
validation:
openAPIV3Schema:
type: object
properties:
maxCpuRequest:
type: string
maxMemoryRequest:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8smaxresources
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
cpu_request := container.resources.requests.cpu
max_cpu := input.parameters.maxCpuRequest
cpu_request > max_cpu
msg := sprintf(
"Container %v meminta CPU %v, melebihi batas %v. Ajukan exception jika memang diperlukan.",
[container.name, cpu_request, max_cpu]
)
}
---
# Constraint: Terapkan batas maksimum
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sMaxResources
metadata:
name: max-resource-requests
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet"]
namespaces: ["production", "staging"]
parameters:
maxCpuRequest: "4"
maxMemoryRequest: "8Gi"
Dengan policy seperti ini, setiap deployment yang meminta CPU lebih dari 4 core atau memori lebih dari 8Gi akan ditolak secara otomatis. Tim harus mengajukan exception dengan justifikasi yang jelas. Ini mencegah overprovisioning bahkan sebelum resource dibuat — jauh lebih baik daripada membersihkan setelahnya.
Mengintegrasikan Biaya ke CI/CD Pipeline
Salah satu praktik terbaik yang diadopsi oleh organisasi FinOps yang matang: menampilkan estimasi biaya langsung di pull request. Ketika engineer mengubah konfigurasi deployment, mereka langsung melihat dampak biayanya sebelum merge. Transparan dan efektif.
# GitHub Actions: Estimasi biaya perubahan Kubernetes manifest
name: Cost Estimation
on:
pull_request:
paths:
- "k8s/**"
- "helm/**"
jobs:
estimate-cost:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Calculate Resource Changes
id: calc
run: |
# Bandingkan resource requests antara base dan PR branch
git diff origin/main -- k8s/ helm/ | \
grep -E "^\+.*cpu:|^\+.*memory:" | \
while read line; do
echo "Changed: $line"
done
# Hitung estimasi biaya bulanan berdasarkan on-demand pricing
echo "monthly_delta=+$47.50" >> $GITHUB_OUTPUT
- name: Comment PR with Cost Impact
uses: actions/github-script@v7
with:
script: |
const delta = "${{ steps.calc.outputs.monthly_delta }}";
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## Estimasi Dampak Biaya
Perubahan ini diperkirakan akan menambah **${delta}/bulan** pada biaya Kubernetes.
Detail: perubahan resource requests pada deployment.`
});
Checklist Implementasi Optimasi Biaya Kubernetes
Oke, banyak banget yang sudah kita bahas. Untuk memudahkan implementasi, berikut checklist yang bisa Anda gunakan sebagai panduan. Urutannya disusun berdasarkan dampak dan kemudahan implementasi — mulai dari yang paling gampang sampai optimasi jangka panjang:
Minggu 1-2: Quick Wins
- Install OpenCost atau Kubecost untuk visibilitas biaya dasar
- Identifikasi dan hapus resource zombie (PV orphan, completed jobs, CrashLoopBackOff pods)
- Jadwalkan shutdown lingkungan non-produksi di luar jam kerja
- Review dan konsolidasi Service bertipe LoadBalancer yang redundan
Minggu 3-4: Right-Sizing
- Kumpulkan data utilisasi selama minimal 7 hari
- Identifikasi pod dengan utilisasi CPU <30% atau memori <40%
- Terapkan rekomendasi VPA (mulai dari mode Off/rekomendasi)
- Migrasi workload ke instance generasi terbaru (Graviton/ARM jika memungkinkan)
Bulan 2: Autoscaling
- Konfigurasi HPA dengan scale-down behavior yang agresif
- Implementasi Karpenter (AWS) atau node autoscaler yang sesuai
- Setup KEDA untuk workload event-driven
- Mulai adopsi spot instances untuk workload yang toleran terhadap interruption
Bulan 3+: FinOps Maturity
- Terapkan kebijakan labeling wajib dengan admission controller
- Bangun dashboard showback per tim dan per produk
- Integrasikan estimasi biaya ke CI/CD pipeline
- Implementasi policy automation dengan OPA Gatekeeper atau Kyverno
- Evaluasi transisi dari showback ke chargeback
Kesimpulan
Optimasi biaya Kubernetes memang bukan proyek satu kali — ini praktik berkelanjutan yang harus jadi bagian dari budaya engineering organisasi Anda. Tapi kabar baiknya, hasilnya sepadan. Dengan kombinasi right-sizing pod yang presisi, autoscaling multi-layer (HPA + VPA + Karpenter), pemanfaatan spot instances, dan implementasi FinOps yang matang, organisasi secara konsisten mencapai penghematan 40-70% dari total biaya Kubernetes mereka.
Kunci suksesnya ada pada tiga hal: visibilitas (Anda nggak bisa mengoptimasi apa yang nggak bisa diukur), otomasi (policy enforcement dan autoscaling menghilangkan ketergantungan pada tindakan manual), dan akuntabilitas (setiap tim harus melihat dan bertanggung jawab atas biaya cloud mereka).
Mulailah dari quick wins — install cost monitoring, bersihkan resource zombie, dan jadwalkan shutdown non-production. Kemudian secara bertahap tingkatkan maturitas FinOps Anda. Ingat, pengurangan request 5-15% per pod mungkin terdengar kecil, tapi efek kompounding di seluruh klaster bisa menghasilkan penghematan ratusan ribu dolar per tahun.
Di era di mana biaya cloud terus meningkat dan setiap dolar harus dipertanggungjawabkan, kemampuan mengelola biaya Kubernetes secara efektif bukan lagi nice-to-have — ini keharusan.