3. OpenCost架构与数据流

OpenCost 是一个面向 Kubernetes 的开源成本监控与分摊引擎。它并不直接“读取账单”,而是把集群里持续产生的资源用量指标,结合云厂商价格或自定义价目表,换算成可归属到命名空间、工作负载、Pod、标签维度的成本数字。理解它的关键,不是记住某个命令,而是看清一条完整的数据链路:指标从哪里来、经过哪些组件、在何处被定价、最终以什么形式暴露给 API 与 UI。本节围绕这条链路展开。

一、组件构成:四个角色各司其职

OpenCost 的部署形态通常是一个 Deployment(或 Helm Chart 安装的 Pod),内部由若干协作组件构成,从数据流角度看可分为四类角色:

  • 指标采集层(Metrics Collector):从 kubelet 的 cAdvisor 接口、kube-state-metrics 以及节点信息中拉取容器 CPU、内存、GPU、网络、存储等用量数据,同时获取 Pod 与工作负载的元数据(namespace、owner、labels)。
  • 成本计算引擎(Cost Engine):核心是 Allocation 计算逻辑。它把“资源用量”乘以“单位价格”,再按时间窗口聚合,生成可分摊的成本记录。
  • 价格与配置源(Pricing / Config):包括云厂商价目表(如 AWS、GCP、Azure 的按需与预留价格)、自定义 CSV 价目表、以及 OpenCost 自身的配置(如 CPU/内存单价、货币单位、分摊策略)。
  • API 与 UI 层:对外暴露 REST API(如 /allocation、/assets、/metrics),并提供内置 Web UI 用于可视化查询。

这四类角色并非独立进程,而是同一进程内的模块化协作。真正决定数据流向的,是它们之间的调用顺序与数据形态变化。

二、指标采集:从 cAdvisor 到内部指标库

OpenCost 采集指标的主要来源是 kubelet 暴露的 cAdvisor 端点。以容器 CPU 为例,它读取的是累计 CPU 使用时间(单位秒),而不是瞬时百分比。采集周期通常为 30 秒到 1 分钟,采集到的原始样本会先进入内部的时间序列缓存。

采集过程中有两个容易忽略的细节:

  • 元数据关联:仅有用量数字无法分摊成本,必须把容器 ID 关联到 Pod、Pod 关联到 ReplicaSet/Deployment、再关联到 namespace 与 labels。这一步依赖 kube-state-metrics 或直接调用 Kubernetes API。
  • 缺失与重启处理:容器重启会导致累计计数器归零,采集层需要识别这种“计数器重置”,否则会算出负的用量增量。

采集完成后,数据以“资源用量 + 时间戳 + 归属标签”的形式进入成本计算引擎,这是链路中第一次形态转换:从原始计数器变成可归属的用量增量。

三、成本计算引擎:用量 × 价格 × 时间

成本计算引擎的核心公式可以简化为:

某资源成本 = 用量增量 × 单位价格 × 时间权重
某工作负载总成本 = Σ(CPU成本 + 内存成本 + GPU成本 + 网络成本 + 存储成本)

以 CPU 为例,假设某 Pod 在 1 小时内平均使用 0.5 核,集群 CPU 单价为 0.04 美元/核·小时,则该 Pod 的 CPU 成本约为 0.5 × 0.04 × 1 = 0.02 美元。内存同理,按 GiB·小时计价。GPU 与存储则按各自单价单独计算。

引擎在计算时会处理几个关键问题:

  • 空闲成本与未分摊成本:集群总成本减去已分摊成本,剩余部分可归为 idle 或 unallocated,避免“分摊后总和对不上账单”。
  • 共享资源分摊:如网络、控制平面等无法直接归属的资源,按 CPU/内存用量比例或自定义策略分摊。
  • 时间窗口对齐:价格可能按小时或按月变化,用量按秒累计,引擎需在窗口内做加权平均。

计算结果不是原始指标,而是结构化的成本记录,包含 cost、cpuCost、memoryCost、namespace、controller、pod 等字段。这些记录会被缓存,供 API 查询。

四、API 与 UI 的数据链路

当用户通过 UI 或外部系统查询时,链路进入最后一环:

  1. UI 或调用方发起 HTTP 请求,例如 GET /allocation?window=1h&aggregate=namespace。
  2. API 层解析查询参数(时间窗口、聚合维度、过滤条件),向成本计算引擎请求对应窗口的成本记录。
  3. 引擎返回已聚合的成本数据,API 层将其序列化为 JSON。
  4. UI 接收 JSON 后渲染为表格或图表;外部系统则直接消费 JSON 做二次分析。

这条链路中,API 是唯一对外暴露的稳定接口,UI 只是 API 的一个消费者。因此,做成本集成时,优先对接 API 而非 UI。

OpenCost 数据链路:从指标采集到 API / UI kubelet / cAdvisor CPU 累计时间、内存 GPU、网络、存储用量 kube-state-metrics Pod / Deployment namespace、labels 元数据 价格与配置源 云厂商价目表 / CSV CPU、内存、GPU 单价 指标采集层 Metrics Collector 拉取用量 → 关联元数据 → 处理计数器重置 → 生成可归属用量增量 成本计算引擎 Cost Engine 用量增量 × 单位价格 × 时间权重 → 按 namespace / workload / pod 聚合 处理 idle 成本、共享资源分摊、时间窗口对齐 REST API /allocation /assets /metrics Web UI / 外部系统 表格、图表、JSON 消费 数据形态变化 原始计数器 可归属用量增量 结构化成本记录 聚合 JSON 响应 每一层都改变 数据的粒度与含义 API 是唯一稳定 对外接口

五、链路中的关键判断点

理解这条链路后,排查成本异常时就有了明确的切入点。如果 UI 显示某命名空间成本为零,可能的原因依次是:采集层没有拿到该命名空间的用量、元数据关联失败导致无法归属、价格配置缺失导致单价为零、或 API 查询窗口内确实没有数据。按链路顺序逐段验证,比直接猜测配置错误更高效。

另一个常见问题是“分摊总和与云账单不一致”。这通常不是计算错误,而是链路中缺少了 idle 成本或共享资源分摊。OpenCost 的 API 支持返回未分摊部分,调用时注意查看 unallocated 或 idle 字段,而不是只看已分摊的工作负载成本。

实践建议:对接 OpenCost 时,先用 curl 直接请求 /allocation 接口,确认返回 JSON 中的 cost、cpuCost、memoryCost 字段是否符合预期,再接入 UI 或自建看板。这样可以把“数据链路问题”和“展示层问题”分开定位。

总结来看,OpenCost 的架构并不复杂,但它的价值恰恰在于这条链路的完整性:从 kubelet 的原始计数器,到可归属的用量增量,再到乘以价格后的成本记录,最后通过 API 暴露为可查询的 JSON。掌握这条链路,就掌握了 OpenCost 成本工程的起点。