Kubernetes cost optimization không chỉ là câu chuyện “tắt bớt node cho rẻ”. Trong môi trường production, tối ưu chi phí Kubernetes phải đi cùng độ tin cậy, khả năng rollback và quan sát được tác động lên latency, error rate, capacity headroom. Nếu làm vội, bạn có thể tiết kiệm vài trăm đô nhưng đổi lại là pod bị OOMKilled, rollout chậm, autoscaler rung lắc hoặc mất availability khi có traffic spike.
Bài viết này là runbook thực chiến cho team platform/SRE/sysadmin vận hành Kubernetes trên EKS, GKE, AKS hoặc cluster self-managed. Bối cảnh lab/production: cluster có nhiều namespace, microservice, ingress, metrics-server, Prometheus/Grafana, workload stateless lẫn stateful, node group on-demand và có thể dùng spot/preemptible node. Mục tiêu là giảm lãng phí có kiểm soát, không hy sinh SLO.
1. Tư duy đúng về tối ưu chi phí Kubernetes
Chi phí Kubernetes thường rò rỉ ở nhiều tầng:
- Compute: requests CPU/memory đặt quá cao, node underutilized, workload chạy 24/7 dù chỉ cần giờ hành chính.
- Autoscaling: HPA/VPA/Cluster Autoscaler cấu hình thiếu hoặc ngưỡng chưa phù hợp.
- Storage: PVC thừa dung lượng, snapshot cũ, storage class đắt, log/metrics retention quá dài.
- Network: egress liên vùng, NAT Gateway, Load Balancer không dùng, traffic qua public path không cần thiết.
- Observability: scrape quá dày, high-cardinality metrics, log ingest không giới hạn.
Nguyên tắc quan trọng: tối ưu dựa trên dữ liệu thực tế, thay đổi nhỏ, đo trước/sau và có rollback.
2. Bước 1: Lập baseline chi phí và mức sử dụng
Trước khi chỉnh requests hoặc node group, cần biết hiện trạng. Nếu cloud có cost allocation tags/labels, hãy bật theo cluster, namespace, team hoặc environment. Trong Kubernetes, kiểm tra phân bổ tài nguyên:
kubectl top nodes
kubectl top pods -A --sort-by=cpu
kubectl top pods -A --sort-by=memory
kubectl get nodes -o wide
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,CPU_REQ:.spec.containers[*].resources.requests.cpu,MEM_REQ:.spec.containers[*].resources.requests.memory,CPU_LIM:.spec.containers[*].resources.limits.cpu,MEM_LIM:.spec.containers[*].resources.limits.memory'
Output mẫu:
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
worker-1 520m 13% 6200Mi 48%
worker-2 410m 10% 5900Mi 46%
worker-3 390m 9% 6100Mi 47%
Nếu CPU thấp nhưng memory gần 50%, cluster có thể đang bị giới hạn bởi memory request, không phải CPU. Việc chọn instance family khác có thể hiệu quả hơn chỉ giảm node.
3. Bước 2: Tìm namespace và workload lãng phí
Nếu có kube-state-metrics và Prometheus, so sánh request với usage thực tế. Ví dụ PromQL để xem tỷ lệ CPU usage/request theo namespace:
sum(rate(container_cpu_usage_seconds_total{container!="",pod!=""}[5m])) by (namespace)
/
sum(kube_pod_container_resource_requests{resource="cpu"}) by (namespace)
Memory usage/request:
sum(container_memory_working_set_bytes{container!="",pod!=""}) by (namespace)
/
sum(kube_pod_container_resource_requests{resource="memory"}) by (namespace)
Nếu namespace chỉ dùng 10–20% request trong nhiều ngày, đó là ứng viên tối ưu. Tuy nhiên cần xem p95/p99 theo thời gian, không chỉ snapshot.
4. Bước 3: Chuẩn hóa requests và limits
Scheduler dùng requests để đặt pod lên node. Requests quá cao làm node có vẻ đầy dù usage thật thấp. Requests quá thấp làm pod chen chúc, dễ throttle/OOM khi spike. Cách làm an toàn:
- Lấy usage p95 hoặc p99 trong 7–14 ngày.
- Đặt CPU request gần p90/p95 tùy workload, thêm buffer 20–30%.
- Đặt memory request gần p95 vì memory khó co giãn hơn CPU.
- Không đặt CPU limit quá thấp cho service latency-sensitive vì dễ bị throttling.
- Memory limit phải có, nhưng cần theo dõi OOMKilled sau thay đổi.
Ví dụ Deployment trước tối ưu:
resources:
requests:
cpu: "1000m"
memory: "2Gi"
limits:
cpu: "1000m"
memory: "2Gi"
Nếu usage 14 ngày cho thấy CPU p95 220m, memory p95 780Mi, có thể thử:
resources:
requests:
cpu: "300m"
memory: "1Gi"
limits:
cpu: "1500m"
memory: "1536Mi"
CPU limit cao hơn request cho phép burst; memory limit có buffer để tránh OOM.
5. Bước 4: Dùng HPA đúng cách
Horizontal Pod Autoscaler giúp scale replica theo CPU, memory hoặc custom metrics. Kiểm tra HPA hiện có:
kubectl get hpa -A
kubectl describe hpa -n production api-service
Ví dụ HPA cơ bản:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-service
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 65
Đừng đặt minReplicas quá thấp với service cần availability. Với service nhận traffic không đều, custom metrics như request rate, queue length hoặc latency thường tốt hơn CPU.
6. Bước 5: VPA cho workload ít biến động
Vertical Pod Autoscaler gợi ý hoặc tự chỉnh request. Với production, nên bắt đầu bằng chế độ recommendation:
kubectl describe vpa -n production api-service
Dùng VPA để lấy khuyến nghị, sau đó cập nhật Helm values/GitOps thay vì cho VPA tự evict pod ngay từ đầu. Với workload stateful hoặc latency-sensitive, cần PodDisruptionBudget và maintenance window.
7. Bước 6: Cluster Autoscaler và node group
Cluster Autoscaler chỉ scale node khi pod pending do thiếu tài nguyên request, và scale down khi node rảnh đủ lâu. Kiểm tra pod pending:
kubectl get pods -A --field-selector=status.phase=Pending
kubectl describe pod -n production <pod-name>
Các lỗi thường gặp:
- Pod có request quá lớn, không fit instance type nào.
- PodDisruptionBudget quá chặt khiến autoscaler không drain node.
- Local storage hoặc annotation ngăn scale down.
- Node group min size đặt quá cao so với nhu cầu thật.
Khuyến nghị: tách node group theo workload. Ví dụ on-demand cho control/critical workload, spot cho batch/worker chịu được gián đoạn, GPU riêng cho AI job.
8. Bước 7: Spot/preemptible node có kiểm soát
Spot node có thể giảm chi phí lớn nhưng không phù hợp mọi thứ. Chỉ đưa workload stateless, idempotent hoặc queue worker lên spot. Dùng taint/toleration và nodeSelector:
tolerations:
- key: "capacity"
operator: "Equal"
value: "spot"
effect: "NoSchedule"
nodeSelector:
capacity: spot
Cần có:
- PodDisruptionBudget hợp lý.
- Replica trải trên nhiều node/zone bằng topology spread constraints.
- Graceful shutdown xử lý SIGTERM.
- Queue retry hoặc idempotency cho background job.
9. Bước 8: Tối ưu storage và snapshot
Kiểm tra PVC:
kubectl get pvc -A
kubectl get pv
kubectl describe pvc -n production data-postgres-0
Các điểm hay lãng phí:
- PVC cấp 500Gi nhưng dùng 40Gi trong nhiều tháng.
- Storage class SSD/PIOPS cho workload không cần IOPS cao.
- Snapshot backup giữ quá lâu.
- Log lưu trong PVC thay vì đẩy sang hệ thống log có retention.
Giảm size PVC thường khó hơn tăng size, nên cần backup/restore hoặc migration. Đừng thu nhỏ volume production trực tiếp nếu chưa có quy trình rollback.
10. Bước 9: Network và Load Balancer
Cloud bill thường có phần network bị bỏ quên. Kiểm tra Service type LoadBalancer:
kubectl get svc -A | grep LoadBalancer
Xem Ingress:
kubectl get ingress -A
Dọn các LB không còn traffic, gom service qua ingress controller khi phù hợp, tránh route traffic nội bộ qua public endpoint. Với multi-AZ, kiểm tra cross-zone data transfer. Với NAT Gateway, workload kéo image hoặc gọi API internet quá nhiều cũng có thể làm bill tăng.
11. Bước 10: Observability cũng cần FinOps
Prometheus/logging có thể trở thành nguồn chi phí lớn. Một vài việc nên làm:
- Giảm scrape interval cho metric không quan trọng.
- Loại bỏ high-cardinality label như user_id, request_id, path động.
- Thiết lập retention theo môi trường: dev/staging ngắn hơn production.
- Chặn debug log ở production, chỉ bật theo thời gian khi incident.
- Dùng recording rules cho query nặng thay vì dashboard quét raw series liên tục.
12. Quy trình thay đổi an toàn
- Chọn 1 namespace hoặc 1 service ít rủi ro làm pilot.
- Ghi baseline: cost/ngày, CPU/memory p95, replica, latency, error rate.
- Giảm request 10–30%, bật HPA nếu phù hợp.
- Deploy qua GitOps/Helm, theo dõi ít nhất 24–72 giờ.
- Nếu ổn, mở rộng sang service tương tự.
- Ghi lại saving và incident nếu có.
13. Troubleshooting lỗi thường gặp
Pod bị Pending sau khi giảm node
Chạy kubectl describe pod. Nếu báo insufficient CPU/memory, request vẫn quá cao hoặc node group không có instance phù hợp. Nếu do taint/affinity, kiểm tra rule scheduling.
Pod OOMKilled sau khi giảm memory
Kiểm tra:
kubectl get pod -n production api-service-xxx -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}'
kubectl describe pod -n production api-service-xxx
Tăng memory limit/request, xem memory leak, hoặc tách workload batch khỏi API.
CPU throttling tăng
Nếu đặt CPU limit quá sát request, container có thể bị throttling dù node còn CPU. Với API latency-sensitive, cân nhắc bỏ CPU limit hoặc đặt limit cao hơn request nhiều lần.
Autoscaler scale lên/xuống liên tục
Kiểm tra HPA target quá nhạy, metric dao động, cooldown chưa đủ, hoặc request quá thấp làm CPU utilization % cao bất thường. Dùng stabilizationWindowSeconds trong autoscaling/v2.
14. Checklist nghiệm thu
- Đã có dashboard cost theo cluster/namespace/team.
- Đã đo usage p95/p99 trước khi chỉnh requests.
- Không giảm minReplicas dưới mức chịu lỗi tối thiểu.
- HPA/VPA/Cluster Autoscaler có metric và alert đi kèm.
- Workload critical không bị ép sang spot node.
- PDB, topology spread và readiness/liveness probe hoạt động đúng.
- Không có pod pending, OOMKilled hoặc CPU throttling bất thường sau thay đổi.
- Đã xác nhận latency, error rate, saturation không xấu đi.
- Đã ghi lại saving ước tính và rollback plan.
15. Lab thực hành
- Tạo namespace
cost-labvà deploy một ứng dụng demo với request CPU/memory cố tình cao. - Cài metrics-server, chạy
kubectl topđể xem usage thật. - Giảm request theo p95 giả lập và quan sát scheduling.
- Tạo HPA với CPU target 60–70%.
- Dùng load generator như
heyhoặck6để tạo traffic. - Ghi lại số replica, latency, CPU/memory trước và sau tối ưu.
Kết luận
Kubernetes cost optimization là một vòng lặp vận hành liên tục: đo đạc, tối ưu nhỏ, kiểm chứng, rồi chuẩn hóa thành policy. Cách làm tốt nhất không phải “cắt giảm mạnh tay” mà là đưa FinOps vào quy trình platform: requests dựa trên dữ liệu, autoscaling đúng metric, node group phù hợp workload, storage/network có owner và observability không tạo chi phí vô hình. Khi làm đúng, bạn vừa giảm bill cloud vừa tăng kỷ luật vận hành production.
