Skip to main content

EKS Cost Optimization

Phần này trình bày các chiến lược tối ưu chi phí khi vận hành EKS: từ lựa chọn compute (Spot, Graviton, Fargate), right-sizing resource, đến các công cụ giám sát và phân bổ chi phí.


📌 1. Tổng quan chi phí EKS

1.1. Cấu trúc chi phí

Thành phầnChi phíGhi chú
EKS Control Plane$0.10/giờ (~$73/tháng)Cố định, không phụ thuộc số Node
EC2 Worker NodesTheo instance typePhần lớn chi phí nằm ở đây
EBS VolumesTheo GB + IOPSPersistentVolume cho stateful workload
NAT Gateway$0.045/giờ + data transferTốn kém nếu traffic outbound lớn
Load BalancerALB ~$22/tháng + LCUMỗi Ingress/Service tạo một LB
CloudWatch / LoggingTheo data ingestedDễ bị phình nếu không kiểm soát
Data TransferTheo GB cross-AZ, cross-RegionThường bị bỏ quên

1.2. Nguyên tắc tối ưu

  1. Right-size - Chọn đúng lượng resource cần thiết
  2. Right-type - Chọn đúng loại compute (Spot, Graviton, Fargate)
  3. Right-time - Scale theo nhu cầu, tắt khi không dùng
  4. Visibility - Theo dõi chi phí liên tục, phân bổ theo team/service

💰 2. Tối ưu Compute (EC2 Worker Nodes)

2.1. Spot Instances

Spot Instances giúp tiết kiệm lên đến 90% so với On-Demand. Phù hợp cho workload stateless, fault-tolerant.

Cấu hình Managed Node Group với Spot:

resource "aws_eks_node_group" "spot" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "spot-workers"
node_role_arn = aws_iam_role.node.arn
subnet_ids = var.private_subnet_ids

capacity_type = "SPOT"
instance_types = ["m5.large", "m5a.large", "m5d.large", "m6i.large"]

scaling_config {
desired_size = 3
max_size = 10
min_size = 1
}

labels = {
"capacity-type" = "spot"
}

taint {
key = "spot"
value = "true"
effect = "NO_SCHEDULE"
}
}

Schedule workload lên Spot nodes:

apiVersion: apps/v1
kind: Deployment
metadata:
name: batch-processor
spec:
replicas: 5
template:
spec:
tolerations:
- key: "spot"
operator: "Equal"
value: "true"
effect: "NoSchedule"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: capacity-type
operator: In
values:
- spot
containers:
- name: processor
image: my-batch-app:latest
resources:
requests:
cpu: 500m
memory: 512Mi

Xử lý Spot Interruption:

Spot Instance có thể bị thu hồi bất cứ lúc nào (cảnh báo trước 2 phút). Cần cài AWS Node Termination Handler:

helm repo add eks https://aws.github.io/eks-charts
helm install aws-node-termination-handler eks/aws-node-termination-handler \
--namespace kube-system \
--set enableSpotInterruptionDraining=true \
--set enableScheduledEventDraining=true
Best Practices cho Spot
  • Luôn khai báo nhiều instance types (ít nhất 4-5 loại) để tăng khả năng có capacity.
  • Dùng topologySpreadConstraints để spread Pod qua nhiều AZ.
  • Không dùng Spot cho stateful workload (database, message queue).
  • Kết hợp On-Demand (baseline) + Spot (burst traffic).

2.2. Graviton (ARM) Instances

Graviton instances (C6g, M6g, R6g, M7g,...) tiết kiệm ~20% chi phí và hiệu năng tốt hơn so với x86 cùng tier.

Cấu hình Node Group Graviton:

resource "aws_eks_node_group" "graviton" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "graviton-workers"
node_role_arn = aws_iam_role.node.arn
subnet_ids = var.private_subnet_ids

capacity_type = "ON_DEMAND"
instance_types = ["m7g.large", "m6g.large"]
ami_type = "AL2023_ARM_64_STANDARD"

scaling_config {
desired_size = 2
max_size = 8
min_size = 1
}

labels = {
"arch" = "arm64"
}
}

Schedule workload lên Graviton nodes:

spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values:
- arm64
Lưu ý khi dùng Graviton
  • Container image phải hỗ trợ multi-arch (amd64 + arm64).
  • Build image với docker buildx build --platform linux/amd64,linux/arm64.
  • Kiểm tra tất cả dependencies (native libraries) tương thích ARM.

