在多租户Kubernetes集群中,多个业务部门共享同一个计算池,资源按需伸缩。月底拿到云账单时,平台团队只能看到集群整体费用,却无法回答每个部门实际消耗了多少。没有清晰的分摊机制,成本管理就停留在估算阶段,扩容申请、预算审批和容量回收都缺少数据支撑。真正可落地的方案不是简单把总账单除以部门数,而是要把资源用量与业务归属对应起来,形成可追溯、可解释的按月核算数据。

本文从分摊模型设计、计量采集、计算过程和报表闭环四个部分展开,重点讨论如何在不显著增加运维负担的前提下,把集群费用拆到业务部门,并让核算结果真正服务于预算管理和资源优化。
一、分摊之前:把成本对象和归属关系定义清楚
集群成本分摊的前提是资源可以被标识到具体业务部门。命名空间是Kubernetes中最容易落实的隔离单元,建议一个部门或一个成本中心使用独立命名空间。除此之外,还需要在Namespace、Deployment、StatefulSet、PVC等对象上统一打标签。常见的标签包括team、cost-center、biz-line、environment。平台团队可以制定准入策略,例如OPA或Kyverno强制要求新命名空间必须带cost-center标签,否则拒绝创建。这样短期的成本归集不会因为遗漏标签而变成糊涂账。
apiVersion: v1
kind: Namespace
metadata:
name: order-service
labels:
team: order-platform
cost-center: dept-1008
biz-line: ecommerce
environment: production
标签只能说明资源的归属,还不能直接回答花了多少钱。成本核算需要两个维度:资源使用量和资源单价。使用量来自Prometheus、kube-state-metrics和云厂商监控数据,单价来自云账单、机房折旧或内部资源定价。CPU、内存是最基础维度,GPU卡的单价高且独占性强,必须单独计量;存储需要区分SSD、HDD和对象存储,网络出口流量在混合云场景下也可能是大额费用。建议先建立计量指标清单,避免一开始追求全维度而导致工程复杂度失控。
分摊规则通常分为三类:按实际使用量、按申请量、按固定比例。实际使用量贴近真实消耗,但受调度波动影响;申请量适合预算管理,因为资源预留决定了集群容量;固定比例适合共享平台组件成本分配。多数企业采用混合规则,例如基础资源按实际使用量分摊,共享组件按部门Pod数或请求量加权分摊,空闲资源由平台预算统一承担。规则越简单,业务部门越容易接受,后续争议越少。
二、计量数据采集与成本计算
采集侧的核心是Prometheus。kube-state-metrics暴露kube_namespace_labels、kube_pod_container_resource_requests等指标,可以按namespace聚合。容器真实用量则需要通过cAdvisor或node exporter计算,PromQL可以计算某个命名空间过去30天的CPU核时平均值。核时是比较直观的单位:1核使用1小时等于1核时,也可以扩展为GB时用于内存。下文示例假设集群CPU单价为每核时0.35元,内存每GB时0.05元,具体价格可以根据云资源账单倒推。
# 按命名空间统计CPU平均使用核数(30天)
sum by (namespace) (
rate(container_cpu_usage_seconds_total{container!="", namespace!=""}[5m])
) * 1000
得到平均核数后,计算公式为:月度CPU费用 = 平均使用核数 × 24小时 × 当月天数 × 单价。内存同理。如果某些业务存在明显波峰,可以取P95或加权平均值,避免短时扩容造成费用误判。数据可以每天汇总,写到时序数据库或关系型数据库,月底统一生成账单。下面用SQL演示把日粒度数据汇总成月账单。
SELECT namespace, cost_center, team, SUM(cpu_core_hours) * 0.35 AS cpu_cost, SUM(memory_gb_hours) * 0.05 AS mem_cost, SUM(gpu_hours) * 8.00 AS gpu_cost, SUM(storage_gb_hours) * 0.0004 AS storage_cost FROM namespace_daily_usage WHERE stat_date BETWEEN '2024-01-01' AND '2024-01-31' GROUP BY namespace, cost_center, team;
这里没有把复杂的分摊系数写进SQL,因为共享成本通常不属于任何单一命名空间。网关、监控、日志、服务网格等基础设施组件可以单独归入platform命名空间,再按各业务命名空间的总Pod运行小时数比例分摊。例如订单中心Pod运行小时数占全部业务Pod的12%,那么平台组件成本的12%就计入订单中心。这套计算最好放在Python或Go程序中完成,而不是依赖一条SQL,便于处理多级分摊和补录数据。
GPU成本不能简单按Pod请求量分摊。一块A100卡可能同时被多个容器共享,但不同工作负载的显存和算力占用差异很大。建议通过DCGM或云监控采集GPU利用率、显存占用,将GPU费用拆成时间片与显存占用综合权重。对于独占GPU的训练任务,直接按Pod运行时长×卡数×单价计算更加准确。存储成本还需注意PV回收策略,已删除Pod但未释放的云盘也会计费,这些都需要定期巡检。
三、账单输出与业务部门核算
账单不应该只是数字罗列,要和部门预算、项目阶段、负责人关联。建议每月生成一份成本明细表,包含部门、成本中心、命名空间、资源类型、费用、环比变化、预算额度、预算偏差率。环比变化可以快速暴露异常增长,比如某个团队灰度发布新版本导致内存占用翻倍。预算偏差率帮助财务和业务负责人判断是否需要追加资源或进行架构优化。报表可以通过定时任务生成CSV或接入BI工具。
| 部门 | 成本中心 | CPU费用 | 内存费用 | GPU费用 | 存储费用 | 分摊总额 | 预算偏差 |
|---|---|---|---|---|---|---|---|
| 订单平台 | dept-1008 | 8420 | 3100 | 0 | 1260 | 12780 | +6.2% |
| 搜索推荐 | dept-1009 | 5100 | 4300 | 12800 | 890 | 23090 | -3.1% |
把成本数据推送给业务部门之前,平台团队需要先做一轮合理性校验。比如同一个命名空间内,数据库和应用混跑,数据库的IO成本可能比计算成本更高,如果仍然按CPU和内存分摊,会低估数据库团队费用。比较实用的做法是给不同类型的Workload打上app-type标签,将数据库、消息队列、缓存等有状态服务单独计量。若使用云数据库RDS或Redis,则直接读取对应云资源账单按标签拆分,不需要混入集群账单。
共享平台的成本处理是最容易引发争议的地方。监控系统为所有部门服务,但没有人愿意为看似不直接产生业务价值的组件买单。此时可以设置成本池,由技术委员会确认分摊比例,并把规则写进制度。例如集群管理成本20%由平台承担,80%按业务资源使用量分摊。规则每一季度评审一次,既保证公平性,又避免过度设计。
四、落地中的关键问题和优化闭环
第一是标签治理必须前置。很多团队在集群创建初期先不规划命名空间和标签,等业务规模上来以后再补标签,工作量翻倍且容易造成历史数据缺失。建议在CI/CD发布流水线中增加标签校验,创建资源时未带cost-center的直接拦截。对于已经上线的服务,可以按Git仓库归属、owner信息批量回填,但需要与业务负责人核对,避免映射错误。
第二要区分成本核算与资源容量管理。核算的目的不是向业务部门索取费用,而是让资源使用显性化。部分团队会混淆名义成本和现金成本,例如集群节点已经购买,空闲资源仍然折旧。建议在账单中同时展示现金成本和资源使用率,让管理者看到哪些部门申请了大量配额但实际使用率很低,推动配额回收。成本分摊只是手段,提高整体资源效率才是目标。
第三是自动化闭环。手工月底出账单容易延迟且口径不一致。可以通过定时任务每天采集指标,月底自动计算并推送邮件或企业微信。对于异常增长,可以设置阈值告警,例如单命名空间CPU费用环比增长超过30%时通知负责人。更进一步,将成本数据回流到容量规划系统,预测下季度资源缺口,避免拍脑袋式扩容。
最后,实施成本分摊方案不需要一步到位。初期可以从CPU和内存两个维度入手,跑通从标签到账单的流程,再逐步加入GPU、存储和网络。与财务部门对齐口径同样重要,把分摊结果纳入部门成本中心后,业务部门才会真正重视资源使用效率。总结下来,集群成本分摊的核心不是算法多复杂,而是归属清晰、计量可靠、规则透明、反馈及时。这套方法落地后,云资源费用就不再是一笔说不清的公共开销,而是每个团队都能看懂的运营数据。

