Cost Optimization #
Kubernetes memudahkan scaling, tapi juga memudahkan pemborosan. Cluster yang dibuat untuk eksperimen dan tidak pernah dihapus, node yang dipakai 10% kapasitasnya, atau development environment yang berjalan 24/7 padahal hanya dipakai 8 jam — semuanya adalah biaya nyata yang sering tidak disadari sampai tagihan cloud datang. Artikel ini membahas cara sistematis mengidentifikasi dan mengurangi pemborosan tanpa mengorbankan reliabilitas.
Di Mana Biaya Kubernetes Berasal #
Komponen biaya cluster Kubernetes di cloud:
Node (terbesar, biasanya 70-80% total biaya):
→ Compute: CPU dan memory node
→ Spot/Preemptible: 60-80% lebih murah dari on-demand
Storage (10-20%):
→ PersistentVolumes (SSD/HDD)
→ Snapshot backup
→ Container registry storage
Networking (5-15%):
→ Load balancer per Service type LoadBalancer
→ Egress traffic keluar region/internet
→ NAT Gateway untuk private cluster
Managed control plane:
→ EKS: $0.10/jam per cluster (~$72/bulan)
→ GKE/AKS: gratis untuk control plane
Identifikasi Pemborosan #
# Lihat utilisasi node (apakah node terlalu banyak?)
kubectl top nodes
# Hitung request vs actual usage per namespace
kubectl top pods -A --containers | sort -k4 -rn | head -20
# Cari Pod dengan resource yang sangat over-provisioned
# (request jauh lebih besar dari actual usage)
# Pod dengan CPU usage < 10% dari request (over-provisioned)
(
rate(container_cpu_usage_seconds_total{container!=""}[30m])
/ on(pod, namespace, container)
kube_pod_container_resource_requests{resource="cpu", container!=""}
) < 0.1
# Namespace dengan utilisasi CPU rendah
sum by (namespace) (
rate(container_cpu_usage_seconds_total{container!=""}[1h])
) /
sum by (namespace) (
kube_pod_container_resource_requests{resource="cpu"}
) * 100
Spot/Preemptible Node: Penghematan Terbesar #
Spot Instance (AWS) atau Preemptible VM (GCP) adalah node yang 60-80% lebih murah dari on-demand, tapi bisa diambil kembali oleh provider dengan notice 2 menit (AWS) atau 30 detik (GCP).
Workload yang cocok untuk Spot/Preemptible:
✓ Stateless service yang bisa restart cepat
✓ Batch processing dan data pipeline
✓ CI/CD runner
✓ Development dan staging environment
✓ ML training job
✓ Worker yang bisa retry jika terganggu
Workload yang TIDAK cocok:
✗ Database dengan data lokal
✗ Service yang tidak punya replicas (single replica production)
✗ Workload dengan startup time sangat lama (>10 menit)
✗ Service yang tidak bisa gracefully handle SIGTERM
# Node pool Spot untuk GKE
gcloud container node-pools create spot-workers \
--cluster production \
--spot \
--machine-type n2-standard-4 \
--num-nodes 0 \
--enable-autoscaling --min-nodes 0 --max-nodes 50
# Node pool Spot untuk EKS (managed node group)
eksctl create nodegroup \
--cluster production \
--name spot-workers \
--instance-types m5.xlarge,m5a.xlarge,m4.xlarge \ # multiple types untuk ketersediaan
--spot \
--nodes-min 0 \
--nodes-max 50
# Tolerasi Spot node di Deployment
spec:
template:
spec:
tolerations:
- key: "cloud.google.com/gke-spot" # GKE
operator: "Equal"
value: "true"
effect: "NoSchedule"
# atau untuk EKS:
# key: "eks.amazonaws.com/capacityType"
# value: "SPOT"
# Prefer Spot, tapi fallback ke on-demand jika tidak tersedia
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: cloud.google.com/gke-spot
operator: In
values: ["true"]
Scale ke Nol untuk Non-Production #
Development dan staging environment yang berjalan 24/7 membuang 2/3 biaya untuk jam yang tidak dipakai:
Pola yang umum:
Development environment: dipakai jam 9-18 (9 jam dari 24 jam = 37%)
Staging environment: dipakai jam 8-22 saat testing berlangsung (58%)
Sisa 42-63% waktu: cluster menyala tapi tidak ada yang pakai
Tools untuk scale ke nol:
Kube-downscaler: scale Deployment ke 0 berdasarkan jadwal
KEDA CronScaler: schedule scale
Manual: CronJob yang jalankan kubectl scale
# Kube-downscaler: matikan semua di luar jam kerja
# Deploy kube-downscaler dengan konfigurasi:
# --default-downtime="Mon-Fri 18:00-08:00 Asia/Jakarta"
# --default-downtime="Sat-Sun 00:00-24:00 Asia/Jakarta"
# Annotation di Deployment untuk exclude dari downscaling
metadata:
annotations:
downscaler/exclude: "true" # jangan scale down ini
downscaler/force-uptime: "true" # paksa tetap nyala
# Atau CronJob sederhana untuk scale down staging setiap malam
kubectl create cronjob scale-down-staging \
--image=bitnami/kubectl \
--schedule="0 20 * * 1-5" \
-- kubectl scale --replicas=0 deployment --all -n staging
kubectl create cronjob scale-up-staging \
--image=bitnami/kubectl \
--schedule="0 8 * * 1-5" \
-- kubectl scale --replicas=2 deployment --all -n staging
Right-Sizing: Hapus Padding yang Tidak Perlu #
Sumber padding yang sering tidak disadari:
1. "Safety margin" yang terlalu besar
Orang sering set requests 2-3x lebih besar dari yang dibutuhkan
"Lebih baik lebih dari kurang"
→ Biaya lebih besar, utilisasi rendah
2. Tidak pernah di-review setelah initial setup
requests di-set saat awal, tidak pernah di-update
meski usage actual sudah diketahui setelah beberapa bulan
3. Copy-paste dari template
"Deployment sebelumnya pakai 512Mi, pakai yang sama aja"
meski aplikasi baru jauh lebih ringan
Gunakan VPA untuk rekomendasi:
# Install VPA dan set mode Off untuk monitoring
kubectl apply -f https://github.com/kubernetes/autoscaler/releases/latest/download/vertical-pod-autoscaler.yaml
# Deploy VPA per Deployment dalam mode Off
# Setelah beberapa hari, lihat rekomendasi:
kubectl describe vpa api-vpa -n production | grep -A 10 "Recommendation"
# Terapkan rekomendasi ke values.yaml dan redeploy
# Review setiap bulan sekali sebagai bagian dari cost review rutin
Monitoring Biaya per Namespace dan Tim #
Tools untuk cost visibility:
Kubecost: breakdown biaya per namespace, Pod, label
OpenCost: open-source alternatif Kubecost
Cloud native: AWS Cost Explorer, GCP Billing, Azure Cost Management
Label strategy untuk cost allocation:
Semua resource harus punya label:
team: backend
environment: production
project: checkout-service
Dengan label ini, biaya bisa di-aggregate per tim dan proyek
# Enforcing cost labels via Kyverno policy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-cost-labels
spec:
validationFailureAction: Enforce
rules:
- name: check-cost-labels
match:
any:
- resources:
kinds: ["Deployment", "StatefulSet", "DaemonSet"]
validate:
message: "Resource harus punya label: team, environment, project"
pattern:
metadata:
labels:
team: "?*"
environment: "?*"
project: "?*"
Ringkasan #
- Spot/Preemptible untuk workload toleran — 60-80% lebih murah; gunakan untuk stateless service dengan replicas, batch job, CI runner, dan non-production environment.
- Scale ke nol untuk development dan staging — environment yang tidak dipakai di luar jam kerja = 42-63% biaya terbuang; kube-downscaler atau CronJob bisa menghemat signifikan.
- VPA dalam mode Off untuk rekomendasi — observasi penggunaan aktual selama 1-2 minggu, terapkan rekomendasi ke resource requests; hindari “safety margin” yang tidak berbasis data.
- Label yang konsisten untuk cost allocation — tanpa label
teamdanproject, tidak bisa tahu siapa yang menyebabkan lonjakan biaya; enforce via Kyverno policy.- Satu LoadBalancer per Ingress, bukan per Service — setiap LoadBalancer Service punya biaya tersendiri ($15-20/bulan per LB); gunakan satu Ingress Controller yang berbagi satu LB.
- Review biaya bulanan secara regular — biaya cloud cluster tanpa review bulanan cenderung naik terus; alokasikan waktu untuk cost review dan right-sizing sebagai bagian operasional rutin.
← Sebelumnya: High Availability Berikutnya: Disaster Recovery →