2.3. Karpenter Consolidation

Karpenter tự động gom Pod từ nhiều Node underutilized về ít Node hơn, tiết kiệm chi phí (xem thêm bài Scaling).

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: cost-optimized
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
- key: kubernetes.io/arch
operator: In
values: ["amd64", "arm64"]
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 30s
limits:
cpu: 100
memory: 400Gi

2.4. Fargate cho workload nhỏ

Fargate tính phí theo vCPU + Memory thực dùng (tính theo giây). Phù hợp cho workload nhỏ, không muốn quản lý Node.

resource "aws_eks_fargate_profile" "batch" {
cluster_name = aws_eks_cluster.main.name
fargate_profile_name = "batch-jobs"
pod_execution_role_arn = aws_iam_role.fargate.arn
subnet_ids = var.private_subnet_ids

selector {
namespace = "batch"
labels = {
compute = "fargate"
}
}
}
So sánhEC2Fargate
Quản lý NodeTự quản lýAWS quản lý
Chi phíRẻ hơn khi utilization caoRẻ hơn khi workload nhỏ/burst
Spot support
DaemonSet
GPU

📐 3. Right-sizing Resources

3.1. Vấn đề over-provisioning

Phần lớn cluster EKS bị over-provisioned — request nhiều CPU/Memory hơn thực dùng. Điều này gây lãng phí vì:

  • Kubernetes schedule Pod dựa trên requests (không phải actual usage)
  • Node phải đủ lớn để chứa tổng requests của tất cả Pod
  • Thực tế Pod chỉ dùng 10-30% resource đã request

3.2. Dùng VPA Recommender

Cài VPA ở mode Off để thu thập recommendation mà không tự động thay đổi (xem thêm bài Scaling):

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Off"

Xem recommendation:

kubectl describe vpa my-app-vpa

3.3. Dùng Kubecost

Kubecost là công cụ phân tích chi phí K8s phổ biến, giúp hiển thị chi phí theo namespace, deployment, label.

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"

Kubecost cung cấp:

  • Chi phí theo namespace, deployment, Pod
  • Right-sizing recommendations
  • Savings recommendations (idle resources)
  • Allocation reports cho chargeback

3.4. Prometheus metrics hữu ích

Một số PromQL queries để phát hiện over-provisioning:

CPU request vs actual usage:

# Tỷ lệ CPU thực dùng / CPU request (theo namespace)
sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (namespace)
/
sum(kube_pod_container_resource_requests{resource="cpu"}) by (namespace)

Memory request vs actual usage:

# Tỷ lệ Memory thực dùng / Memory request (theo namespace)
sum(container_memory_working_set_bytes{container!=""}) by (namespace)
/
sum(kube_pod_container_resource_requests{resource="memory"}) by (namespace)

Nếu tỷ lệ < 0.3 (30%), workload đang bị over-provisioned.


🌐 4. Tối ưu Networking Cost

4.1. Giảm chi phí NAT Gateway

NAT Gateway tốn $0.045/giờ + $0.045/GB data processed. Với traffic lớn, đây là khoản chi phí đáng kể.

Giải pháp:

  • Dùng VPC Endpoints cho AWS services (S3, ECR, CloudWatch, STS,...) — miễn phí data transfer
resource "aws_vpc_endpoint" "s3" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.ap-southeast-1.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = var.private_route_table_ids
}

resource "aws_vpc_endpoint" "ecr_api" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.ap-southeast-1.ecr.api"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
security_group_ids = [var.vpc_endpoint_sg_id]
private_dns_enabled = true
}

VPC Endpoints cần thiết cho EKS:

ServiceTypeLý do
S3GatewayECR image layers, logs
ECR (api + dkr)InterfacePull container images
STSInterfaceIRSA authentication
CloudWatch LogsInterfaceLogging
EC2InterfaceKarpenter provisioning

4.2. Giảm chi phí Cross-AZ traffic

Cross-AZ data transfer tốn $0.01/GB mỗi chiều. Với microservices gọi nhau nhiều, chi phí này tích lũy nhanh.

Giải pháp:

  • Dùng topologySpreadConstraints kết hợp topology-aware routing:
apiVersion: v1
kind: Service
metadata:
name: my-service
annotations:
service.kubernetes.io/topology-mode: Auto
spec:
selector:
app: my-app
ports:
- port: 80

