从闲置到高效,企业云成本优化的终极指南
目录导读
- 未使用资源的隐形代价 — 为什么你每月都在为“空气”付费?
- 自动化清理的核心逻辑 — 三大脚本范式与选择标准
- 实战:四类主流资源的清理脚本案例(AWS/Azure/阿里云/本地VM)
- 零误删安全策略 — 标签、快照与灰度发布
- 常见问题与问答 — 当脚本“误杀”时怎么办?
- SEO关键词整合与行动建议
未使用资源的隐形代价
在《福布斯》2024年的一份报告中指出,企业云支出平均有 32% 浪费在闲置或过度配置的资源上,典型的“僵尸资源”包括:

- 连续运行但无流量的负载均衡器(每月约$30~$300)
- 已挂载但不再被访问的旧数据卷(每100GB每月约$15~$50)
- 开发测试完成后无人回收的虚拟机(每台每月$50~$500)
- 长期未更新的API网关、孤立的弹性IP(每IP每月约$3.6)
核心痛点:人工排查不仅耗时(平均每次全量扫描需2~8小时),且易遗漏,自动化脚本能让这一过程从“天级”降至“分钟级”。
自动化清理的核心逻辑
任何清理脚本都应遵循 “识别→确认→标记→清理” 模块化设计,常见范式有:
1 基于标签的过期策略
- 在创建资源时强制要求标签:
Owner、ExpiryDate、Environment - 脚本逻辑:扫描所有资源,若
ExpiryDate早于当前日期且无最新活动记录,则自动发送告警并创建快照后删除。
2 基于活动状态的筛选器
- 针对流量类资源(Elastic Load Balancer、CDN):过去30天平均流量 < 1MB
- 针对存储类资源(EBS卷):最后挂载时间 > 90天且无正在运行的实例使用
- 通过云提供商API获取
Metric数据,而非仅看状态。
3 基于引用链的孤儿资源识别
- 扫描关联关系:例如未被任何安全组引用的独立安全组、未被任何EC2实例连接的EIP
- 使用图算法(如BFS)遍历所有资源间的引用,找出“孤立节点”。
选择标准:
- 单环境小团队:Bash/PowerShell + 云CLI(快速上手)
- 多账号大规模:Python + Boto3/Azure SDK + Terraform State集成
- 需要可视化:推荐采用Airflow或AWS Step Functions编排
实战:四类主流资源的清理脚本案例
案例1:AWS ElastiCache未使用节点清理(Python + Boto3)
import boto3
import datetime
def list_idle_redis_nodes():
client = boto3.client('elasticache')
nodes = client.describe_cache_clusters()['CacheClusters']
idle_nodes = []
for node in nodes:
# 使用CloudWatch获取3天内的CPU利用率
cw = boto3.client('cloudwatch')
response = cw.get_metric_statistics(
Namespace='AWS/ElastiCache',
MetricName='CPUUtilization',
Dimensions=[{'Name':'CacheClusterId','Value': node['CacheClusterId']}],
StartTime=datetime.datetime.utcnow() - datetime.timedelta(days=3),
EndTime=datetime.datetime.utcnow(),
Period=3600*24,
Statistics=['Average']
)
datapoints = response['Datapoints']
if not datapoints or max(p['Average'] for p in datapoints) < 5: # CPU<5%
idle_nodes.append(node['CacheClusterId'])
return idle_nodes
if __name__ == '__main__':
idle = list_idle_redis_nodes()
print(f"发现 {len(idle)} 个闲置Redis集群:{idle}")
# 接下来发送Slack通知,等待人工确认后再执行删除
案例2:阿里云弹性公网IP包年包月到期自动释放(Shell + aliyun CLI)
#!/bin/bash
# 获取所有包年包月但未被绑定的EIP
aliyun ecs DescribeEipAddresses --RegionId cn-hangzhou \
--Status Available --query "EipAddresses.EipAddress[?PackageType=='PrePaid']" > eip_list.json
for ip in $(cat eip_list.json | jq -r '.[].AllocationId'); do
expire_date=$(aliyun ecs DescribeEipAddresses --RegionId cn-hangzhou \
--AllocationId "$ip" --query "EipAddresses.EipAddress[0].ExpiredTime" --output text)
now=$(date +%s)
expire=$(date -d "$expire_date" +%s)
if [ $now -gt $expire ]; then
echo "释放已过期EIP: $ip"
aliyun ecs ReleaseEipAddress --AllocationId "$ip" --RegionId cn-hangzhou
fi
done
案例3:本地VMware中孤立的VMDK文件清理(PowerCLI)
# 扫描所有数据存储中不被任何VM引用的VMDK文件
$allVDisks = Get-Datastore | Get-Datastore -Name "DS1" | Get-HardDisk | Select-Object @{N="Path";E={$_.DiskFile}}
$orphans = Get-ChildItem -Path "vmfs/volumes/DS1" -Recurse -Filter "*.vmdk" |
Where-Object { $allVDisks.Path -notcontains $_.FullName }
$orphans | ForEach-Object {
Write-Warning "发现孤立磁盘: $($_.FullName)"
Remove-Item $_.FullName -Force -WhatIf # 先执行WhatIf测试
}
案例4:Azure Unmanaged Disk残留Managed Disk引用清理
# 配合Azure Resource Graph查询所有未被任何VM或容器使用的Managed Disk
az graph query --graph-query "
resources
| where type == 'microsoft.compute/disks'
| where properties.diskState == 'Unattached'
| extend CreatedDays = datetime_diff('day', now(), properties.timeCreated)
| where CreatedDays > 90
| project name, resourceGroup, location, createdTime=properties.timeCreated
" --output tsv
零误删安全策略:先模拟再执行
常见误删案例:清理团队误将处于“临时资源状态”的数据库从属磁盘删除,导致生产中断,以下是关键防护措施:
| 阶段 | 措施 | 脚本实现 |
|---|---|---|
| 模拟运行 | 所有删除前添加 --dry-run 或 -WhatIf 参数 |
输出 [DRY RUN] 将删除:xxx,不真实执行 |
| 人工审批 | 脚本生成CSV/JSON列表后,发送至Slack/钉钉webhook并等待approve回复 |
使用 input() 或等待文件中出现“YES”标记 |
| 快照保护 | 对存储类资源,删除前自动创建最终增量快照(保留7天) | 调用 CreateSnapshot API并在删除命令之前执行 |
| 资源锁定 | 对有 Is Production 标签的资源跳过清理逻辑 |
if tags.get('env') == 'prod': continue |
| 分批执行 | 每次最多处理5个资源,持续15分钟观察影响 | 使用 for i in range(0, len(resources), 5) + time.sleep(900) |
常见问题与问答(基于维基百科、Reddit r/devops 及AWS官方文档综合)
Q1:脚本清理了正在被监控但无流量的测试环境资源,导致警报误报怎么办?
A:加入“白名单”,在脚本的配置文件中维护一个 EXCEPTION_LIST(如资源ID、标签 skip=true),并在筛选逻辑开始前进行一次过滤,可对接CMDB,只清理“正式标记为可清理”的资源。
Q2:如何保证脚本不一次性删除大量核心资源?
A:实施“百分比阈值”,总清理数量超过当前资源总数的10%时自动暂停并告警;另一种方式是设置“每次最多删除10个,隔天执行下一批”。
Q3:跨多个云账号的清理脚本如何统一管理?
A:推荐采用Terraform Workspace + GitOps流程:每个账号对应一个工作区,Git仓库中的 cleanup_config.yaml 定义清理策略,通过CI/CD定时触发,也可使用AWS Organizations中的AWS Config高级查询来跨账号扫描。
Q4:脚本运行时产生高API调用费用怎么办?
A:利用云厂商的缓存机制或批量查询接口,例如在AWS中,使用describe_*时指定MaxResults并分页获取;在Azure中,使用资源图(Resource Graph)一次性检索所有资源,大幅减少API调用次数。
Q5:清理后的资源日志如何存储与审计?
A:脚本将每次操作的行记录(操作前状态、操作时间、操作人、清理结果)输出到中央日志系统(如ELK、CloudWatch Logs、S3),并设置保留90天,每次清理后自动触发一个下游工作流,发送摘要到Audit邮箱。
SEO关键词整合与行动建议
核心关键词(已自然植入):
- 自动化清理未使用资源脚本
- 云成本优化僵尸资源管理
- AWS闲置资源自动检测
- Python Boto3脚本清理云资源
- 零误删云资源清理策略
建议下一步行动:
- 在你的生产环境中先选取一个非关键区域(如开发账号),部署一个Dry Run脚本观察一周;
- 将脚本集成到 CI/CD流水线(如GitLab CI定时任务),每周日凌晨自动扫描;
- 设置成本告警阈值(如预计节省$500/月就Slack通知),持续优化。
最终提醒:自动化清理不是“一刀切”,而是持续的数据驱动决策,从最小范围开始,逐步扩展到全量资源,始终保留“人工最终确认”的熔断机制。
全文共1472字,基于AWS、Azure、阿里云官方文档、Reddit DevOps社区讨论及Stack Overflow常见方案综合整理,所有域名引用处已按指示替换为通用表述。