计量计费与账户充值

想了解平台如何记录资源占用并计费、如何自定义计价函数,或想给账户充值 / 查询充值订单 / 确认对公到账时读这篇

平台使用 / 计量计费
计量计费计费账单billingmetering资源占用podwatch_podPRICE_FUN价格price成本自定义计价kubeflow-watchsupervisord充值

计量计费与账户充值

简介

计量计费用于完整记录平台所有用户在任何时间、任何场景下使用的 Pod 所占用的资源信息,并据此核算成本。

实现代码

  • 监听采集进程:myapp/tools/watch_pod.py。该进程运行在 infra 命名空间下的 kubeflow-watch Deployment(Pod)中,通过 supervisord 启动。

    • 容器启动命令为 supervisord -c /home/myapp/myapp/tools/supervisord.conf(见 install/kubernetes/cube/base/deploy-watch.yaml:55)。
    • supervisord.conf 中以 [program:watch-pod] command=python myapp/tools/watch_pod.py 拉起该进程(见 myapp/tools/supervisord.conf:35)。
  • 历史 Pod 信息通过 myapp/views/view_pod.py 实现。

  • Pod 特殊字段通过 myapp/models/model_pod.py 设置。

数据库存储

保存在 kubeflow 数据库的 pod 表中(表名见 myapp/models/model_pod.py:23__tablename__ = 'pod')。

自定义价格信息

myapp/models/model_pod.py 中,Pod.price 属性负责计算单个 Pod 的总价格:先从配置文件读取 PRICE_FUN 函数得到每小时单价,再根据起止时间计算运行时长,折算出该任务的总价格。若配置中没有 PRICE_FUN,或计算异常,则价格为 0。

# 获取总价(myapp/models/model_pod.py:54)
@property
def price(self):
    price_fun = conf.get('PRICE_FUN', None)
    if price_fun:
        try:
            price = price_fun(self)  # 获取每个小时的价格
            end_time = datetime.datetime.strptime(self.end_time, '%Y-%m-%d %H:%M:%S') if self.end_time else datetime.datetime.now()
            start_time = datetime.datetime.strptime(self.start_time, '%Y-%m-%d %H:%M:%S') if self.start_time else datetime.datetime.now()
            duration = (end_time - start_time).total_seconds()
            price = round(duration * price / 3600, 2)  # 单价 × 时长(秒)/3600 = 总价
            return price
        except Exception as e:
            print(e)
    return 0

PRICE_FUN 在配置文件 config.py 中定义,用于返回 Pod 资源的每小时单价。需要自定义价格时,可在配置文件中按集群实际资源单价改写该函数。

说明:config.py 已内置一个非零的默认 PRICE_FUNinstall/kubernetes/cube/overlays/config/config.py:900install/docker/config.py:900),按 CPU / 内存 / GPU 的资源量计价;未配置或计算异常时价格为 0。

# 自定义任务计价函数(config.py:900,返回每小时价格)
import pysnooper, datetime, math
# @pysnooper.snoop(watch_explode=('pod'))
def PRICE_FUN(pod):
    resource = json.loads(pod.resource)
    # 每核 cpu 0.075 元/h  每 G 内存 0.035 元/h   gpu v100 16 元/h  T4 9 元/h
    price = 16 * float(resource.get('gpu', 0)) + 0.075 * float(resource.get('cpu', 0)) + 0.035 * float(resource.get('memory', 0))
    return price  # 返回每个小时的价格

传入 PRICE_FUNpod 对象包含如下字段(可据此自定义计价逻辑),示例(通过 pysnooper 打印):

pod.end_time      = '2024-03-03 19:27:39'
pod.host          = '192.168.3.7'
pod.id            = 1
pod.name          = 'admin-098b'
pod.namespace     = 'jupyter'
pod.node_selector = '{"cpu": "true", "notebook": "true", "org": "public"}'
pod.org           = 'public'
pod.pod_type      = 'notebook'
pod.project       = public
pod.project_id    = 1
pod.resource      = '{"memory": 0, "cpu": 0.0}'   # 计价主要依据此字段(含 cpu/memory/gpu)
pod.start_time    = '2024-02-29 20:48:44'
pod.status        = 'Failed'
pod.user          = admin
pod.user_id       = 1

提示:计价单价属于运维侧策略配置,落在配置文件中,相关说明见「04-运维管理」的配置文件管理

账户充值

充值为计量计费与算力 / 存储租赁提供余额入口:用户创建充值订单、完成支付后余额入账,后续账单从余额扣减。 充值订单复用账单 Bill 表(订单号 RC + 时间戳 + 6 位随机);余额挂在用户上(user.get_balance() / user.add_balance())。

后端:myapp/views/view_recharge.py(路由 /recharge_modelview/api);前端 pages/Recharge

支付渠道

渠道 channel 流程
微信支付 wechat 调支付网关生成二维码,扫码支付,网关回调后自动入账
支付宝 alipay 同上
对公汇款 offline 返回对公银行账户信息与汇款识别码(由用户名 + 用户 id + 前缀生成,汇款时备注),管理员确认到账后入账

充值档位由前端页面提供,不依赖后端配置。

接口一览

接口 方法 说明
/account GET 账户信息:当前余额、汇款识别码、对公账户
/order/create POST 创建充值订单(amount + channel);微信 / 支付宝返回二维码,对公返回银行信息
/order/status/<order_id> GET 查询订单状态(前端轮询用)
/order/confirm/<order_id> POST 仅管理员:确认对公汇款到账
/notify/<channel> GET/POST 支付网关异步回调

入账一致性(幂等设计)

入账唯一出口为 settle_order(bill_id)

  1. 对账单行加锁(with_for_update)并判断 status未支付才置「已支付」并加余额,已支付直接返回——同一订单余额只加一次;
  2. 加余额时锁用户行——balance 为 JSON 读改写,避免同一用户多笔订单并发入账丢更新。

因此支付回调重复推送、管理员重复点击确认,都不会导致重复入账。 算力 / 存储租赁的按量 / 按天费用从余额扣减,余额不足可触发欠费释放。

最后更新 2026-08-11完整文档以官方仓库为准:GitHub Wiki