Tối Ưu Chi Phí Kubernetes 2026: Hướng Dẫn Tiết Kiệm 40-60% Trên EKS, AKS, GKE

Hướng dẫn chi tiết cách tối ưu chi phí Kubernetes trên EKS, AKS, GKE năm 2026. Bao gồm right-sizing với VPA, Karpenter, Spot Instance và OpenCost/Kubecost — tiết kiệm 40-60% chi phí mà không ảnh hưởng hiệu năng.

Mở Đầu: Kubernetes Đang "Ngốn" Tiền Của Bạn Như Thế Nào?

Kubernetes đã trở thành tiêu chuẩn de facto cho việc triển khai ứng dụng trên cloud — điều đó thì ai cũng biết rồi. Nhưng đi kèm với sự phổ biến ấy là một thực tế khá đau: phần lớn doanh nghiệp đang lãng phí từ 28% đến 40% chi phí Kubernetes của mình. Theo nghiên cứu mới nhất từ Cast AI vào đầu năm 2026, trung bình chỉ có 13% CPU được yêu cầu (requested) thực sự được sử dụng, còn memory utilization cũng chỉ đạt mức 23%.

Nói thẳng ra: cứ mỗi 100 đồng bạn chi cho Kubernetes, có tới 30-40 đồng bị "đốt" vào tài nguyên không ai đụng tới.

Với một nghiên cứu phân tích 3.042 cluster production trên 600+ công ty vào tháng 1/2026, 68% pod đang yêu cầu bộ nhớ gấp 3-8 lần so với mức thực tế sử dụng. Trung bình mỗi công ty lãng phí khoảng 847 USD/tháng chỉ riêng cho memory over-provisioning. Con số này nghe không lớn lắm cho đến khi bạn nhân nó lên theo số cluster đang chạy.

Bài viết này sẽ hướng dẫn bạn từ A đến Z cách tối ưu chi phí Kubernetes trên ba nền tảng cloud lớn nhất — Amazon EKS, Azure AKS và Google GKE — với các chiến lược đã được kiểm chứng thực tế, code mẫu sẵn sàng áp dụng, và các công cụ mới nhất năm 2026. Mình đã áp dụng nhiều cách trong số này cho các dự án thực tế và kết quả thật sự đáng ngạc nhiên.

1. Hiểu Cấu Trúc Chi Phí Kubernetes Trên Từng Cloud Provider

1.1 Ba Thành Phần Chi Phí Chính

Trước khi tối ưu bất cứ gì, bạn cần hiểu tiền đang đi đâu đã. Chi phí Kubernetes bao gồm ba lớp chính:

  • Control Plane (Mặt phẳng điều khiển): Đây là "bộ não" của cluster — bao gồm API server, etcd, scheduler. AWS EKS tính phí 0.10 USD/giờ (~73 USD/tháng). Azure AKS thì miễn phí hoàn toàn cho control plane cơ bản — và thành thật mà nói, đây là lợi thế cạnh tranh khá lớn của Azure. Google GKE miễn phí cho một cluster zonal, nhưng regional cluster sẽ tính 0.10 USD/giờ.
  • Worker Nodes (Compute): Chiếm tới 60-80% tổng chi phí. Đây là các máy ảo (EC2, Azure VM, GCE) chạy workload thực tế của bạn. Chi phí phụ thuộc vào loại instance, số lượng, và mô hình mua (On-Demand, Reserved, Spot).
  • Phụ phí khác: Networking (load balancer, data transfer giữa AZ), storage (persistent volumes), logging/monitoring, và các dịch vụ managed add-on. Phần này hay bị bỏ qua nhưng cộng lại cũng kha khá đấy.

1.2 So Sánh Chi Phí Cơ Bản EKS vs AKS vs GKE

Để bạn dễ hình dung, đây là so sánh chi phí cho một cluster điển hình với 3 worker node (4 vCPU, 16 GB RAM mỗi node) theo giá On-Demand tại khu vực Đông Nam Á:

  • Amazon EKS: Control plane ~73 USD/tháng + 3x m5.xlarge (~461 USD/tháng) = ~534 USD/tháng
  • Azure AKS: Control plane miễn phí + 3x Standard_D4s_v5 (~421 USD/tháng) = ~421 USD/tháng
  • Google GKE: Control plane ~73 USD/tháng (regional) + 3x e2-standard-4 (~388 USD/tháng) = ~461 USD/tháng

