Giới Thiệu: 30% Ngân Sách Cloud Của Bạn Đang Bị Đốt Vô Ích
Theo báo cáo State of FinOps 2026 từ FinOps Foundation, trung bình 30–35% chi tiêu cloud toàn cầu bị lãng phí do tài nguyên idle, over-provisioned hoặc đơn giản là bị "bỏ quên". Nghe thì khó tin, nhưng với tổng chi tiêu cloud dự kiến vượt mốc 1 nghìn tỷ USD trong năm 2026, con số lãng phí thực sự lên tới hàng trăm tỷ đô la mỗi năm.
Nói thẳng ra: bạn có thể đang trả tiền cho những EC2 instance chạy suốt đêm mà chẳng ai động vào, những Azure VM dev/test hoạt động 24/7 dù team chỉ code 8 tiếng mỗi ngày, hoặc những persistent disk trên GCP đã tách khỏi VM từ… rất lâu rồi.
Và phần đáng lo nhất? Bạn có thể không hề biết — cho đến khi nhận hóa đơn cuối tháng và tự hỏi "tiền đi đâu hết vậy?".
Mình từng ở trong tình huống tương tự. Một dự án cũ có hơn 15 EBS volume "trôi nổi" không gắn vào instance nào, tồn tại hơn 6 tháng mà không ai hay. Chi phí không lớn cho từng volume, nhưng cộng dồn lại thì cũng kha khá. Từ đó mình bắt đầu nghiêm túc hơn với việc tự động dọn dẹp.
Bài viết này sẽ hướng dẫn bạn cách tự động phát hiện và dọn dẹp tài nguyên cloud lãng phí trên cả ba nền tảng lớn: AWS, Azure và GCP. Mỗi phần đều đi kèm code thực tế, bạn có thể triển khai ngay hôm nay. Vậy nên, cùng bắt đầu thôi.
1. Các Loại Tài Nguyên Cloud Thường Bị Lãng Phí Nhất
Trước khi nhảy vào giải pháp, bạn cần nắm rõ những "thủ phạm" phổ biến nhất gây cháy ngân sách cloud:
- VM/Instance idle: CPU dưới 5–10% liên tục trong nhiều ngày. Đặc biệt phổ biến với môi trường dev/test và staging — những environment mà mọi người hay tạo rồi quên mất.
- Disk không gắn (Unattached Volumes): EBS volume trên AWS, Managed Disk trên Azure, hoặc Persistent Disk trên GCP đã bị tách khỏi instance nhưng vẫn tính phí đều đặn hàng tháng. Cái này là lãng phí "ẩn" kinh điển.
- Snapshot cũ: Bản snapshot được tạo từ nhiều tháng (thậm chí nhiều năm) trước, không còn ai cần nhưng vẫn chiếm dung lượng storage.
- IP tĩnh không sử dụng: Elastic IP (AWS), Static IP (GCP) không gắn vào resource nào — AWS tính phí $3.65/tháng cho mỗi Elastic IP không dùng. Nhỏ nhưng nhân lên thì đau.
- Load Balancer mồ côi: ALB/NLB không có target group hoạt động, vẫn tính phí cố định hàng tháng.
- Môi trường non-production chạy 24/7: Dev, QA, staging environment chạy liên tục kể cả đêm và cuối tuần — lãng phí tới 65–70% chi phí compute cho những resource này.
2. AWS: Tự Động Phát Hiện Và Dừng Tài Nguyên Idle
2.1 Quét EC2 Instance Có CPU Thấp Bằng Boto3
Script Python dưới đây sử dụng boto3 và CloudWatch để quét tất cả EC2 instance đang chạy, tìm những instance có CPU trung bình dưới ngưỡng trong 7 ngày qua. Khá đơn giản nhưng hiệu quả bất ngờ:
import boto3
from datetime import datetime, timedelta
def find_idle_ec2_instances(region='ap-southeast-1', cpu_threshold=10, days=7):
"""Tìm EC2 instances có CPU trung bình dưới ngưỡng trong N ngày."""
ec2 = boto3.client('ec2', region_name=region)
cw = boto3.client('cloudwatch', region_name=region)
instances = ec2.describe_instances(
Filters=[{'Name': 'instance-state-name', 'Values': ['running']}]
)
idle_instances = []
end_time = datetime.utcnow()
start_time = end_time - timedelta(days=days)
for reservation in instances['Reservations']:
for inst in reservation['Instances']:
instance_id = inst['InstanceId']
instance_type = inst['InstanceType']
metrics = cw.get_metric_statistics(
Namespace='AWS/EC2',
MetricName='CPUUtilization',
Dimensions=[{'Name': 'InstanceId', 'Value': instance_id}],
StartTime=start_time,
EndTime=end_time,
Period=86400,
Statistics=['Average']
)
if metrics['Datapoints']:
avg_cpu = sum(d['Average'] for d in metrics['Datapoints']) / len(metrics['Datapoints'])
if avg_cpu < cpu_threshold:
name_tag = next(
(t['Value'] for t in inst.get('Tags', []) if t['Key'] == 'Name'),
'N/A'
)
idle_instances.append({
'InstanceId': instance_id,
'Name': name_tag,
'Type': instance_type,
'AvgCPU': round(avg_cpu, 2)
})
return idle_instances
# Chạy quét
results = find_idle_ec2_instances()
for inst in results:
print(f"[IDLE] {inst['InstanceId']} ({inst['Name']}) - Type: {inst['Type']} - CPU: {inst['AvgCPU']}%")
2.2 Tự Động Dừng Instance Idle Bằng Lambda + EventBridge
Okay, quét thủ công thì được rồi. Nhưng để thực sự tự động hóa, bạn nên triển khai script dưới dạng AWS Lambda function, kích hoạt bởi EventBridge theo lịch — ví dụ chạy mỗi ngày lúc 22:00 khi team đã về hết:
# EventBridge Schedule Expression: cron(0 22 * * ? *)
# Lambda function tự động stop instance idle
import boto3
import json
from datetime import datetime, timedelta
def lambda_handler(event, context):
ec2 = boto3.client('ec2')
cw = boto3.client('cloudwatch')
sns = boto3.client('sns')
SNS_TOPIC_ARN = 'arn:aws:sns:ap-southeast-1:123456789:idle-instance-alerts'
CPU_THRESHOLD = 10
DAYS = 7
# Chỉ target instance có tag AutoStop=true
instances = ec2.describe_instances(Filters=[
{'Name': 'instance-state-name', 'Values': ['running']},
{'Name': 'tag:AutoStop', 'Values': ['true']}
])
stopped = []
end_time = datetime.utcnow()
start_time = end_time - timedelta(days=DAYS)
for res in instances['Reservations']:
for inst in res['Instances']:
iid = inst['InstanceId']
metrics = cw.get_metric_statistics(
Namespace='AWS/EC2',
MetricName='CPUUtilization',
Dimensions=[{'Name': 'InstanceId', 'Value': iid}],
StartTime=start_time, EndTime=end_time,
Period=86400, Statistics=['Average']
)
if metrics['Datapoints']:
avg = sum(d['Average'] for d in metrics['Datapoints']) / len(metrics['Datapoints'])
if avg < CPU_THRESHOLD:
ec2.stop_instances(InstanceIds=[iid])
stopped.append(iid)
# Gửi thông báo qua SNS
if stopped:
sns.publish(
TopicArn=SNS_TOPIC_ARN,
Subject=f'[FinOps] Đã dừng {len(stopped)} EC2 idle instances',
Message=json.dumps(stopped, indent=2)
)
return {'stopped_instances': stopped, 'count': len(stopped)}
2.3 Phát Hiện EBS Volume Không Gắn
EBS volume bị tách khỏi instance là một trong những nguồn lãng phí "ẩn" phổ biến nhất mà mình thấy. Nhiều người xóa instance nhưng quên mất volume vẫn còn đó, âm thầm tính phí. Script sau giúp bạn tìm ra chúng:
# Tìm tất cả EBS volumes không gắn vào instance nào
aws ec2 describe-volumes \
--filters "Name=status,Values=available" \
--query 'Volumes[*].{ID:VolumeId,Size:Size,Created:CreateTime,Type:VolumeType}' \
--output table
Để xóa volume không gắn đã tồn tại hơn 30 ngày (nhớ chạy dry-run trước, đặc biệt nếu là production account nhé):
import boto3
from datetime import datetime, timedelta, timezone
def cleanup_unattached_ebs(region='ap-southeast-1', age_days=30, dry_run=True):
ec2 = boto3.client('ec2', region_name=region)
cutoff = datetime.now(timezone.utc) - timedelta(days=age_days)
volumes = ec2.describe_volumes(
Filters=[{'Name': 'status', 'Values': ['available']}]
)
for vol in volumes['Volumes']:
created = vol['CreateTime']
if created < cutoff:
vid = vol['VolumeId']
size = vol['Size']
if dry_run:
print(f"[DRY RUN] Sẽ xóa {vid} ({size} GB, tạo ngày {created.date()})")
else:
ec2.delete_volume(VolumeId=vid)
print(f"[DELETED] {vid} ({size} GB)")
# Chạy dry-run trước để kiểm tra
cleanup_unattached_ebs(dry_run=True)
3. Azure: Tự Động Tắt VM Idle Và Dọn Dẹp Tài Nguyên
3.1 Sử Dụng Azure Monitor Alert + Automation Runbook
Azure có một cách tiếp cận khá hay (và thành thật mà nói, mình thấy nó tiện hơn AWS trong khoản này): dùng Azure Monitor phát hiện VM có CPU thấp, rồi tự động trigger Automation Runbook để dừng VM. Các bước cụ thể:
- Tạo Azure Automation Account với System Assigned Managed Identity.
- Cấp quyền
Virtual Machine Contributorcho Managed Identity trên resource group chứa VM. - Tạo Alert Rule trong Azure Monitor với điều kiện: CPU Percentage trung bình dưới 5% trong 15 phút.
- Gắn Action Group gọi Runbook Stop-AzureV2Vm (built-in có sẵn) khi alert kích hoạt.
Chỉ 4 bước thôi, không cần viết code gì cả. Đơn giản mà hiệu quả.
3.2 Script PowerShell Quét Và Dừng VM Idle
Nếu bạn muốn kiểm soát chi tiết hơn — ví dụ tùy chỉnh ngưỡng CPU, lookback time, hay logic phức tạp hơn — thì đây là script PowerShell dùng trong Automation Runbook:
# Azure Automation Runbook - Dừng VM idle dựa trên CPU
param(
[int]$CpuThreshold = 5,
[int]$LookbackMinutes = 60,
[string]$TagName = "AutoStop",
[string]$TagValue = "true"
)
# Kết nối bằng Managed Identity
Connect-AzAccount -Identity
# Lấy tất cả VM có tag AutoStop=true
$vms = Get-AzVM -Status | Where-Object {
$_.Tags[$TagName] -eq $TagValue -and
$_.PowerState -eq "VM running"
}
foreach ($vm in $vms) {
$metrics = Get-AzMetric -ResourceId $vm.Id `
-MetricName "Percentage CPU" `
-TimeGrain 00:05:00 `
-StartTime (Get-Date).AddMinutes(-$LookbackMinutes) `
-EndTime (Get-Date) `
-AggregationType Average
$avgCpu = ($metrics.Data | Measure-Object -Property Average -Average).Average
if ($avgCpu -lt $CpuThreshold) {
Write-Output "Dừng VM: $($vm.Name) (CPU trung bình: $([math]::Round($avgCpu,2))%)"
Stop-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Force
} else {
Write-Output "Giữ VM: $($vm.Name) (CPU trung bình: $([math]::Round($avgCpu,2))%)"
}
}
3.3 Phát Hiện Managed Disk Không Gắn Bằng Azure CLI
# Liệt kê tất cả Managed Disk không gắn vào VM nào
az disk list \
--query "[?managedBy==null].{Name:name, Size:diskSizeGb, SKU:sku.name, RG:resourceGroup, Created:timeCreated}" \
--output table
# Xóa disk không gắn (cẩn thận!)
az disk list --query "[?managedBy==null].id" -o tsv | while read diskId; do
echo "Xóa disk: $diskId"
# az disk delete --ids "$diskId" --yes # Bỏ comment để thực sự xóa
done
4. GCP: Dùng Recommender API Và Cloud Scheduler
4.1 Sử Dụng Idle VM Recommender
Đây là điểm mình thấy GCP làm tốt hơn hai đối thủ còn lại. GCP có sẵn Idle VM Recommender — công cụ miễn phí tự động phân tích CPU và network usage để xác định VM nào đang idle. Nó tạo gợi ý mỗi ngày sau khi VM chạy ít nhất 1 ngày, bạn không cần tự viết logic gì cả.
# Xem danh sách VM idle trong một zone cụ thể
gcloud recommender recommendations list \
--recommender=google.compute.instance.IdleResourceRecommender \
--project=MY_PROJECT_ID \
--location=asia-southeast1-a \
--format="table(content.operationGroups[0].operations[0].resource, priority, stateInfo.state)"
# Xem chi tiết một recommendation
gcloud recommender recommendations describe RECOMMENDATION_ID \
--recommender=google.compute.instance.IdleResourceRecommender \
--project=MY_PROJECT_ID \
--location=asia-southeast1-a
4.2 Tự Động Dừng VM Idle Bằng Cloud Function + Cloud Scheduler
Kết hợp Cloud Scheduler (về cơ bản là cron trên cloud) với Cloud Functions để tự động xử lý VM idle hàng ngày:
# Cloud Function (Python) - Tự động stop VM idle
from google.cloud import recommender_v1
from google.cloud import compute_v1
import functions_framework
@functions_framework.http
def stop_idle_vms(request):
project_id = "my-project-id"
zones = ["asia-southeast1-a", "asia-southeast1-b"]
stopped_vms = []
recommender_client = recommender_v1.RecommenderClient()
compute_client = compute_v1.InstancesClient()
for zone in zones:
parent = (
f"projects/{project_id}/locations/{zone}/"
f"recommenders/google.compute.instance.IdleResourceRecommender"
)
recommendations = recommender_client.list_recommendations(parent=parent)
for rec in recommendations:
if rec.state_info.state == recommender_v1.RecommendationStateInfo.State.ACTIVE:
resource = rec.content.operation_groups[0].operations[0].resource
vm_name = resource.split("/")[-1]
instance = compute_client.get(
project=project_id, zone=zone, instance=vm_name
)
labels = instance.labels or {}
if labels.get("auto-stop") == "true":
compute_client.stop(
project=project_id, zone=zone, instance=vm_name
)
stopped_vms.append(f"{vm_name} ({zone})")
return {"stopped": stopped_vms, "count": len(stopped_vms)}
4.3 Phát Hiện Persistent Disk Và IP Không Sử Dụng
# Tìm Persistent Disk không gắn vào VM nào
gcloud compute disks list \
--filter="NOT users:*" \
--format="table(name, zone.basename(), sizeGb, type.basename(), status)"
# Tìm Static IP không sử dụng
gcloud compute addresses list \
--filter="status=RESERVED" \
--format="table(name, address, region.basename(), status)"
5. Chiến Lược Tổng Thể: Quy Trình Dọn Dẹp Tự Động Hóa
5.1 Thiết Lập Tag/Label Bắt Buộc
Đây là bước quan trọng nhất mà nhiều team hay bỏ qua. Mọi chiến lược tự động hóa đều bắt đầu từ tagging. Không có tag thì bạn không biết tài nguyên thuộc team nào, dùng cho mục đích gì, và liệu có an toàn để dừng hay không.
Cấu trúc tag mình khuyến nghị:
Environment: dev / staging / productionOwner: email hoặc team chịu trách nhiệmAutoStop: true / false — cho phép automation dừng tài nguyên hay khôngExpirationDate: ngày hết hạn dự kiến (cực kỳ hữu ích cho tài nguyên tạm thời kiểu POC hay demo)
5.2 Lịch Trình Dọn Dẹp Đề Xuất
Lịch trình này không phải mình tự nghĩ ra — nó dựa trên mô hình mà nhiều đội FinOps đang áp dụng thành công:
- Hàng ngày (tự động): Dừng VM dev/test ngoài giờ làm việc (20:00–08:00) và cuối tuần. Riêng bước này thôi đã tiết kiệm 65–70% chi phí compute cho non-production.
- Hàng tuần (tự động): Quét và báo cáo instance có CPU dưới 10%. Gửi notification cho team owner qua Slack hoặc email — để họ quyết định giữ hay dừng.
- Hàng tháng (bán tự động): Review và xóa unattached disk, snapshot cũ hơn 90 ngày, IP tĩnh không dùng. Tạo report tổng hợp cho quản lý.
- Hàng quý (thủ công): Đánh giá right-sizing toàn diện. Review commitment (Reserved Instances, Savings Plans) sắp hết hạn để gia hạn hoặc điều chỉnh.
5.3 Thiết Lập Alert Và Báo Cáo
Một điều mình muốn nhấn mạnh: tự động hóa mà không có giám sát thì rất nguy hiểm. Bạn không muốn script tự dừng nhầm một instance production quan trọng lúc 2 giờ sáng đâu. Luôn thiết lập:
- Alert trước khi dừng: Gửi thông báo cho owner 24 giờ trước khi tự động dừng resource. Cho phép override bằng cách thêm tag
SkipAutoStop=true. - Dashboard chi phí: Sử dụng AWS Cost Explorer, Azure Cost Management, hoặc GCP Billing Console để theo dõi tiết kiệm thực tế. Nhìn thấy con số cụ thể sẽ giúp bạn chứng minh giá trị của việc này với sếp.
- Báo cáo hàng tuần: Tổng hợp số tài nguyên idle phát hiện, số đã dừng, và chi phí tiết kiệm ước tính.
6. Bảng So Sánh Công Cụ Native Trên 3 Nền Tảng
Để tiện cho bạn so sánh, đây là bảng tổng hợp nhanh các công cụ native phát hiện và xử lý tài nguyên idle trên từng nền tảng:
| Tính năng | AWS | Azure | GCP |
|---|---|---|---|
| Phát hiện idle VM | Trusted Advisor, Compute Optimizer | Azure Advisor, Monitor Alerts | Idle VM Recommender |
| Lên lịch start/stop | Instance Scheduler, SSM Quick Setup | Auto-shutdown, Automation Runbook | Cloud Scheduler + Cloud Functions |
| Phát hiện disk/storage lãng phí | Trusted Advisor, CLI query | Azure Advisor, CLI query | Idle Resource Recommender |
| Ước tính tiết kiệm | Cost Explorer Savings Plans | Azure Advisor Cost | Recommender Hub |
| Chi phí công cụ | Miễn phí (Trusted Advisor cần Business Support) | Miễn phí | Miễn phí |
Câu Hỏi Thường Gặp (FAQ)
Làm thế nào để biết tài nguyên cloud nào đang bị lãng phí?
Cách nhanh nhất là kiểm tra CPU utilization trung bình trong 7–14 ngày. Nếu một instance có CPU dưới 10% liên tục, khả năng cao là nó đang idle. Ngoài ra, hãy tìm EBS volume hoặc Managed Disk không gắn vào VM nào, snapshot cũ hơn 90 ngày, và IP tĩnh không sử dụng. Các công cụ native như AWS Trusted Advisor, Azure Advisor, và GCP Recommender có thể tự động phát hiện những vấn đề này — bạn chỉ cần bật lên và xem.
Tự động dừng instance có an toàn không? Có ảnh hưởng production không?
An toàn, nhưng chỉ khi bạn thiết lập đúng cách. Nguyên tắc số một: chỉ tự động dừng instance có tag cho phép (ví dụ AutoStop=true). Tuyệt đối không bao giờ tự động dừng resource production mà không có sự phê duyệt thủ công. Luôn chạy dry-run trước, gửi thông báo cho owner trước khi thực hiện, và cung cấp cơ chế override dễ dàng. Làm đúng thì rất an toàn, làm ẩu thì… bạn biết rồi đó.
Nên bắt đầu từ đâu nếu chưa từng làm FinOps?
Bắt đầu từ ba bước đơn giản nhất: (1) Bật auto-shutdown cho tất cả VM dev/test ngoài giờ làm việc — đây là "quick win" dễ nhất và tiết kiệm khoảng 65% ngay lập tức. (2) Quét unattached disk và snapshot cũ, xóa những gì không cần. (3) Thiết lập tagging bắt buộc cho tất cả resource mới tạo. Chỉ ba bước đó thôi có thể tiết kiệm 20–30% chi phí cloud mà không cần đầu tư công cụ phức tạp gì.
Có nên dùng công cụ third-party hay công cụ native là đủ?
Tùy quy mô. Với đội nhỏ và chỉ dùng một cloud provider, công cụ native như AWS Cost Explorer, Azure Cost Management, GCP Billing Console là hoàn toàn đủ xài. Khi bạn scale lên multi-cloud, cần quản lý nhiều account/subscription, hoặc muốn tự động hóa nâng cao hơn (ví dụ autonomous discount management), thì lúc đó công cụ third-party như ProsperOps, nOps, CAST AI, hoặc Infracost sẽ mang lại giá trị rõ rệt hơn.
Tiết kiệm thực tế có thể đạt được bao nhiêu phần trăm?
Theo kinh nghiệm thực tế và báo cáo từ FinOps Foundation 2026: lên lịch start/stop cho non-production tiết kiệm 65–70%, dọn dẹp unattached disk và snapshot tiết kiệm 5–10%, right-sizing instance idle tiết kiệm 30–50%. Cộng lại, một chương trình FinOps bài bản có thể giảm 30–50% tổng chi phí cloud so với trước khi tối ưu. Con số này nghe lớn, nhưng hoàn toàn khả thi nếu bạn thực hiện nghiêm túc.