☰
云原生成本治理实战:账单拆解与资源优化,月账单降低30%
2026/10/6 8:33:17 网站建设 项目流程

做过云原生运维的朋友应该都有这种感受:每到月底,盯着云厂商账单里那一串数字,CPU利用率却常年趴在个位数,心里就特别不是滋味。微服务上了Kubernetes之后,成本结构从“一台台虚拟机”变成了“一堆计算、存储、网络、负载均衡和托管服务杂费”,哪里烧钱、哪里浪费,光靠控制台首页根本看不清楚。今年我们团队专门做了一轮云原生成本治理,没有砍功能、没有降SLA,硬是把月账单压低了30%。这篇文章就是把这套从账单拆解、资源画像、弹性伸缩、存储网络再整治到自动化防反弹的完整过程记录下来,包括每一步背后的思考以及踩过的坑。不管你是运维工程师、开发负责人还是刚接触FinOps的同事,这轮实战里用到的工具、命令和配置都可以直接抄到自己的环境里试。

1. 先搞清楚云原生账单里的钱到底花在哪

1.1 账单结构拆解:不是只有节点费用那点事

从传统虚拟机迁到Kubernetes之后,账单结构会一下子复杂很多。传统云主机无非就是计算、带宽、磁盘三大项,而云原生环境至少分成四个层面:第一个是基础设施层,包括节点(Node)的计算资源、系统盘、数据盘、公网IP、负载均衡器;第二个是集群控制面费用,如果你用托管K8s,会有一笔固定的管理费;第三个是云上生态服务,比如日志、监控、镜像仓库、对象存储、托管数据库;第四个是业务层面,一个Pod跑在节点上,节点背后是云主机,Pod里又挂PVC和Ingress,但账单上不会直接写“这个Deployment花了多少钱”。

我当时做的第一件事,不是去找新的降本工具,而是把过去三个月的账单按“计算、存储、网络、负载均衡、其他服务”五个维度全部拉出来,先看占比。结果很典型:计算占了60%左右,存储和网络各占15%,剩下10%是SLB、NAT、日志之类的杂费。计算费用是大头,但注意存储和网络加在一起也有30%,这两块如果不去治理,就算把节点全部换成Spot,账单也降不下来。

拆完大类之后,必须继续往下拆到工作负载。我们用的是OpenCost,它能把Kubernetes里的命名空间、Deployment、Pod消耗的资源,按云厂商的定价换算成金额,再按标签归集。没有这个粒度,你只能看到一个集群总价,根本不知道是谁在烧钱。实际做完标签映射之后,发现费用最大的几个服务,不是我们想象中流量最高的核心业务,而是几个常年没人管的后台任务、每天都自动创建一个SLB的测试环境、以及一批从不清理的快照。

所以这里给第一个实用建议:千万别上来就调节点规格,先把账单按成本对象拆开。如果连自己的成本构成都说不清,后面优化就是在瞎猜。

1.2 隐藏浪费:每个团队都在“多申请一点”

云原生环境里最典型的浪费,不是某一次操作失误,而是所有人的“多申请一点”心态。开发同学写Deployment时,普遍习惯把requests设成4C8G,但实际业务可能只需要0.5C1G。这样做的根源有两个:一是大家潜意识里觉得云资源是无限的,申请多了总比不够好;二是不太清楚Kubernetes里requests和limits的含义,以为Pod申请了4C8G,节点上就真占用了4C8G。

实际上,Pod的requests只是调度器的“占位符”,它告诉调度器“这个Pod最少需要这么多资源,请你派到一个剩余容量足够的节点上”。limits才是硬限制,进程最多只能用这么多。如果requests虚高,调度器会以为节点资源已经满了,开始疯狂自动扩节点;而真正跑在上面的Pod CPU和内存利用率却低得可怜。这种“隐性浪费”特别可怕,因为每个服务看起来都不算过分,合在一起却导致集群不断膨胀。