Lưu ý rằng đây chỉ là chi phí compute cơ bản thôi nhé. Chi phí thực tế sẽ cao hơn nhiều khi tính thêm networking, storage, và các dịch vụ bổ sung. Và quan trọng hơn — chi phí thấp nhất không phải lúc nào cũng là lựa chọn tốt nhất. Hãy chọn provider phù hợp với hệ sinh thái hiện tại của bạn.

2. Right-Sizing: Chiến Lược Cho Kết Quả Nhanh Nhất

2.1 Tại Sao Hầu Hết Mọi Người Đều Set Resource Requests Sai?

Theo khảo sát, 64% đội ngũ kỹ thuật thừa nhận rằng họ thêm "buffer an toàn" gấp 2-4 lần sau chỉ một lần gặp sự cố OOMKilled. Nghe quen không? Mình chắc nhiều bạn cũng từng làm vậy. Điều đáng nói là: hầu hết team đặt resource requests tại thời điểm deploy rồi... không bao giờ quay lại xem xét lại.

Kết quả? Một pod chỉ cần 128MB RAM nhưng được cấp 1GB. Nhân lên 100 pod, bạn đang lãng phí gần 90GB RAM — tương đương hàng trăm đô la mỗi tháng chỉ vì... sợ.

2.2 Sử Dụng Vertical Pod Autoscaler (VPA) Ở Chế Độ Recommendation

VPA là công cụ chính thức của Kubernetes để tự động điều chỉnh resource requests. Tuy nhiên, lời khuyên chân thành của mình là: đừng bật auto mode ngay. Hãy bắt đầu với chế độ recommendation để thu thập dữ liệu trước, sau đó review và áp dụng thủ công. Tin mình đi, bạn sẽ cảm ơn bước thận trọng này.

Cài đặt VPA trên EKS:

# Cài đặt VPA trên EKS
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.sh

# Hoặc cài qua Helm (khuyến nghị)
helm repo add cowboysysop https://cowboysysop.github.io/charts/
helm install vpa cowboysysop/vertical-pod-autoscaler   --namespace kube-system   --set recommender.enabled=true   --set updater.enabled=false   --set admissionController.enabled=false

Tạo một VPA object ở chế độ "Off" (chỉ đưa ra đề xuất, không tự động thay đổi gì cả):

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
  namespace: production
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  updatePolicy:
    updateMode: "Off"  # Chỉ recommendation, không tự động update
  resourcePolicy:
    containerPolicies:
    - containerName: my-app
      minAllowed:
        cpu: "50m"
        memory: "64Mi"
      maxAllowed:
        cpu: "2000m"
        memory: "4Gi"
      controlledResources: ["cpu", "memory"]

Sau 24-48 giờ, kiểm tra đề xuất của VPA:

# Xem VPA recommendations
kubectl get vpa my-app-vpa -n production -o jsonpath='{.status.recommendation}' | jq .

# Output mẫu:
# {
#   "containerRecommendations": [{
#     "containerName": "my-app",
#     "lowerBound": {"cpu": "50m", "memory": "131072k"},
#     "target": {"cpu": "120m", "memory": "262144k"},
#     "upperBound": {"cpu": "400m", "memory": "524288k"},
#     "uncappedTarget": {"cpu": "120m", "memory": "262144k"}
#   }]
# }

Trong ví dụ trên, VPA đề xuất target là 120m CPU và 256MB memory. Nếu pod đang được set 500m CPU và 1GB memory, bạn có thể tiết kiệm hơn 75% tài nguyên chỉ bằng cách điều chỉnh theo đề xuất. Đơn giản vậy thôi mà hiệu quả thật sự lớn.

2.3 Script Tự Động Quét Và Báo Cáo Lãng Phí

Đây là script Python mình hay dùng để quét tất cả namespace trong cluster và tìm ra các deployment đang over-provisioned. Bạn có thể copy và chạy ngay:

#!/usr/bin/env python3
"""
Script quét Kubernetes cluster để tìm workload over-provisioned.
Yêu cầu: pip install kubernetes
"""
from kubernetes import client, config
from kubernetes.client.rest import ApiException

