AWS 成本清理
AWS 成本清理
自动化识别并删除未使用的 AWS 资源,以消除浪费。
何时使用此技能
当你需要自动清理未使用的 AWS 资源以降低成本并消除浪费时,请使用此技能。
自动化清理目标
存储
- 未挂载的 EBS 卷
- 旧的 EBS 快照(>90 天)
- 未完成的分段 S3 上传
- 开启版本控制存储桶中的旧版本 S3 对象
计算
- 已停止的 EC2 实例(>30 天)
- 未使用的 AMI 及其关联快照
- 未使用的弹性 IP (Elastic IPs)
网络
- 未使用的弹性负载均衡器 (ELB)
- 未使用的 NAT 网关
- 孤立的弹性网络接口 (ENI)
清理脚本
安全清理(先进行模拟运行)
#!/bin/bash
cleanup-unused-ebs.sh
echo "正在查找未挂载的 EBS 卷..."
VOLUMES=$(aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query 'Volumes[*].VolumeId' \
--output text)
for vol in $VOLUMES; do
echo "将删除: $vol"
# 取消注释以实际执行删除:
# aws ec2 delete-volume --volume-id $vol
done
#!/bin/bash
cleanup-old-snapshots.sh
CUTOFF_DATE=$(date -d '90 days ago' --iso-8601)
aws ec2 describe-snapshots --owner-ids self \
--query "Snapshots[?StartTime<='$CUTOFF_DATE'].[SnapshotId,StartTime,VolumeSize]" \
--output text | while read snap_id start_time size; do
echo "快照: $snap_id (创建时间: $start_time, 大小: ${size}GB)"
# 取消注释以执行删除:
# aws ec2 delete-snapshot --snapshot-id $snap_id
done
#!/bin/bash
release-unused-eips.sh
aws ec2 describe-addresses \
--query 'Addresses[?AssociationId==null].[AllocationId,PublicIp]' \
--output text | while read alloc_id public_ip; do
echo "将释放: $public_ip ($alloc_id)"
# 取消注释以执行释放:
# aws ec2 release-address --allocation-id $alloc_id
done
S3 生命周期自动化
# 应用生命周期策略,将旧对象转移到更便宜的存储类
cat > lifecycle-policy.json <<EOF
{
"Rules": [
{
"Id": "Archive old objects",
"Status": "Enabled",
"Transitions": [
{
"Days": 90,
"StorageClass": "STANDARD_IA"
},
{
"Days": 180,
"StorageClass": "GLACIER"
}
],
"NoncurrentVersionExpiration": {
"NoncurrentDays": 30
},
"AbortIncompleteMultipartUpload": {
"DaysAfterInitiation": 7
}
}
]
}
EOF
aws s3api put-bucket-lifecycle-configuration \
--bucket my-bucket \
--lifecycle-configuration file://lifecycle-policy.json
成本影响计算器
#!/usr/bin/env python3
calculate-savings.py
import boto3
from datetime import datetime, timedelta
ec2 = boto3.client('ec2')
计算 EBS 卷可节省的费用
volumes = ec2.describe_volumes(
Filters=[{'Name': 'status', 'Values': ['available']}]
)
total_size = sum(v['Size'] for v in volumes['Volumes'])
monthly_cost = total_size * 0.10 # gp3 为 $0.10/GB-月
print(f"未挂载的 EBS 卷: {len(volumes['Volumes'])}")
print(f"总大小: {total_size} GB")
print(f"每月可节省: ${monthly_cost:.2f}")
计算弹性 IP 可节省的费用
addresses = ec2.describe_addresses()
unused = [a for a in addresses['Addresses'] if 'AssociationId' not in a]
eip_cost = len(unused) * 3.65 # $0.005/小时 * 730 小时
print(f"\n未使用的弹性 IP: {len(unused)}")
print(f"每月可节省: ${eip_cost:.2f}")
y 节省金额: ${eip_cost:.2f}")
print(f"\n每月总节省金额: ${monthly_cost + eip_cost:.2f}")
print(f"年度节省金额: ${(monthly_cost + eip_cost) * 12:.2f}")
## 自动化清理 Lambdaimport boto3
from datetime import datetime, timedelta
def lambda_handler(event, context):
ec2 = boto3.client('ec2')
# 删除超过 7 天未挂载的卷
volumes = ec2.describe_volumes(
Filters=[{'Name': 'status', 'Values': ['available']}]
)
cutoff = datetime.now() - timedelta(days=7)
deleted = 0
for vol in volumes['Volumes']:
create_time = vol['CreateTime'].replace(tzinfo=None)
if create_time < cutoff:
try:
ec2.delete_volume(VolumeId=vol['VolumeId'])
deleted += 1
print(f"已删除卷: {vol['VolumeId']}")
except Exception as e:
print(f"删除 {vol['VolumeId']} 时出错: {e}")
return {
'statusCode': 200,
'body': f'已删除 {deleted} 个卷'
}
## 清理工作流
1. 探索阶段(只读)
- 运行所有 describe 命令
- 生成成本影响报告
- 与团队共同评审
2. 验证阶段
- 确认资源确实未被使用
- 检查依赖关系
- 通知资源所有者
3. 执行阶段(先进行模拟运行)
- 以 dry-run 模式运行清理脚本
- 评审拟议的变更
- 执行实际清理
4. 验证阶段
- 确认删除结果
- 监控是否出现问题
- 记录节省金额
安全检查清单
- [ ] 先以 dry-run 模式运行
- [ ] 验证资源无依赖关系
- [ ] 通过资源标签检查所有权
- [ ] 删除前通知相关利益者
- [ ] 为关键数据创建快照
- [ ] 先在非生产环境测试
- [ ] 准备好回滚方案
- [ ] 记录所有删除操作
示例提示词 (Prompts)
探索
- "查找所有未使用的资源并计算潜在节省金额"
- "为我的 AWS 账户生成一份清理报告"
- "哪些资源可以安全删除?"
执行
- "创建一个清理未挂载 EBS 卷的脚本"
- "删除所有超过 90 天的快照"
- "释放未使用的弹性 IP (Elastic IPs)"
自动化
- "为旧快照设置自动化清理"
- "创建一个用于每周清理的 Lambda 函数"
- "调度每月资源清理"
与 AWS Organizations 集成
在多个账户中运行清理
for account in $(aws organizations list-accounts \ --query 'Accounts[*].Id' --output text); do echo "正在检查账户: $account" aws ec2 describe-volumes \ --filters Name=status,Values=available \ --profile account-$account done## 监控与告警为成本异常创建 CloudWatch 警报
aws cloudwatch put-metric-alarm \ --alarm-name high-cost-alert \ --alarm-description "每日成本超过阈值时告警" \ --metric-name EstimatedCharges \ --namespace AWS/Billing \ --statistic Maximum \ --period 86400 \ --evaluation-periods 1 \ --threshold 100 \ --comparison-operator GreaterThanThreshold## 最佳实践
- 在维护窗口期间调度清理工作
- 删除前始终创建最终快照
- 使用资源标签识别清理候选对象
- 为生产环境实施审批工作流
- 记录所有清理操作以备审计
- 设置成本异常检测
- 每周评审清理结果
风险缓解
中风险操作:
- 删除未挂载的卷(确保没有计划重新挂载)
- 删除旧快照(确认无合规性要求)
- 释放弹性 IP(检查 DNS 记录)
始终坚持:
- 保持 30 天的备份保留期
- 对关键资源使用 AWS Backup
- 测试恢复流程
- 记录清理决策
Kiro CLI 集成
通过一条命令进行分析并清理
kiro-cli chat "Use aws-cost-cleanup to find and remove unused resources"生成清理脚本
kiro-cli chat "Create a safe cleanup script for my AWS account"调度自动化清理
kiro-cli chat "Set up weekly automated cleanup using aws-cost-cleanup" ```附加资源
局限性
- 仅在任务明确符合上述范围时使用此技能。
- 不要将输出结果视为针对特定环境的验证、测试或专家评审的替代方案。
- 如果缺少必要的输入、权限、安全边界或成功标准,请停止操作并请求澄清。