我们当时统计过,全部服务平均CPU利用率只有8%,内存利用率不到30%,但集群已经因为requests堆积而自动扩出了不少新节点。要改变这个现象,必须把“申请量”和“实际用量”两套数据拉出来对比。Prometheus里现成的指标就是container_cpu_usage_seconds_total和container_memory_working_set_bytes。用这两个指标除以节点的可分配量,每天算一次“集群资源利用率日报”,发给各服务负责人。这一步根本不需要商业工具,一行PromQL加一个表格就能做。很多团队卡就卡在“不知道实际用了多少”,那成本优化自然无从谈起。

2. 成本优化的三板斧:规格、副本、伸缩策略

2.1 给每个Deployment做资源画像,重新设置requests和limits

有了真实水位数据,接下来就是一轮细致的资源画像工作。我们给所有核心服务重新确定了资源参数,原则很简单:requests取过去7天P95实际用量再乘以1.2倍;limits取过去7天P99用量再乘以1.5倍。为什么要留这个余量?因为直接贴着实际用量设置,遇到流量毛刺很容易OOMKilled或CPU throttling。但也不能像原来那样留3到4倍余量,那样优化等于白做。

举一个真实案例。订单服务原来请求量是4C8G,但实际P95只有1C2.5G。我们把它调整为requests=1C3G,limits=2C5G。很多人担心调低limits之后Pod会被杀,其实要看哪个指标:内存方面,如果容器实际工作集超过limits,会触发OOMKill;CPU方面,limits超了不会杀Pod,只是把CPU时间片限制在配额内,多余的请求会被限流,延迟可能升高。所以内存limits要稍微保守一些,而CPU limits可以按P99留足。改完之后盯着Prometheus观察了两周,OOMKilled次数为零,CPU限流次数从每秒几百次直接掉到零。

这里的关键经验是:不要一口气把所有服务全改了。我们使用Kustomize管理不同环境的资源覆盖,先改测试环境,做一轮压力测试,再通过GitOps流程发布到生产。生产环境的覆盖单独放在一个overlay里,每次只改两三个服务,观察至少一周。如果同时调整的服务太多,出了问题根本没法回溯是哪个参数引起的。

2.2 副本数和自动伸缩:HPA加VPA的配合

副本数也是一个巨大的成本点。我们看过一批服务,明明QPS只有个位数,却为了“高可用”常年维持3到5个副本。这种情况下副本数跟本不需要那么高,先降到2,再把弹性交给HPA。

HPA配置有几个关键参数容易踩坑。第一个是scaleDown的stabilizationWindowSeconds,默认300秒,会让缩容显得很慢,但如果设得太短,又容易在流量抖动时反复横跳。我们后来通过behavior字段分别控制:扩容时等待窗口短一点(60秒),缩容时等待窗口长一点(300秒)。第二个容易被忽略的点是,HPA必须基于容器的requests才能计算利用率。如果Deployment里没有写resources.requests,HPA采集到的CPU利用率数据就是空的,配置了等于白配。

再进阶一点,我们用VPA来辅助判断“单Pod到底该给多少资源”。VPA的recommender模式只出建议不自动修改,适合团队还没有完全信任自动化的阶段。它根据历史用量给出Recommended Requests,我们每周看一次建议,手工调整Deployment配置。VPA和HPA并不冲突,VPA管单Pod资源,HPA管副本数,但官方不建议让VPA和HPA同时基于同一个指标,否则两者会互相打架。我们后来把VPA建议应用到资源上之后,HPA只单独管CPU,内存不参与HPA,效果稳定很多。

2.3 用好竞价实例(Spot)和弹性节点池

如果集群里所有节点都是按量付费,成本优化永远谈不上彻底。云厂商的竞价实例(Spot Instances)折扣通常可以达到50%到70%,但风险在于随时可能被回收。哪些负载适合跑在Spot上?无状态Web服务、异步任务Worker、CI构建节点、大数据批处理,这些都可以。不适合的包括有状态数据库、依赖本地持久化磁盘的服务、以及需要长期连续运行的关键业务。

