メインコンテンツまでスキップ

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: