OpenCost 成本核算规范深度解析:Kubernetes 工作负载与集群成本的厂商中立计量模型
【免费下载链接】opencostCost monitoring for Kubernetes workloads and cloud costs项目地址: https://gitcode.com/GitHub_Trending/op/opencost
导读
本文以 spec/README.md 与 spec/opencost-specv01.md 为主体,系统解读 OpenCost 项目提出的厂商中立 Kubernetes 成本计量与分摊规范:从"集群总成本(Total Cluster Costs)"的层层分解,到资产成本(Asset Costs)、工作负载成本(Workload Costs)、闲置成本(Idle Costs)与共享成本(Shared Costs)的精确定义与计算公式,并结合仓库源码验证其落地实现。读完本文,你将掌握 OpenCost 成本模型的完整术语体系、可复现的核算公式,以及规范中的关键约定(如max(request, usage)、资源归一化定价、指标采样清单)在真实代码中的对应实现。
一、规范背景:为什么要一套厂商中立的成本核算标准
Kubernetes 支撑着复杂且高度动态的容器化工作负载部署:工作负载通常是瞬态的,消耗的集群资源量也在持续变化。这种灵活性在赋予团队构建强大解决方案能力的同时,也给共享 Kubernetes 环境下的资源利用率与成本度量带来了复杂性——集群资源被多个租户共享,成本如何归属变得模糊不清。
随着组织内 Kubernetes 采用规模扩大,成本度量的准确性会上升为业务关键问题。OpenCost 规范的定位正是提供一套**与云厂商无关(vendor-agnostic)**的计量方法论,用于准确测量 Kubernetes 集群的成本,并将其分摊到集群内托管的租户(tenants)上。该规范由 Kubernetes 实践者社区维护,欢迎各方贡献(见 spec/opencost-specv01.md 的 Introduction 部分)。规范的"最新发布版本"固定在 spec/opencost-specv01.md,是后续所有讨论的基准。
二、成本模型的根基:Total Cluster Costs 的三层分解
规范首先给出整个成本模型的基石——集群总成本(Total Cluster Costs),它代表运营一个 Kubernetes 集群所需的全部成本,并由两个部分构成:
| Total Cluster Costs | = | Cluster Asset Costs | + | Cluster Overhead Costs |
|---|
- 集群资产成本(Cluster Asset Costs):集群内可直接观测实体(nodes、persistent volumes、attached disks、load balancers,以及网络入/出流量成本)所直接产生的费用。从财务会计角度看,这相当于计量产品成本时的"销货成本(Cost of Goods Sold)"。
- 集群开销成本(Cluster Overhead Costs):运营集群全部资产所需的开销,例如集群管理费(Cluster Management Fees)。从财务视角看,这对应"销售、一般及行政费用(SG&A)",即间接成本。
而集群资产成本又可以进一步切分为两类计费模式:
| Total Cluster Costs | = | Resource Allocation Costs(全部资产) | + | Resource Usage Costs(全部资产) | + | Cluster Overhead Costs(整个集群) |
|---|
- 资源分配成本(Resource Allocation Costs):按照资源的预置时长累积的费用,与是否被使用无关。典型如 CPU 的小时费率(hourly rate)。
- 资源使用成本(Resource Usage Costs):按单位用量累积的费用。典型如按出站字节数计费(cost per byte egressed)。
单个资产(Asset)的成本即其资源分配成本与资源使用成本之和,例如:一个 Node 的成本 = CPU 成本 + GPU 成本 + RAM 成本 + 节点网络成本。
这一分层结构在仓库核心模型中有直接对应。在 core/pkg/opencost/allocation.go 中,Allocation 结构体即为上述"分配成本"建模的核心载体,其中CPUCostIdle、GPUCostIdle、RAMCostIdle字段(第 66、70、87 行)分别承载 CPU/GPU/RAM 的闲置成本分量,用于在后续计算中实现"资产成本 = 工作负载成本 + 闲置成本"的恒等式。
三、Cluster Asset Costs:资产成本的属性模型与测量示例
规范定义:集群资产(Cluster Assets)是 Kubernetes 集群内可直接观测、且因资源使用而直接产生成本的实体。每个符合规范的资产必须包含至少一个成本组件,且该组件必须携带 Amount、Unit、Rate 属性以及 TotalCost 值。
3.1 资源分配成本的测量属性
| 属性 | 类型 | 说明 | 示例 |
|---|---|---|---|
| Amount | float | 资产预留的资源量 | 2 CPU cores |
| Duration | float | 分配周期的起止时间,按小时计 | 24 hours |
| Unit | string | Amount 的计量单位 | CPU cores |
| HourlyRate | float | 每单位小时的成本 | 每 CPU 小时 $0.20 |
| Total Cost | float | Amount × Duration × HourlyRate | — |
3.2 资源使用成本的测量属性
| 属性 | 类型 | 说明 | 示例 |
|---|---|---|---|
| Amount | float | 资源使用量 | 1GB 互联网出站流量 |
| Unit | string | Amount 的计量单位 | GB |
| UnitRate | float | 每单位成本 | 每出站 GB 的美元数 |
| TotalCost | float | Amount × UnitRate | — |
3.3 典型资产的成本测量示例(24 小时窗口)
规范以"在指定时间窗口(如 24 小时)内、采用常见计费模型"为前提,给出了以下资产的测量方法:
- Nodes(节点)
- CPU 分配成本:
cores = avg_over_time(cpu) by (node),duration = end running - start running [hrs],price = 云厂商定义或自定义定价表 [$/core-hr],total cost = cores × duration × price。 - RAM 分配成本:
ram bytes = avg_over_time(GB) by (node),duration同上,price = 云厂商定义或自定义定价表 [$/GB-hr],total cost = ram bytes × duration × price。
- CPU 分配成本:
- Persistent Volumes(持久卷)
Disk Size = avg_over_time(GB) by (pv),Price = 云厂商定义或自定义定价表 [$/GB-hr](通常取决于磁盘类型、IOPS、备份大小),持久存储挂载在 pod 级别。
- Attached disks(附加磁盘)
- 同样按
avg_over_time(GB) by (pv)计量磁盘大小,价格通常取决于磁盘类型、IOPS、备份大小;对应的是节点上每个 pod 使用的临时存储(ephemeral storage)。
- 同样按
- Load balancers(负载均衡器)
- 使用成本:
amount = 入站字节数,price = 每入站字节价格。 - 分配成本:
rules = 定义的转发规则数,price = 每条转发规则的平均价格。
- 使用成本:
- Overhead Costs(开销成本)
- 集群管理费:云厂商通常按小时收取的费用。
- 运维费用(Operator fees):分配给集群运维的潜在 DevOps 团队成本。
从仓库实现看,资产侧的定价与计量逻辑沉淀在 core/pkg/pricing 模块中:price.go定义资源计价原语,node.go、persistentvolume.go、network.go分别对应节点、持久卷与网络资产的成本计算,store.go与memory.go提供价格存储与内存缓存,支撑"云厂商定义或自定义定价表"两种价格来源。各云厂商的定价接入(如 pkg/cloud/aws、pkg/cloud/gcp)可视为规范"vendor-agnostic"定位的工程化实现。
四、Workload Costs:以max(request, usage)为核心的工作负载成本
**工作负载(Workloads)**被定义为"资产成本所承诺(committed)归属的实体"。某些资源只产生使用成本(Usage Costs),而另一些资源即使未被使用也独立产生分配成本(Allocation Costs)。因此规范给出了一条关键约定:
当资产存在资源分配成本(如 CPU、GPU)时,工作负载成本应理解为max(request, usage)。
这条公式的本质是:把kube-scheduler直接预留(reserved)或分配(allocated)的成本计入工作负载——即使容器实际用量低于其请求量,也按请求量计费;反之若用量超过请求量,则按实际用量计费。同时,工作负载成本应在尽可能低的层级计算,即容器(container)级,随后可按任意维度向上聚合。
4.1 各资源类型的测量口径
| 资源类型 | 测量方式 |
|---|---|
| CPU | 请求量与使用量中的较大者,以 cores 或 millicores 计量 |
| Memory | 请求资源与使用内存中的较大者,以 bytes 或 gigabytes 计量 |
| GPU | 请求资源与使用资源中的较大者,以 cores 计量 |
| Storage Volume | PVC(Persistent Volume Claim)请求的存储容量,以 bytes/gigabytes 计量,挂载于 Kubernetes pod 级 |
| Network | 跨可用区、跨区域或跨公网的入/出流量字节数(因云厂商而异) |
| Load Balancer | 使用的负载均衡器数量,加上连接数与字节数(因云厂商而异) |
4.2 支持的成本聚合维度
一个完整的 OpenCost 规范实现应支持以下工作负载成本聚合维度:
container→pod→deployment→statefulset→job→controller name→controller kind→label→annotation→namespace→cluster
这一"从容器逐级上卷到集群"的聚合链条,在仓库的 core/pkg/opencost/allocation.go 与 core/pkg/opencost/summaryallocation.go 中均有对应实现;Allocation 聚合逻辑支持按标签、注解、命名空间等维度分组,与规范列出的维度一一呼应。
4.3 源码级验证:max(request, usage)的真实实现
规范中的核心公式并非停留在纸面,在 pkg/costmodel/costmodel.go 第 760-779 行可以直接找到它的实现:
if req != nil && used != nil { x1 := req.Value if math.IsNaN(x1) { log.Debugf("NaN value found during %s allocation calculation for requests.", allocationType) x1 = 0.0 } y1 := used.Value if math.IsNaN(y1) { log.Debugf("NaN value found during %s allocation calculation for used.", allocationType) y1 = 0.0 } result = []*util.Vector{ { Value: math.Max(x1, y1), // ← 规范中的 max(request, usage) Timestamp: math.Max(req.Timestamp, used.Timestamp), }, } ... }可以看到实现细节与规范完全对齐:请求量(request)与使用量(usage)两者取较大值(math.Max(x1, y1)),同时对 NaN 做防御性处理(归零),并在两者都无数据时将分配量置 0。这印证了规范中"对拥有资源分配成本的资产,工作负载成本 = max(request, usage)"的约定在 OpenCost 计算引擎中是逐向量执行的。
五、Shared Costs:可选分摊的共享成本
共享工作负载成本(Shared Workload Costs)、集群闲置成本(Cluster Idle Costs)与开销成本(Overhead Costs),是组织可以选择性地在各租户之间分摊的常见成本类型。最典型的例子是系统工作负载成本,例如kube-system命名空间下的 pod,它们为所有租户提供基础服务。规范给出的常见分摊方法有三种:
- 均匀分摊:在所有其他租户之间均匀分配(uniformly)。
- 按比例分摊:按租户对集群资产成本的消费比例分摊(proportionate to a tenant's consumption of Cluster Asset costs)。
- 自定义指标分摊:例如按网络出站字节数等自定义指标分摊(custom metric)。
一个完整的规范实现应当支持多种共享成本分摊方式。在 OpenCost 代码中,这一能力对应 Allocation 聚合选项中的共享(share)机制:core/pkg/opencost/allocation.go 定义了ShareWeighted("__weighted__",按权重共享)、ShareEven("__even__",均匀共享)与ShareNone("__none__",不共享)三种模式(第 39-48 行),ShareIdle、ShareNamespace等选项控制具体共享目标,computeIdleCoeffs等函数实现共享系数的计算——与规范列出的"均匀 / 按比例 / 自定义"方法一一对应。
六、Idle Costs:闲置成本的精确度量
闲置成本(Idle Costs)可以在资产/资源级与工作负载级两个层面计算。
6.1 资产级闲置成本
资产闲置成本表示"集群资产成本"与"被分配或消耗的资源成本"之间的成本加权差:
| Cluster Idle Cost | = | ( Cluster Asset Costs − Workload Costs ) |
进而可以计算集群闲置百分比(Cluster Idle %):
| Cluster Idle % | = | Idle Cost / Resource Allocation Costs |
关于闲置成本的计算范围,规范还给出两点重要约定:
- 资产闲置成本可以按单个资产、资产组、集群,以及单个资源(如 CPU)分别计算。
- 严格按用量计费的资源可以视为 100% 效率,但不应纳入集群闲置百分比的测量(因为使用成本永远被消耗,不存在"闲置"),这一点在规范的注释 [^1] 中有明确说明。
6.2 工作负载级闲置成本
工作负载闲置成本是对"已请求但未使用"资源的成本加权度量(cost-weighted measurement of requested resources that are unused)。它可以基于任意 Kubernetes 工作负载分组计算,例如容器、pod、标签、注解、命名空间等。
6.3 源码验证:闲置成本的计算与再分配
闲置成本的计算与再分配逻辑在仓库中有完整实现:
- 在 pkg/costmodel/costmodel.go 第 2137-2170 行,闲置成本被精确实现为"资产总成本减去分配总成本"的差值:
cpuIdleCost := assetTotal.TotalCPUCost() - allocTotal.TotalCPUCost() gpuIdleCost := assetTotal.TotalGPUCost() - allocTotal.TotalGPUCost() ramIdleCost := assetTotal.TotalRAMCost() - allocTotal.TotalRAMCost() // 对每个分量:若为负则归零 ... CPUCost: cpuIdleCost, GPUCost: gpuIdleCost, RAMCost: ramIdleCost,- 在 core/pkg/opencost/allocation.go 中,闲置分配(Idle Allocation)作为 Allocation 的一种特殊类型存在:
IdleSuffix = "__idle__"(第 28 行)用于标识闲置分配,IsIdle()方法(第 1174-1180 行)通过名称是否包含该后缀来判断;聚合选项ShareIdle(第 1545 行)决定闲置成本是"不共享(ShareNone)""均匀共享(ShareEven)"还是"加权共享(ShareWeighted)",这正对应规范"共享成本可选择性分摊"与"闲置成本可分摊给租户"的设计。cpuCostIdle、gpuCostIdle、ramCostIdle三个字段则承载聚合后每个分配对象的闲置成本分量。
七、Pod 状态对成本归属的影响
Pod 的状态会直接影响成本能否被归属、资源是否被视为"已分配"。规范给出了 OpenCost 模型的处理约定:
| 状态 | 成本分配方式 | 状态 |
|---|---|---|
| Running | Max(Usage, request) | 已实现(Implemented) |
| ImagePullBackOff | Request | 当前不计费(Currently no charge) |
也就是说:对于 Running 状态的 Pod,按"使用量与请求量的较大值"分配成本;而对于ImagePullBackOff状态的 Pod,OpenCost 模型不为其分配资源成本——即使这些资源在集群中被预留。
八、Appendix A:资源价格的推导与归一化(Pricing Normalization)
许多云厂商直接在计费模型中提供资源的小时成本。规范建议在此类场景下,直接使用每个资源的**完全摊销净成本(fully Amortized Net Cost)**作为输入。
当云厂商没有明确提供 RAM、CPU 或 GPU 的单独价格时,OpenCost 模型需要自行推导。规范的推荐做法是:按资源族(family)使用 CPU、GPU、RAM 等价格输入的边际费率,采用可伸缩的比例。具体计算思路是:
- 确保各资源分量之和等于该资产(如节点)基于厂商计费费率的总价。
- 当资源分量之和高于(或低于)节点价格时,保持输入价格之间的比例不变,仅调整总值。
规范给出的示例:假设预置了一个节点,含 1 GPU、1 CPU、1 GB RAM,月成本 $35。如果基于该实例族内各实例的平均边际成本,GPU 基价为 $30、CPU 基价为 $30、RAM 每 GB 为 $10,则这些输入会被归一化为GPU $15、CPU $15、RAM $5,使得三者之和恰好等于节点成本 $35。注意归一化后 GPU 与 CPU 的价格仍保持为 RAM 价格的 3 倍——比例关系不变,这正是"保持比例、调整总值"的含义。
这一"价格归一化"思想在仓库的定价模块中可找到对应支撑:自定义定价表通过 core/pkg/pricing/store.go 加载,core/pkg/pricing/node.go 与 core/pkg/pricing/resource.go 定义了按资源族/实例类型核算 CPU、RAM、GPU 单位价格的能力,与规范"基于边际费率按族推导"的建议一致。
九、Appendix B:推荐的 Kubernetes 资源采样指标
规范建议使用以下指标/数据源对 Kubernetes 资源进行采样:
| 指标 | 数据来源 |
|---|---|
| container_cpu/usage_seconds_total | cAdvisor |
| container_memory_working_set_bytes | cAdvisor |
| gpu_usage | 芯片组特定指标(chipset-specific metrics) |
| cpu_requested | kube API |
| ram_requested | kube API |
| gpu_requested | kube API |
也就是说:使用量(usage)指标来自 cAdvisor 与芯片组级指标,而请求量(requested)指标来自 Kubernetes API Server。这两类指标正是前面max(request, usage)公式的两个输入来源。在仓库中,Prometheus 采集与指标解析逻辑位于 modules/prometheus-source/pkg/prom,其查询构建与规范列出的指标口径相对应;而工作负载请求量(requests)的解析可在 pkg/costmodel 的查询与解析代码中找到。
十、术语表(Glossary)
规范还提供了一份贯穿全文的术语定义,摘录如下:
- Cluster Assets(集群资产):Kubernetes 集群内可直接观测、且因资源而直接产生成本的实体,例如节点、持久卷、附加磁盘与负载均衡器。
- Container(容器):容器镜像的一个实例;同一镜像可能有多个副本同时运行。
- Image(镜像):包含需运行软件(通常是微服务)的容器模板。
- Server / Instance / Node / Node Pool(服务器/实例/节点/节点池):在此语境下指 Kubernetes 使用的机器(云上或本地、物理或虚拟)。
- Pod:Kubernetes 特有的概念,由一组容器组成;Pod 被视为可在集群中调度或伸缩的单一资源块。
- Container Orchestration(容器编排):管理服务器实例集群并维护容器与 Pod 生命周期;调度(Scheduling)是容器编排器的职能,负责将 Pod/容器调度到服务器实例上运行。
- Cluster(集群):一组服务器实例。
- Namespace(命名空间):Kubernetes 概念,用于创建"虚拟"集群,使 Pod/容器可在其中部署并与其他命名空间隔离观测。
- Pod Labels(Pod 标签):用于标识用户有意义对象的键/值对;对系统核心没有语义含义,通常用于将多个命名空间的分组与工作负载关联。
十一、规范现状与后续(Appendix C)
规范正文以三个附录收尾:Appendix A(定价归一化)、Appendix B(指标采样清单),以及Appendix C——规范作者在此标注"Working examples of OpenCost data to come!",即"OpenCost 数据的可运行示例将后续补充"。这意味着当前 spec/opencost-specv01.md 已具备完整的术语体系、公式定义与测量口径,而配套的端到端数据示例仍在演进中;社区贡献者可以以此为切入点参与完善(规范引言亦明确"欢迎所有贡献")。
结语
OpenCost 规范的价值在于把"Kubernetes 集群成本核算"这一复杂问题,拆解为一套清晰、可复现、与云厂商无关的数学框架:从集群总成本到资产成本(分配 + 使用)、再到工作负载成本(max(request, usage))与闲置成本的恒等式,辅以共享成本的分摊方法与定价归一化规则。而仓库源码(pkg/costmodel/costmodel.go 中的math.Max(x1, y1)与闲置成本差值计算、core/pkg/opencost/allocation.go 中的ShareEven/ShareWeighted/ShareNone与__idle__标识)证明这些规范约定不仅是纸面定义,而是已经落地为可运行的计量引擎。对于需要在多云环境中核算 Kubernetes 成本、实现成本分摊与闲置分析的技术团队,这份规范既是理解 OpenCost 的入口,也是自建成本系统时可参考的设计蓝本。
【免费下载链接】opencostCost monitoring for Kubernetes workloads and cloud costs项目地址: https://gitcode.com/GitHub_Trending/op/opencost
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考