我们当时先把测试环境全部迁移到了Spot节点池,节点费用直接省了近一半。生产环境也建了一个Spot节点池,配合PodDisruptionBudget和拓扑分布约束,让无状态服务混部上去。具体做法是给节点池打spot=true的标签,在Deployment里用nodeSelector选择,再用podAntiAffinity确保同一服务的副本分布在不同节点。如果某个服务是消息消费者,Spot回收时可能会中断处理,需要单独评估是否能接受。

还有一点很重要:Spot节点的价格在不同可用区之间是波动的,最好使用云厂商原生的节点组混合模式,让它自动选择最低价的可用区去调度。生产环境不要把所有节点都换成Spot,至少要保留一个小的按量节点池做兜底。这样当Spot池被大规模回收或者资源不足时,HPA扩容出去的Pod会落到按量池里,虽然成本会高,但至少服务不会悬空。

3. 存储、网络和负载均衡的隐藏成本

3.1 存储优化:别让快照和云盘成了隐形黑洞

云原生的存储成本往往比想象中高很多。我们之前每个Deployment都挂独立的云盘,有些只是用来写日志,而且每个环境都开启了每小时快照,保留7天。月底一看,快照费用居然跟云盘正本差不多。后来我们做了三件事,效果立竿见影。

第一,调整持久化方案。日志、临时文件这类数据尽量不要持久化到云盘,改为上报到对象存储或者Elasticsearch,Pod本身使用emptyDir就够了。第二,按实际IO需求选云盘类型。普通业务用通用型SSD甚至HDD,只有高频数据库才需要高性能SSD,不要所有盘都默认最高配置。第三,重新设计快照策略:保留3天内的每小时快照、7天内的每日快照、30天内的每周快照,把那些没用的长期快照全部清理掉。这几项做完,存储账单缩水了40%。

这里有一个Kubernetes的固有坑:PVC扩容只能大不能小。一旦PVC创建时指定了容量,后面想缩小就必须重建PVC并迁移数据,非常麻烦。所以我们要求所有新建PVC先做需求评估,不要图省事直接给100GB。如果需要缩减,只能通过新PVC加数据迁移的方式处理,这会涉及业务中断,一定要提前规划好维护窗口。

3.2 网络流量和负载均衡:SLB不是白菜价

网络费用的两大来源,分别是负载均衡器和公网流量。很多团队忽视了云厂商负载均衡器的计费方式,通常按LCU(Load Balancer Capacity Unit)计费,也就是按并发连接数、流量、规则数综合计算。如果你给每个微服务单独创建一个SLB,费用会成倍上升,而且这些SLB很多还只是给内部调用用,完全没必要。

我们做的第一件事就是合并入口。Kubernetes集群内统一使用Nginx Ingress,而不是给每个服务单独挂云负载均衡器。Nginx Ingress部署在集群内部,前面只需要挂一个SLB,之后通过Ingress资源把不同域名或路径转发到不同Service。这样虽然多了一层Nginx转发,但省掉了大量的SLB实例费。如果你特别担心Nginx成为瓶颈,可以给Ingress Controller单独做HPA,或者直接使用云厂商的ALB Ingress Controller,但千万不要回到“每个服务一个SLB”的老路。

公网流量部分,我们规定内部调用一律走内网DNS,数据库连接串全部改成内网地址。有些外部调用必须经过公网时,尽量在出口侧做统一NAT网关,并且给大文件下载走对象存储的CDN,而不是从应用服务器转发。另外那些流量型服务,建议开启云厂商的TCP优化和流量压缩,能有效降低出网流量费用。这块优化完成后,网络费用整体下降了25%。

4. 成本可视化、预算告警和自动化治理

4.1 用OpenCost/Kubecost把成本分账到业务线