Khi bật topology-aware routing, traffic ưu tiên gửi đến Pod cùng AZ trước.

4.3. Giảm chi phí Load Balancer

Mỗi LoadBalancer Service tạo một NLB/ALB riêng (~$22/tháng). Với nhiều services, chi phí nhân lên.

Giải pháp: Dùng IngressGroup để share một ALB cho nhiều services:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-a
annotations:
alb.ingress.kubernetes.io/group.name: shared-alb
spec:
rules:
- host: app-a.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-a-svc
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-b
annotations:
alb.ingress.kubernetes.io/group.name: shared-alb
spec:
rules:
- host: app-b.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-b-svc
port:
number: 80

📉 5. Tối ưu Storage Cost

5.1. Chọn đúng EBS Volume Type

Volume TypeChi phíIOPSPhù hợp
gp3$0.08/GB3000 baseline (free)Đa số workload
gp2$0.10/GB3 IOPS/GBLegacy, nên chuyển sang gp3
io2$0.125/GB + IOPSLên đến 64,000Database high-performance
st1$0.045/GBThroughput optimizedLog storage, batch

Luôn dùng gp3 thay gp2 — rẻ hơn 20% và IOPS cao hơn:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3
provisioner: ebs.csi.aws.com
parameters:
type: gp3
fsType: ext4
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

5.2. Dọn dẹp PVC không sử dụng

# Liệt kê PVC Bound nhưng không được Pod nào tham chiếu
pods_json="$(kubectl get pods --all-namespaces -o json)"

kubectl get pvc --all-namespaces -o json | \
jq -r --argjson pods "$pods_json" '
($pods.items
| map(. as $p
| $p.spec.volumes[]?
| select(has("persistentVolumeClaim"))
| "\($p.metadata.namespace)/\(.persistentVolumeClaim.claimName)")
| unique) as $used
| .items[]
| select(.status.phase == "Bound")
| "\(.metadata.namespace)/\(.metadata.name)" as $pvc
| select(($used | index($pvc)) == null)
| "\($pvc) - \(.spec.resources.requests.storage // "N/A")"
'

5.3. EFS vs EBS

Tiêu chíEBSEFS
Access modeReadWriteOnceReadWriteMany
Chi phí$0.08/GB (gp3)$0.30/GB (Standard)
PerformanceCao, consistentTùy throughput mode
Phù hợpDatabase, single PodShared storage, multi-Pod

Dùng EFS Infrequent Access cho data ít truy cập — tiết kiệm ~92%:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: efs-ia
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap
fileSystemId: fs-xxxxxxxx
directoryPerms: "700"
basePath: "/dynamic"
# Tự động chuyển file sang IA sau 30 ngày không truy cập
uid: "1000"
gid: "1000"

⏰ 6. Scheduling & Off-hours Savings

6.1. Scale-to-zero ngoài giờ làm việc

Với môi trường dev/staging, có thể tắt hoàn toàn ngoài giờ:

Dùng KEDA CronScaler:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: office-hours-scaler
spec:
scaleTargetRef:
name: my-dev-app
minReplicaCount: 0
maxReplicaCount: 3
triggers:
- type: cron
metadata:
timezone: Asia/Ho_Chi_Minh
start: 0 8 * * 1-5
end: 0 19 * * 1-5
desiredReplicas: "3"

Ngoài giờ 8h-19h (thứ 2 - thứ 6), Pod scale về 0 — tiết kiệm ~65% chi phí dev/staging.

6.2. Tắt Node Group dev/staging

# Tắt cuối ngày
aws eks update-nodegroup-config \
--cluster-name dev-eks \
--nodegroup-name dev-workers \
--scaling-config minSize=0,maxSize=0,desiredSize=0

# Bật đầu ngày
aws eks update-nodegroup-config \
--cluster-name dev-eks \
--nodegroup-name dev-workers \
--scaling-config minSize=2,maxSize=5,desiredSize=2

💵 7. Savings Plans & Reserved Instances

7.1. Compute Savings Plans

Cam kết sử dụng compute trong 1 hoặc 3 năm, tiết kiệm lên đến 66%:

LoạiFlexibilityTiết kiệm
Compute Savings PlansMọi instance family, region, OS~40-50%
EC2 Instance Savings PlansCố định instance family + region~50-60%
Reserved InstancesCố định instance type + AZ~55-66%