def get_resource_waste_report():
    config.load_kube_config()
    v1 = client.CoreV1Api()
    apps_v1 = client.AppsV1Api()
    custom_api = client.CustomObjectsApi()

    report = []

    # Lấy tất cả namespace (trừ system namespaces)
    namespaces = v1.list_namespace()
    skip_ns = {"kube-system", "kube-public", "kube-node-lease"}

    for ns in namespaces.items:
        ns_name = ns.metadata.name
        if ns_name in skip_ns:
            continue

        # Lấy metrics cho các pod trong namespace
        try:
            pod_metrics = custom_api.list_namespaced_custom_object(
                group="metrics.k8s.io",
                version="v1beta1",
                namespace=ns_name,
                plural="pods"
            )
        except ApiException:
            continue

        for pod_metric in pod_metrics.get("items", []):
            pod_name = pod_metric["metadata"]["name"]

            try:
                pod = v1.read_namespaced_pod(pod_name, ns_name)
            except ApiException:
                continue

            for i, container in enumerate(pod.spec.containers):
                if not container.resources.requests:
                    continue

                requested_cpu = parse_cpu(
                    container.resources.requests.get("cpu", "0")
                )
                requested_mem = parse_memory(
                    container.resources.requests.get("memory", "0")
                )

                actual_cpu = parse_cpu(
                    pod_metric["containers"][i]["usage"]["cpu"]
                )
                actual_mem = parse_memory(
                    pod_metric["containers"][i]["usage"]["memory"]
                )

                cpu_util = (actual_cpu / requested_cpu * 100) if requested_cpu > 0 else 0
                mem_util = (actual_mem / requested_mem * 100) if requested_mem > 0 else 0

                if cpu_util < 50 or mem_util < 50:
                    report.append({
                        "namespace": ns_name,
                        "pod": pod_name,
                        "container": container.name,
                        "cpu_requested": f"{requested_cpu}m",
                        "cpu_actual": f"{actual_cpu}m",
                        "cpu_utilization": f"{cpu_util:.1f}%",
                        "mem_requested": f"{requested_mem}Mi",
                        "mem_actual": f"{actual_mem}Mi",
                        "mem_utilization": f"{mem_util:.1f}%",
                    })

    report.sort(key=lambda x: float(x["cpu_utilization"].rstrip("%")))

    print(f"
{'='*80}")
    print(f"BÁO CÁO LÃNG PHÍ TÀI NGUYÊN KUBERNETES")
    print(f"{'='*80}")
    print(f"Tổng số container over-provisioned: {len(report)}")
    print(f"{'='*80}
")

    for item in report[:20]:
        print(f"Namespace: {item['namespace']}")
        print(f"  Pod: {item['pod']} | Container: {item['container']}")
        print(f"  CPU: {item['cpu_requested']} requested -> "
              f"{item['cpu_actual']} used ({item['cpu_utilization']})")
        print(f"  Memory: {item['mem_requested']} requested -> "
              f"{item['mem_actual']} used ({item['mem_utilization']})")
        print()

def parse_cpu(value):
    if value.endswith("n"):
        return int(value[:-1]) / 1_000_000
    elif value.endswith("m"):
        return int(value[:-1])
    return int(value) * 1000

def parse_memory(value):
    units = {"Ki": 1/1024, "Mi": 1, "Gi": 1024, "Ti": 1048576}
    for unit, multiplier in units.items():
        if value.endswith(unit):
            return float(value[:-len(unit)]) * multiplier
    return float(value) / (1024 * 1024)

if __name__ == "__main__":
    get_resource_waste_report()

3. Autoscaling Thông Minh: HPA, VPA Và Karpenter

3.1 Horizontal Pod Autoscaler (HPA) — Làm Đúng Cách

HPA tự động tăng giảm số lượng pod dựa trên metric. Nhưng thực tế là nhiều team mắc lỗi khi cấu hình HPA — dẫn đến scaling quá chậm hoặc flapping (cứ scale up rồi lại scale down liên tục, rất phiền). Đây là cấu hình HPA tối ưu mà mình khuyến nghị:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60  # Scale khi CPU vượt 60%
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 70
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60   # Chờ 60s trước khi scale up
      policies:
      - type: Percent
        value: 50                       # Tăng tối đa 50% mỗi lần
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300   # Chờ 5 phút trước khi scale down
      policies:
      - type: Percent
        value: 25                       # Giảm tối đa 25% mỗi lần
        periodSeconds: 120

Một lưu ý quan trọng: Đừng chạy VPA và HPA trên cùng một metric (ví dụ cả hai đều dùng CPU). Đây là anti-pattern nổi tiếng sẽ tạo ra feedback loop gây mất ổn định hệ thống. Thay vào đó, hãy dùng VPA cho CPU/memory requests, và HPA cho custom metrics như request rate, queue depth, v.v.

3.2 Karpenter: Tương Lai Của Node Autoscaling

Nếu bạn đang dùng AWS EKS, thì Karpenter thật sự là một game-changer. Khác với Cluster Autoscaler truyền thống (vốn làm việc với node group cố định), Karpenter tự động chọn instance type tối ưu nhất cho pending pod, khởi động node trong 30-60 giây (so với vài phút của Cluster Autoscaler), và liên tục consolidate node để giảm lãng phí.

Minh chứng thực tế đáng chú ý: Salesforce đã migrate hơn 1.000 EKS cluster từ Cluster Autoscaler sang Karpenter vào đầu năm 2026, đạt mức tiết kiệm khoảng 5% trong FY2026 và dự kiến thêm 5-10% trong FY2027. Operational overhead giảm khoảng 80% nhờ tự động hóa thay thế quản lý node group thủ công. Những con số đó khó mà bỏ qua được.

Cấu hình Karpenter NodePool tối ưu cho chi phí:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: cost-optimized
spec:
  template:
    metadata:
      labels:
        workload-type: general
    spec:
      requirements:
      - key: kubernetes.io/arch
        operator: In
        values: ["amd64"]
      - key: karpenter.sh/capacity-type
        operator: In
        values: ["spot", "on-demand"]  # Ưu tiên Spot
      - key: karpenter.k8s.aws/instance-category
        operator: In
        values: ["c", "m", "r"]         # Cho phép nhiều instance family
      - key: karpenter.k8s.aws/instance-generation
        operator: Gt
        values: ["4"]                    # Chỉ dùng gen 5+
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 60s               # Consolidate node rỗi sau 60s
  limits:
    cpu: "200"
    memory: "800Gi"
  weight: 10                             # Ưu tiên NodePool này
---
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
  blockDeviceMappings:
  - deviceName: /dev/xvda
    ebs:
      volumeSize: 50Gi
      volumeType: gp3
      deleteOnTermination: true

Với cấu hình trên, Karpenter sẽ tự động ưu tiên Spot instance (tiết kiệm đến 70-90%), chọn instance type phù hợp nhất với kích thước pod, và consolidate các node có utilization thấp bằng cách di chuyển pod sang node khác rồi terminate node cũ. Khá thông minh phải không?

3.3 GKE Autopilot — Lựa Chọn "Zero Waste" Cho Google Cloud

Nếu bạn dùng Google Cloud, GKE Autopilot là lựa chọn đáng xem xét nghiêm túc. Khác với GKE Standard nơi bạn phải quản lý node, Autopilot tính phí theo pod resource requests — nghĩa là bạn chỉ trả tiền cho tài nguyên mà pod yêu cầu, không phải cho toàn bộ node.

# Tạo GKE Autopilot cluster
gcloud container clusters create-auto my-cluster   --region=asia-southeast1   --release-channel=regular

# Deploy workload — GKE Autopilot tự quản lý node
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: my-app:latest
        resources:
          requests:
            cpu: "250m"
            memory: "512Mi"
          limits:
            cpu: "500m"
            memory: "1Gi"
EOF

Với Autopilot, việc right-sizing resource requests trở nên cực kỳ quan trọng vì bạn trả tiền chính xác theo requests. Kết hợp với VPA recommendation mode, bạn có thể đạt mức tối ưu gần như lý tưởng.

4. Spot Instance Trên Kubernetes: Tiết Kiệm 60-90% Chi Phí Compute

4.1 Nguyên Tắc Vàng Khi Dùng Spot

Spot Instance (AWS), Spot VMs (Azure), và Preemptible/Spot VMs (GCP) cung cấp compute với giá giảm 60-90% so với On-Demand. Đổi lại, chúng có thể bị thu hồi bất cứ lúc nào khi cloud provider cần capacity.

Nghe đáng sợ đúng không? Nhưng với Kubernetes, việc xử lý Spot interruption trở nên dễ dàng hơn rất nhiều nhờ cơ chế rescheduling tự động. Mình đã chạy production workload trên Spot hơn một năm nay và (gõ lên gỗ) chưa gặp sự cố nghiêm trọng nào.

Nguyên tắc sử dụng Spot hiệu quả trên Kubernetes:

  • Đa dạng hóa instance type: Sử dụng nhiều loại instance khác nhau để giảm rủi ro bị thu hồi đồng loạt. Ví dụ: thay vì chỉ dùng m5.xlarge, hãy cho phép m5.xlarge, m5a.xlarge, m5d.xlarge, m6i.xlarge.
  • Duy trì On-Demand baseline: Luôn giữ một số node On-Demand cho critical workload. Tỷ lệ khuyến nghị: 20-30% On-Demand, 70-80% Spot.
  • Xử lý graceful shutdown: Cấu hình terminationGracePeriodSeconds phù hợp và sử dụng preStop hooks để xử lý dữ liệu trước khi pod bị terminate.
  • Tách biệt workload: Dùng node selector và taints/tolerations để đảm bảo workload phù hợp mới chạy trên Spot node.

4.2 Cấu Hình Spot Node Pool Trên EKS Với Karpenter

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: spot-workloads
spec:
  template:
    spec:
      requirements:
      - key: karpenter.sh/capacity-type
        operator: In
        values: ["spot"]
      - key: karpenter.k8s.aws/instance-category
        operator: In
        values: ["c", "m", "r"]
      - key: karpenter.k8s.aws/instance-size
        operator: In
        values: ["large", "xlarge", "2xlarge"]
      taints:
      - key: spot
        value: "true"
        effect: NoSchedule
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 30s
  limits:
    cpu: "100"
---
# Deployment sử dụng Spot nodes
apiVersion: apps/v1
kind: Deployment
metadata:
  name: batch-processor
spec:
  replicas: 5
  selector:
    matchLabels:
      app: batch-processor
  template:
    metadata:
      labels:
        app: batch-processor
    spec:
      tolerations:
      - key: spot
        operator: Equal
        value: "true"
        effect: NoSchedule
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: karpenter.sh/capacity-type
                operator: In
                values: ["spot"]
      terminationGracePeriodSeconds: 120
      containers:
      - name: processor
        image: batch-processor:latest
        resources:
          requests:
            cpu: "500m"
            memory: "1Gi"
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 30 && /app/graceful-shutdown.sh"]

4.3 Cấu Hình Spot Node Pool Trên AKS

# Thêm Spot node pool vào AKS cluster
az aks nodepool add   --resource-group myResourceGroup   --cluster-name myAKSCluster   --name spotnodepool   --priority Spot   --eviction-policy Delete   --spot-max-price -1   --node-count 3   --min-count 1   --max-count 10   --enable-cluster-autoscaler   --node-vm-size Standard_D4s_v5   --labels workload-type=batch   --node-taints "kubernetes.azure.com/scalesetpriority=spot:NoSchedule"

5. Giám Sát Chi Phí Với OpenCost Và Kubecost

5.1 OpenCost — Tiêu Chuẩn Mở Từ CNCF

OpenCost là dự án mã nguồn mở được CNCF hỗ trợ, cung cấp khả năng giám sát chi phí Kubernetes theo namespace, workload, và label. Nếu bạn chưa có giải pháp cost monitoring nào, đây là điểm khởi đầu tuyệt vời — miễn phí mà lại khá mạnh.

# Cài đặt OpenCost trên cluster
helm install opencost opencost/opencost   --namespace opencost   --create-namespace   --set opencost.exporter.defaultClusterId="my-production-cluster"   --set opencost.ui.enabled=true

# Truy vấn chi phí theo namespace qua API
curl -s http://localhost:9090/allocation/compute   -d window=7d   -d aggregate=namespace   -d accumulate=true | jq '.data[] | {
    namespace: .name,
    totalCost: .totalCost,
    cpuCost: .cpuCost,
    memCost: .ramCost,
    efficiency: .totalEfficiency
  }'

5.2 Kubecost — Giải Pháp Toàn Diện Hơn

Kubecost xây dựng trên nền tảng OpenCost nhưng bổ sung nhiều tính năng enterprise đáng giá: multi-cluster view, tích hợp với billing API của cloud provider để reconcile chi phí thực tế (bao gồm Reserved Instance, Savings Plans), và đề xuất tối ưu tự động. Nếu công ty bạn đủ lớn, đầu tư vào Kubecost rất đáng.

# Cài đặt Kubecost
helm install kubecost cost-analyzer   --repo https://kubecost.github.io/cost-analyzer/   --namespace kubecost   --create-namespace   --set kubecostToken="your-token"   --set prometheus.server.global.external_labels.cluster_id="my-cluster"

Tạo Prometheus alert cho chi phí bất thường (bạn sẽ cảm ơn mình khi nó bắt được spike chi phí lúc 3 giờ sáng):

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: kubecost-cost-alerts
  namespace: kubecost
spec:
  groups:
  - name: cost-alerts
    rules:
    - alert: NamespaceCostSpike
      expr: |
        sum(kubecost_cluster_costs{}) by (namespace)
        > 1.5 * avg_over_time(
          sum(kubecost_cluster_costs{}) by (namespace)[7d:1h]
        )
      for: 2h
      labels:
        severity: warning
      annotations:
        summary: "Chi phí namespace tăng đột biến"
        description: "Chi phí hiện tại cao hơn 50% so với trung bình 7 ngày"
    - alert: LowResourceEfficiency
      expr: |
        kubecost_container_cpu_usage_avg / kubecost_container_cpu_request_avg < 0.3
      for: 24h
      labels:
        severity: info
      annotations:
        summary: "Container sử dụng CPU dưới 30%"

5.3 Xây Dựng Dashboard Chi Phí Với Grafana

Kết hợp OpenCost hoặc Kubecost với Grafana để tạo dashboard chi phí trực quan cho team. Theo kinh nghiệm của mình, khi mọi người nhìn thấy số tiền thực tế trên dashboard, họ tự giác tối ưu hơn rất nhiều. Đây là một số query PromQL hữu ích:

# Tổng chi phí theo namespace (24h qua)
sum(rate(kubecost_allocation_cpu_cost_total[24h])) by (namespace)
+ sum(rate(kubecost_allocation_memory_cost_total[24h])) by (namespace)

# CPU utilization efficiency theo deployment
sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (namespace, pod)
/
sum(kube_pod_container_resource_requests{resource="cpu"}) by (namespace, pod)
* 100

# Top 10 namespace tốn kém nhất
topk(10,
  sum(kubecost_allocation_cpu_cost_total + kubecost_allocation_memory_cost_total)
  by (namespace)
)

6. Cost Allocation Và Chargeback — Ai Đang Trả Tiền Cho Cái Gì?

6.1 Chiến Lược Label Cho Cost Allocation

Kubernetes labels là công cụ mạnh mẽ nhất để phân bổ chi phí. Nhưng chúng chỉ hiệu quả khi được áp dụng nhất quán trên toàn cluster. Nghe thì dễ, nhưng thực tế thì... đây là một trong những thứ khó enforce nhất trong tổ chức lớn. Đây là cấu trúc label mình khuyến nghị:

metadata:
  labels:
    # Bắt buộc - dùng cho cost allocation
    app.kubernetes.io/name: "payment-service"
    app.kubernetes.io/component: "api"
    cost.company.com/team: "platform-engineering"
    cost.company.com/project: "checkout-v2"
    cost.company.com/environment: "production"
    cost.company.com/cost-center: "CC-PLATFORM-2026"

    # Tùy chọn - dùng cho reporting chi tiết
    cost.company.com/business-unit: "e-commerce"
    cost.company.com/tier: "critical"

6.2 Bắt Buộc Label Bằng OPA Gatekeeper

Để đảm bảo mọi workload đều có label chi phí (vì con người thì hay quên), sử dụng OPA Gatekeeper hoặc Kyverno để enforce policy:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
  - target: admission.k8s.gatekeeper.sh
    rego: |
      package k8srequiredlabels
      violation[{"msg": msg}] {
        provided := {label | input.review.object.metadata.labels[label]}
        required := {label | label := input.parameters.labels[_]}
        missing := required - provided
        count(missing) > 0
        msg := sprintf("Pod thiếu label bắt buộc: %v", [missing])
      }
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-cost-labels
spec:
  match:
    kinds:
    - apiGroups: ["apps"]
      kinds: ["Deployment", "StatefulSet", "DaemonSet"]
    namespaceSelector:
      matchExpressions:
      - key: "kubernetes.io/metadata.name"
        operator: NotIn
        values: ["kube-system", "kube-public", "monitoring"]
  parameters:
    labels:
    - "cost.company.com/team"
    - "cost.company.com/project"
    - "cost.company.com/environment"

7. Tối Ưu Networking Và Storage — Chi Phí "Ẩn" Hay Bị Bỏ Qua

7.1 Giảm Chi Phí Data Transfer Giữa AZ

Trên AWS, data transfer giữa các Availability Zone tính phí 0.01 USD/GB mỗi chiều (0.02 USD/GB cho cả hai chiều). Con số này nghe nhỏ xíu, nhưng với microservices giao tiếp liên tục qua lại, chi phí có thể lên hàng ngàn USD mỗi tháng. Mình từng thấy một hệ thống tốn hơn 2.000 USD/tháng chỉ riêng cho cross-AZ traffic.

Giải pháp: sử dụng topology-aware routing để ưu tiên traffic trong cùng AZ:

apiVersion: v1
kind: Service
metadata:
  name: my-service
  annotations:
    service.kubernetes.io/topology-mode: Auto
spec:
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080

Bạn cũng nên cân nhắc sử dụng pod topology spread constraints để phân bổ pod đều trên các AZ, giảm thiểu cross-AZ traffic:

spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: ScheduleAnyway
    labelSelector:
      matchLabels:
        app: my-app

7.2 Tối Ưu Persistent Volume

Storage trên Kubernetes cũng là nguồn lãng phí đáng kể mà nhiều người không để ý. Một số best practices:

  • Dùng đúng loại storage: gp3 trên AWS rẻ hơn gp2 tới 20% và có IOPS baseline cao hơn. Chuyển từ gp2 sang gp3 gần như không có lý do gì để không làm. Trên Azure, Premium SSD v2 cho phép điều chỉnh IOPS và throughput độc lập.
  • Resize thay vì tạo mới: Kubernetes hỗ trợ PVC expansion — tăng kích thước volume mà không cần tạo lại.
  • Xóa PV không sử dụng: PersistentVolume với reclaimPolicy: Retain sẽ không bị xóa khi PVC bị delete. Nhớ quét và xóa các PV orphaned định kỳ nhé.
# Tìm PV không có PVC (orphaned)
kubectl get pv --no-headers | awk '$5 == "Released" || $5 == "Available" {print $1, $2, $5, $6}'

# Liệt kê tất cả PVC và dung lượng
kubectl get pvc --all-namespaces -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,SIZE:.spec.resources.requests.storage,STATUS:.status.phase --sort-by='.spec.resources.requests.storage'

8. Tự Động Hóa Tối Ưu Chi Phí Với GitOps

8.1 Tích Hợp Cost Check Vào CI/CD Pipeline

Một trong những cách hiệu quả nhất để ngăn chặn lãng phí là kiểm tra chi phí ngay từ khâu code review. Đừng đợi đến khi bill đến rồi mới giật mình. Bạn hoàn toàn có thể tích hợp cost estimation vào CI/CD pipeline:

# .github/workflows/cost-check.yml
name: Kubernetes Cost Check
on:
  pull_request:
    paths:
    - 'k8s/**'
    - 'helm/**'

jobs:
  cost-estimate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4

    - name: Estimate resource costs
      run: |
        # Parse tất cả Kubernetes manifests
        for file in $(find k8s/ -name "*.yaml" -o -name "*.yml"); do
          cpu=$(yq e '.spec.template.spec.containers[].resources.requests.cpu' "$file" 2>/dev/null)
          mem=$(yq e '.spec.template.spec.containers[].resources.requests.memory' "$file" 2>/dev/null)
          replicas=$(yq e '.spec.replicas // 1' "$file" 2>/dev/null)

          if [ "$cpu" != "null" ] && [ -n "$cpu" ]; then
            echo "File: $file | CPU: $cpu x $replicas | Memory: $mem x $replicas"
          fi
        done

8.2 Scheduled Right-Sizing Review

Tạo CronJob trong cluster để tự động gửi báo cáo right-sizing hàng tuần. Cái hay của cách này là bạn không cần nhớ phải check — nó tự nhắc bạn:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: weekly-cost-report
  namespace: monitoring
spec:
  schedule: "0 9 * * 1"  # Mỗi thứ Hai lúc 9h sáng
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: cost-reporter
          containers:
          - name: reporter
            image: curlimages/curl:latest
            command:
            - /bin/sh
            - -c
            - |
              REPORT=$(curl -s http://kubecost.kubecost:9090/savings                 -d window=7d)
              curl -X POST "$SLACK_WEBHOOK_URL"                 -H "Content-Type: application/json"                 -d "{"text": "*Báo Cáo Chi Phí Kubernetes Tuần*
$REPORT"}"
            env:
            - name: SLACK_WEBHOOK_URL
              valueFrom:
                secretKeyRef:
                  name: slack-secrets
                  key: webhook-url
          restartPolicy: OnFailure

9. Checklist Tối Ưu Chi Phí Kubernetes — Bắt Đầu Từ Đâu?

Okay, nhiều thông tin quá đúng không? Đừng lo, đây là checklist được sắp xếp theo thứ tự tác động từ cao đến thấp, giúp bạn biết nên bắt đầu từ đâu:

Tuần 1-2: Quick Wins (Tiết kiệm 20-30%)

  1. Cài đặt OpenCost hoặc Kubecost để có visibility về chi phí hiện tại
  2. Triển khai VPA ở chế độ recommendation cho tất cả workload production
  3. Xác định và xóa các resource không sử dụng: orphaned PV, idle Load Balancer, unused namespaces
  4. Chuyển EBS volume từ gp2 sang gp3 (tiết kiệm 20% ngay lập tức — đây là quick win dễ nhất)

Tuần 3-4: Autoscaling (Tiết kiệm thêm 15-25%)

  1. Áp dụng VPA recommendations để right-size resource requests
  2. Cấu hình HPA cho stateless workload với behavior policy phù hợp
  3. Triển khai Karpenter (EKS) hoặc cấu hình Cluster Autoscaler tối ưu
  4. Bật topology-aware routing để giảm cross-AZ data transfer

Tháng 2: Spot Và Commitment (Tiết kiệm thêm 20-40%)

  1. Thêm Spot node pool cho workload fault-tolerant
  2. Mua Reserved Instances hoặc Savings Plans cho On-Demand baseline
  3. Thiết lập cost allocation labels và enforce bằng OPA Gatekeeper
  4. Tích hợp cost check vào CI/CD pipeline

Tháng 3+: Văn Hóa FinOps (Duy trì và cải thiện liên tục)

  1. Thiết lập weekly cost review meeting
  2. Tạo chargeback/showback report cho từng team
  3. Đặt budget alerts và anomaly detection
  4. Đánh giá GKE Autopilot hoặc các giải pháp serverless container (Fargate, Azure Container Apps) cho workload phù hợp

Kết Luận

Tối ưu chi phí Kubernetes không phải là việc làm một lần rồi xong. Thật ra thì đó là một hành trình liên tục đòi hỏi sự kết hợp giữa công cụ phù hợp, quy trình rõ ràng, và (cái này quan trọng nhất) văn hóa FinOps trong tổ chức.

Tin tốt là với những chiến lược trong bài viết này — từ right-sizing với VPA, autoscaling với Karpenter, tận dụng Spot instance, đến giám sát bằng OpenCost/Kubecost — bạn hoàn toàn có thể cắt giảm 40-60% chi phí Kubernetes mà không ảnh hưởng đến hiệu năng ứng dụng.

Hãy bắt đầu từ những quick wins nhỏ, đo lường kết quả, và dần mở rộng phạm vi tối ưu. Chỉ riêng việc cài OpenCost và chạy VPA recommendation mode trong tuần đầu tiên, bạn đã có thể phát hiện ra hàng trăm — thậm chí hàng ngàn — USD đang bị lãng phí mỗi tháng. Và tin mình đi, đó mới chỉ là khởi đầu thôi.

Về Tác Giả Editorial Team

Our team of expert writers and editors.