光看云厂商账单,最多只能定位到云主机级别,没法关联到具体Deployment。我们需要的是让每个业务负责人打开面板,就能看到自己这个月烧了多少。开源工具OpenCost正好做这件事,它从Prometheus拉取容器资源指标,再按照配置好的云定价表计算成本,最终按命名空间、Deployment和标签汇总。

我们在集群里部署了一套OpenCost,配合Grafana做成本大盘,每几个小时刷新一次,能看到每个命名空间、每个Deployment在24小时内的成本排行。后来也试过Kubecost,它在网络成本分摊和多集群聚合上更强,但核心思路完全一致。最关键的是,我们给每个命名空间设了一个预算基线,比如某业务线每月预算8000块,超过基线的20%就触发告警。这个动作带来的心理效应非常明显,以前“没人觉得自己用了多少”,现在每个服务负责人都开始主动盯自己的成本曲线。

这里有个很现实的坑:成本数据允许有一定误差,但不能失真。OpenCost里的节点单价默认是目录价,而企业实际购买通常有折扣。我们要在配置里把折扣率填进去,或者定期用真实账单校准。否则优化一段时间后,你会发现工具上显示的成本和云厂商账单对不上,团队会立刻对工具失去信任,后面的治理就推不动了。

4.2 用Prometheus和Alertmanager做成本异常告警

成本告警不能等到月底看账单,必须实时感知。我们自己在Prometheus里构造了一个监控指标,比如每个命名空间的日成本估算,然后设置两类告警。第一类是相对告警:当天成本比近7天日均成本增长超过50%并且持续2小时,触发warning。第二类是绝对告警:某个命名空间的周成本超过了预算的80%,就发出通知。通知统一走Alertmanager推到运维值班群,关键成本项直接电话告警。

告警触发后,我们一般会看两条线。一是流量是不是真的上涨了,比如拉了促销活动;二是是否有异常的Pod副本数。通过PromQL查一下Deployment的期望副本数和当前副本数,如果发现副本数暴增,很可能是HPA配置的指标被某个突发流量打爆,或者某个服务在大量重试。我们后来制作了一个“成本巡检单”,把常见嫌疑项列出来,比如新发布的服务没写资源限制、某条HPA被误删、上一周的Pod还卡在Pending状态导致额外节点被拉起。按这个清单过一圈,基本能快速定位问题。

4.3 用Ansible和Kustomize统一管理资源配置,防止反弹

成本优化最怕的不是优化不动,而是优化完之后“反弹”。今天把某个服务从4C8G降到1C2G,明天开发为了排查问题又临时改成8C16G,月底一看还涨了。要从机制上防住反弹,必须把资源配置纳入标准发布流程。

我们用Kustomize管理多环境配置,生产环境的overlay固定了每个服务的requests和limits,不允许在仓库外手工改。CI流水线里加了一个资源校验脚本,扫描待发布的YAML文件,如果发现没有resources字段,或者requests明显大于历史P95用量,就直接打回让开发重新填写。这个脚本用Python写,大概几十行,本质是读YAML加比对Prometheus数据。

另外还用Ansible写了一套巡检剧本,定期在集群里扫描各种“僵尸资源”:没有设置resources的Pod、超过30天未使用的PVC、副本数已经为0但仍占用负载均衡器资源的Service。Ansible直接调用Kubernetes API获取信息,再把结果汇总成周报告。这套流程坚持了一个月之后,新上线的服务默认自带合理资源配置,几乎没有再出现大规模的规格反弹。

5. 实战中的坑与排查经验速查表

5.1 调整limits后服务变得很慢,怎么办?

这是降配之后最常遇到的问题。当你把limits从高的值压下来时,CPU限流(throttling)会把Pod可使用的CPU时间片切断,表现就是服务延迟明显上升、吞吐量下降。排查时看Prometheus指标container_cpu_cfs_throttled_periods_percentage,如果长期高于5%,说明limits设得太紧了。