Khuyến nghị:

  • Dùng Compute Savings Plans cho baseline capacity (On-Demand nodes luôn chạy)
  • Phần burst traffic dùng Spot Instances

7.2. Tính toán Savings Plans

Ví dụ: Cluster chạy 5x m6i.xlarge On-Demand 24/7

On-Demand: 5 × $0.192/giờ × 730 giờ = $700.8/tháng
Savings Plans (1yr, No Upfront): 5 × $0.121/giờ × 730 giờ = $441.65/tháng
Tiết kiệm: $259.15/tháng (~37%)

Savings Plans (3yr, All Upfront): 5 × $0.076/giờ × 730 giờ = $277.4/tháng
Tiết kiệm: $423.4/tháng (~60%)

📊 8. Công cụ theo dõi chi phí

8.1. AWS Cost Explorer

  • Xem chi phí theo service, tag, thời gian
  • Tạo Cost Allocation Tags để phân bổ chi phí theo team/project

Tag resources cho cost tracking:

resource "aws_eks_node_group" "workers" {
# ...
tags = {
Environment = "production"
Team = "backend"
Project = "order-service"
CostCenter = "CC-1234"
}
}

8.2. Kubecost

Kubecost hiển thị chi phí bên trong Kubernetes — theo namespace, deployment, label:

helm install kubecost kubecost/cost-analyzer \
--namespace kubecost \
--create-namespace \
--set kubecostToken="your-token" \
--set prometheus.server.persistentVolume.size=32Gi

8.3. AWS Budgets

Tạo budget alert để cảnh báo khi chi phí vượt ngưỡng:

resource "aws_budgets_budget" "eks_monthly" {
name = "eks-monthly-budget"
budget_type = "COST"
limit_amount = "1000"
limit_unit = "USD"
time_unit = "MONTHLY"

cost_filter {
name = "Service"
values = ["Amazon Elastic Kubernetes Service"]
}

notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "FORECASTED"
subscriber_email_addresses = ["devops-team@example.com"]
}
}

🏗️ 9. Chiến lược tối ưu tổng thể

Quick Wins (thực hiện ngay)

Hành độngTiết kiệm ước tínhĐộ khó
Chuyển gp2 → gp3~20% storage costThấp
Tạo VPC Endpoints (S3, ECR)~30-50% NAT costThấp
Share ALB bằng IngressGroup~$22/tháng mỗi LB bớtThấp
Right-size resource (VPA recommendation)~20-40% computeTrung bình
Dùng Spot cho non-critical workload~60-90% computeTrung bình

Trung hạn (1-3 tháng)

Hành độngTiết kiệm ước tínhĐộ khó
Chuyển sang Graviton instances~20% computeTrung bình
Dùng Karpenter consolidation~20-30% computeTrung bình
Topology-aware routing~30-50% cross-AZ costTrung bình
Scale-to-zero dev/staging ngoài giờ~65% dev/stagingThấp

Dài hạn (3-12 tháng)

Hành độngTiết kiệm ước tínhĐộ khó
Compute Savings Plans~40-60% baselineThấp (cam kết tài chính)
Kubecost chargebackVisibilityTrung bình
Multi-arch images (amd64 + arm64)~20% sau khi dùng GravitonCao

Checklist Cost Optimization

  • ✔ VPC Endpoints cho S3, ECR, STS, CloudWatch đã tạo
  • ✔ StorageClass mặc định là gp3 (không phải gp2)
  • ✔ Spot Instances cho stateless/batch workload
  • ✔ VPA mode Off đang thu thập recommendation
  • ✔ IngressGroup để share ALB
  • ✔ Cost Allocation Tags trên tất cả resources
  • ✔ AWS Budget alert đã cấu hình
  • ✔ Dev/Staging scale-to-zero ngoài giờ
  • ✔ Kubecost hoặc CloudWatch cost dashboard đã cài

10. Tổng kết

Tối ưu chi phí EKS là quá trình liên tục, tập trung vào:

  • Compute - Spot + Graviton + Karpenter consolidation
  • Networking - VPC Endpoints + IngressGroup + topology-aware routing
  • Storage - gp3 + EFS IA + dọn PVC không dùng
  • Scheduling - Scale-to-zero ngoài giờ cho non-production
  • Commitment - Savings Plans cho baseline capacity
  • Visibility - Kubecost + Cost Explorer + Budget alerts

Tham khảo: