前言
先说结论:云成本优化最大的难点不是技术,是没人愿意承认自己的服务在浪费钱。
我负责省级电力云平台的架构与运维,团队 15 人。2024 年做了一轮系统性的成本治理,把平台年度云资源支出降了约 25%。这篇文章把整个过程拆开讲:怎么定位浪费、怎么改配置、怎么防止三个月后又涨回去,以及中间踩的坑。
不是理论框架,是可以直接抄的作业。
一、为什么云账单一定会虚高
先看行业数据。这不是我们一家的问题:
- Flexera《2026 年云现状报告》:受访组织自估29%的云支出属于浪费
- 2026 年全球云浪费规模超1000亿美元,其中闲置算力占35%、实例超配占25%,是两大浪费来源
- 云安全联盟(CSA)2026 年估算:企业云计算投入超 1 万亿美元,其中最高35%被浪费
K8s 环境的浪费有其特殊机制。阿里云 ACK 官方文档里写得很直白:
为确保资源的稳定性,管理员通常会为 LS 类型的应用申请较大的资源 Request 和 Limit,因此会出现 K8s 集群资源的分配率(Requested)较高,而实际利用率(Usage)较低的情况。
AWS 在 Kubecost 的文档里给了一个更具体的例子:
如果一个容器 request 1 CPU 但只用了 200 millicores(0.2 CPU),那么多出的 0.8 CPU 处于未使用状态,但节点容量依然被预留并被计费。
翻译成大白话:你申请多少,云厂商就按多少收钱,跟你实际用多少没关系。
而在真实的研发流程里,Request 值是怎么定下来的?大概率是——上线时随手填个数,或者照着别人的 YAML 抄一份,然后三年没人动过。业务体量变了,配置没变。
这就是浪费的根源。
二、成本优化5步法
第1步:先让钱变得可见
不能优化的东西是”看不见的东西”。第一步不是省钱,是搞清楚钱花在哪。
我们当时的做法:
# KubeCost 部署(社区版够用)
helm repo add kubecost https://kubecost.github.io/cost-analyzer/
helm install kubecost kubecost/cost-analyzer\
--namespacekubecost--create-namespace\
--setkubecostToken="your-token"
部署完之后重点看三个视图:
维度 | 看什么 | 典型发现 |
by Namespace | 哪个业务线花钱最多 | 通常前 3 个命名空间占 70% 以上支出 |
by Deployment | 哪个服务的单位成本异常 | 副本数远超实际负载的服务 |
by Node Pool | 哪类节点最贵、利用率最低 | GPU 节点池利用率常常不到 30% |
我们第一次跑完 KubeCost 的结果:CPU 平均分配率 78%,实际利用率只有20%左右。也就是说超过四分之三的算力是”预留但没用”的状态。
这个数字当时在团队里引起不小的争论,因为它意味着过去几年的扩容决策大多是过度保守的。
第2步:Rightsizing(修正超配)
这一步能拿到的收益最大,但风险也最集中。
核心原则:Request定得准,Limit定得宽。
- requests 决定调度,是实际”占位”和计费的依据,必须贴近真实用量
- limits 决定上限,防止单个 Pod 打爆节点,可以留冗余
实操方法是用 Prometheus 拉历史真实用量:
# 过去 7 天 CPU 使用量的 P95
quantile_over_time(0.95,
rate(container_cpu_usage_seconds_total{
namespace="prod", pod=~"your-app-.*"
}[5m])[7d:1m]
)
# 过去 7 天内存使用量的 P95(注意用 working_set,不含可回收缓存)
quantile_over_time(0.95,
container_memory_working_set_bytes{
namespace="prod", pod=~"your-app-.*"
}[7d:1m]
)
拿到 P95 之后,Request设成P95的1.1-1.2倍。留出缓冲,但不至于浪费一半。
改造后的 Deployment 长这样:
resources:
requests:
cpu:"250m"# 原值 1000m,P95 实测 210m
memory:"512Mi"# 原值 2Gi,P95 实测 420Mi
limits:
cpu:"1000m"# 保留 4 倍冗余应对突发
memory:"1Gi"# 内存 Limit 不能设太高,OOMKill 风险
⚠️ 这里有个关键点:内存和CPU的处理逻辑完全不同。
- CPU 是可压缩资源:超了会被 throttle(限流),服务变慢但不崩
- 内存是不可压缩资源:超了直接 OOMKill,Pod 被杀重启
所以 CPU Limit 可以放宽,内存 Limit 要相对保守,宁可多给一点。
第3步:清理僵尸资源
这一步最没有技术含量,但见效最快。
我们的巡检清单:
# 1. 找出长期低负载的 Deployment
kubectl get deploy-A-ojson|jq-r'.items[] |
"\(.metadata.namespace)/\(.metadata.name) replicas=\(.spec.replicas)"'
# 2. 孤儿 PVC(已释放但仍计费)
kubectl get pvc-A--field-selector=status.phase=Bound
# 3. 无人使用的 LoadBalancer(云上每个 LB 都在单独计费)
kubectl get svc-A--field-selector=spec.type=LoadBalancer
# 4. 已停止但未删除的节点
kubectl get nodes|grepNotReady
重点检查这四类:
- 测试/预发环境的常驻资源——很多团队测试环境 7×24 跑着,实际只在上班时间有人用
- 孤儿PVC——Pod 删了,存储卷还在计费
- 多余的LoadBalancer——一个 LB 每月几十到几百块,很容易积攒出几十个
- 过期镜像的临时实例——上线灰度用的,忘了下线
我们这一轮清理掉了 47 个孤儿 PVC 和 23 个无流量 LB,一年下来这部分就是六到七位数级别的支出。
第4步:弹性化,别再用固定副本硬扛
Rightsizing 解决的是”单个 Pod 该多大”,弹性解决的是”该有几个 Pod”。
HPA 配置示例:
apiVersion:autoscaling/v2
kind:HorizontalPodAutoscaler
metadata:
name:your-app-hpa
spec:
scaleTargetRef:
apiVersion:apps/v1
kind:Deployment
name:your-app
minReplicas:3
maxReplicas:20
metrics:
-type:Resource
resource:
name:cpu
target:
type:Utilization
averageUtilization:65# 不要设太高,扩容有延迟
behavior:
scaleDown:
stabilizationWindowSeconds:300# 缩容要慢,避免抖动
policies:
-type:Percent
value:20
periodSeconds:60
两个参数要特别注意:
- averageUtilization 不要设到 80% 以上。HPA 从采集指标到新 Pod 就绪有延迟,等真到 90% 再扩容,服务已经被打爆了。65%左右是比较稳的起点。
- scaleDown.stabilizationWindowSeconds 要设。缩容比扩容危险——流量波动时如果频繁缩了又扩,会出现服务抖动。
节点层面的分层:
节点池 | 实例类型 | 用途 | 成本 |
核心池 | 按需实例 | 有状态服务、核心链路 | 100% |
弹性池 | 按需实例 | 无状态业务 Pod | 100% |
抢占池 | 竞价/抢占式 | 离线计算、批处理、CI | 20%-40% |
抢占式实例的坑我们在第五部分讲。
第5步:建立机制,防止反弹
这是最容易被跳过、也最关键的一步。
前四步做完,账单会降下来。但如果没有机制,三个月后一定涨回去。原因很简单:新服务上线时,开发还是照着旧的 YAML 抄 Request;扩容还是靠拍脑袋;没人对成本负责。
我们落地的三个机制:
1.成本归属到团队
用 Namespace 打标签,配合 KubeCost 的分摊功能,每月出一份各业务线的成本报表,直接发到负责人群里。
2.上线准入检查
在 CI 流水线加一个卡点,新 Deployment 如果没有填 requests/limits,或者 Request 值超过阈值,直接构建失败。
# 用 LimitRange 做命名空间级兜底
apiVersion:v1
kind:LimitRange
metadata:
name:default-limits
namespace:prod
spec:
limits:
-type:Container
default:
cpu:"500m"
memory:"512Mi"
defaultRequest:
cpu:"100m"
memory:"128Mi"
3.月度成本复盘会
固定每月一次,只看三个指标:总支出环比、分配率 vs 实际利用率的差值、Top 5 成本增长的服务。
行业数据也在印证这个方向的价值:FinOps 基金会《2026 年 FinOps 现状报告》调研 1192 家企业发现,78%的FinOps团队向CTO或CIO汇报,而 2023 年这个比例只有 61%。成本管理已经从运维的杂活,变成了技术管理者要直接盯的指标。
三、5个坑,每个我们都踩过
坑1:一刀切砍Limit,结果大面积OOMKill
第一次优化的时候,我们图省事,写了个脚本把所有服务的内存 Limit 统一压到 Request 的 1.5 倍。
结果第二天凌晨,一批 Java 服务集体 OOMKill 重启。
原因是JVM的堆内存和容器内存不是一回事。Java 应用除了堆,还有 Metaspace、线程栈、堆外内存、Direct Buffer。容器内存占用通常比 -Xmx 高出 30%-50%。
正确做法:
# Java 服务要显式配置容器感知,让 JVM 自己适配容器 Limit
-XX:+UseContainerSupport
-XX:MaxRAMPercentage=70.0# 堆占容器内存的 70%,留 30% 给非堆
教训:不同技术栈的内存模型差异极大,不能用一个统一比例去套。
坑2:只看平均值,大促当天被打爆
我们用 P95 定了 Request 之后上线,跑了两个月一切正常。
然后遇到一次业务高峰,实际流量是日常的 4 倍,服务直接雪崩。
问题在于P95反映的是”过去的常态”,不是”未来的极端”。而且如果 HPA 的 maxReplicas 也设得保守,弹性就形同虚设。
正确做法: - Request 用 P95,Limit和maxReplicas用历史峰值的1.5倍以内,- 重大活动前必须做压测,别信历史数据 - 关键服务配置 PodDisruptionBudget,防止缩容和驱逐同时发生
坑3:抢占式实例用在了有状态服务上
抢占式实例便宜是真便宜,只有按需实例的 20%-40%。我们一开始把一批 MySQL 从节点也放到了抢占池。
三天后,云平台回收了实例,主从复制中断,数据恢复花了大半天。
抢占式实例的正确使用范围:
可以用 | 不能用 |
无状态业务 Pod(多副本) | 数据库、消息队列等有状态服务 |
离线批处理、报表任务 | 单副本的关键服务 |
CI/CD 构建节点 | 需要固定 IP 的服务 |
可容忍中断的 AI 训练任务 | 长连接网关 |
而且必须配置:
# 用 taint + toleration 隔离抢占池,并保证足够的驱逐宽限期
tolerationSeconds:30# 收到回收通知后至少 30 秒优雅退出
坑4:优化过了头,SLO掉了
这是最隐蔽的坑。
有一段时间我们把 CPU Request 压得非常激进,账面利用率从 20% 提到了 50%以上,成本报表很好看。
但两个月后业务方反馈:接口 P99 延迟明显变长。
查下来是CPU Throttling——Limit 太低,Pod 想用 CPU 的时候被内核限流了。
# 监控 CPU 限流比例,超过 25% 就说明 Limit 给少了
rate(container_cpu_cfs_throttled_periods_total[5m])
/
rate(container_cpu_cfs_periods_total[5m])
教训:成本优化的边界是SLO,不是账单数字。省下的钱如果换来客诉和 SLA 赔付,就是负收益。
我们后来的做法是给每个服务先定 SLO(比如 P99 < 200ms、可用性 99.95%),优化过程中持续盯着这个指标,一旦劣化立刻回退。
坑5:没有组织机制,三个月后原样反弹
这个坑前面提过,这里再强调一次,因为它是成本优化失败的最主要原因。
FinOps 基金会的报告里有一句话很扎心:
从业者反映云优化的边际收益在递减:“我们已经解决了浪费中的’大石头’,现在面对的是大量更小的机会。”
意思是:一次性的运动式治理只能砍掉最明显的浪费,剩下的都是分散在各处的细节。没有持续机制,浪费会以新服务的形式重新长出来。
我们的解法就是前面说的三个机制:成本归属、上线卡点、月度复盘。核心是让成本变成研发流程里的一个常规检查项,而不是每年搞一次的专项运动。
四、一个可以直接抄的执行清单
如果你现在要启动成本治理,建议按这个顺序做:
第1周:摸底- [ ] 部署 KubeCost 或对接云厂商账单 API - [ ] 拉出 Top 20 高成本服务清单 - [ ] 计算集群整体的分配率 vs 实际利用率
第2-4周:低风险收益- [ ] 清理孤儿 PVC、无流量 LB、废弃节点 - [ ] 测试环境改为定时启停(下班关机,上班开机) - [ ] 下线确认无流量的服务
第2-3月:核心改造- [ ] 用 Prometheus P95 数据重定 Request - [ ] 灰度改造,先非核心服务,观察两周再推核心 - [ ] 配置 HPA,注意缩容稳定窗口 - [ ] 无状态服务迁到抢占式节点池
第3月起:机制固化- [ ] CI 加 Request/Limit 检查卡点 - [ ] 配 LimitRange 做命名空间兜底 - [ ] 月度成本报表 + 复盘会 - [ ] 建立 CPU Throttling、OOMKill 告警
不要做的事:- ❌ 一次性全量改造(必须灰度) - ❌ 只优化不监控(SLO 劣化了都不知道) - ❌ 用统一比例套所有技术栈(Java / Go / Python 内存模型完全不同)
五、写在最后
做完这一轮我最大的感受是:云成本优化本质上不是技术问题,是组织问题。
技术上,Rightsizing、弹性伸缩、抢占实例这些都是成熟方案,没什么黑魔法。难的是让每个团队愿意为自己的资源用量负责,难的是在”稳定”和”省钱”之间找到那条线,难的是把一次性治理变成常态机制。
另外补充一个背景数据,说明这件事为什么值得投入:Gartner 预测 2026 年全球 IaaS 支出将达2873亿美元,同比增长29.3%;其中 AI 优化基础设施支出423亿美元,同比增长96.4%。
云支出还在快速上涨,AI 基础设施又带来了新的成本结构(GPU 资源单价高、利用率更难预测)。成本管理这件事,未来几年只会越来越重要,不会越来越轻。
CNCF 2024 年度调查显示,Kubernetes 在生产环境的使用率已达80%,算上试点和评估中的组织达93%。K8s 已经是事实标准,也就意味着 K8s 的成本治理已经是每个平台团队的必修课。
如果你也在做成本优化,欢迎评论区交流你们踩过的坑——尤其是那些我还没踩到的。
数据来源
- Flexera《2026 年云现状报告》(State of the Cloud Report)
- FinOps 基金会《2026 年 FinOps 现状报告》(State of FinOps 2026),调研样本 1192 家企业
- 云安全联盟(Cloud Security Alliance)2026 年 6 月《Cloud Cost Optimization in 2026》
- SpendArk《The State of Cloud Waste 2026》
- CAST AI《The Cloud Waste Problem》2026 年 9 月
- Gartner 2026 年全球 IT 支出预测
- CNCF 2024 年度调查报告
- 阿里云 ACK 官方文档《在离线混部的技术架构、混部资源模型和单机 QoS 保障》
- AWS Containers Blog《Dynamic Kubernetes request right sizing with Kubecost》