解决方法有两条:一是把limits往上调,留出更多余量;二是去掉CPU limits,只保留requests。去掉limits要谨慎,因为它会破坏QoS等级,还可能导致某个Pod跑满节点影响邻居。我们的建议是分批次调整:第一周只降内存limits,第二周再降CPU limits。如果同时降两个维度,出了问题很难定位是哪一个引起的。

对Java类服务要额外注意。JVM默认认为容器内存就是物理机内存,如果不显式设置堆大小,它可能会根据节点内存自动计算堆的初始值,导致容器OOMKilled。解决办法是在启动参数里显式配置-Xmx,比如容器内存limits是4G,就把-Xmx设为2G或3G,留一部分给元空间和线程栈。我们当时有个服务连续OOM了三次,排查半天才发现是JVM自适应堆导致的。

5.2 HPA扩缩容抖动怎么处理?

HPA抖动通常表现为副本数忽多忽少,不仅影响服务稳定,还会让云节点费用产生不必要的波动。常见原因有三个:一是指标采集周期太短,Prometheus默认15秒抓一次,而HPA默认每15秒评估一次,指标一波动就会误判;二是突发流量触发了扩容,但稳定后又被立即缩容,形成震荡;三是Pod启动时间本身长,副本还没就绪就被认为闲置,触发了缩容。

应对方案有三个。第一,把HPA评估周期调长,比如--horizontal-pod-autoscaler-sync-period设为60秒。第二,对业务指标先做rate聚合,窗口拉到5分钟均值再作为HPA指标,过滤掉秒级抖动。第三,配置behavior参数,扩容时等待窗口可以短一些(60秒),缩容时等待窗口长一些(300秒),并且限制每次缩容的副本数量,比如最多缩1个。这样即使流量突然下去,副本也不会疯狂缩水,下一波高峰到来时还能有足够余量。

5.3 Spot实例回收导致Pod重建风暴?

Spot节点被云厂商回收之前,通常会提前两分钟发出中断通知。如果没处理这个通知,Pod会被直接强杀,严重时会有一大片Pod同时重建,集群内Pod数量瞬间翻倍,造成极大的调度压力。我们当时部署了一个DaemonSet来监听Spot中断通知,收到之后立刻对节点打污点(taint),同时给Pod设置优雅终止宽限期,让服务有足够时间完成请求处理并退出。

另一个问题是,Spot节点池如果资源不足,新调度出来的Pod会落到按量节点池,成本瞬间反弹。要能从监控上区分这个现象,我们对节点池打了标签,在成本大盘里单独展示按量池上的Pod数量。看到按量池Pod数量异常上涨,就知道该扩容Spot池了。我们不建议为省钱完全关闭按量池,生产环境至少保留一个很小的按量池给关键Pod做兜底,否则一次大规模Spot回收就会拖垮一套服务。

6. 我个人的一点体会

折腾完整轮成本优化之后,给我留下最深印象的其实不是那30%的数字,而是整个协作方式的改变。最初,我们只是把成本报表发给各业务线,对方第一反应是“这是不是算错了”。等我们把OpenCost的拆分逻辑和Prometheus真实用量截图一起发过去,并告诉他们“你这个服务请求量是实际使用量的4倍”时,业务负责人才开始认真对待。后来,好几个团队主动跑来问怎么调整参数,因为看到了自己服务的成本排行,也体会到了降本带来的预算空间。

关于优化节奏,我想多说一句:宁慢勿快。我们每两周做一轮,每轮只触碰一部分服务,改完至少观察一周。虽然整体花了两个月时间,但没有因资源调整引起过一次故障。这种“慢”换来的信任,比账面上那30%更珍贵。如果谁想一口气把所有服务全部降配,大概率会在某个繁忙时段出问题,反而让团队对成本优化产生抵触。

最后再分享一个小技巧。如果你还没搭建Kubecost或OpenCost,可以先写几个PromQL挂到Grafana上,把每个命名空间的CPU和内存请求量与使用量趋势展示出来。当一个数字开始变得可测量、可比较时,所有人都会自动去关注它。这是一种机制上的成本意识,比任何一条优化命令都管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询