Multi-Cloud-Billing · 三云账单聚合 · 自托管 FinOps 控制台
前言:为什么要自己拉三云账单
每个月最后一周,财务扔过来一个问题:
「上个月咱们三朵云加起来花了多少钱?每个账号分别多少?哪些服务是大头?」
我们公司 AWS / 阿里云 / 腾讯云全在用,AWS 还有 3 个子账号、阿里云 2 个主账号、腾讯云 2 个国际站账号。光是把这些控制台挨个点开看本月合计,至少 15 分钟,而且:
- 看不到”过去 6 个月 EC2 在 AWS 的趋势”,控制台只有本月
- Excel 想统一格式发给财务,但三家 API 返回结构完全不一样
- 半夜 12 点想看下今天累计账单,控制台没有”昨日累计”,得第二天才看得到
- 退款、调账这种负数行经常藏在明细里,月度概览看不到
于是就有了 multi-cloud-billing —— 一个本地跑的「三云账单聚合」小工具:
- AWS / 阿里云 / 腾讯云 各家账单拉下来,落到本地 SQLite
- 带图表的 Web 仪表盘 + Excel 文件下载
- 账号管理 UI(AK/SK Fernet 加密、添加账号自动拉 6 个月历史)
定位很明确:自托管的 FinOps 控制台——不替代云厂商控制台、不做预算告警,纯粹是把分散在三个控制台的月度账单汇总成一个视图,方便月底对账和看趋势。
它解决什么问题
把上面 4 件事压成一个本地网页:
| 场景 | 之前 | 现在 |
|---|---|---|
| 月底看本月合计 | 3 家控制台挨个看 15min | 打开 /,5 秒看本月合计(多币种并列) |
| 看 EC2 历史趋势 | 控制台只有本月,自己存 | 切换月份滑块,自动从 SQLite 读 |
| 统一格式发财务 | 自己拼 Excel 累半天 | 点下载,3 sheet 的 xlsx 直接拿 |
| 半夜看昨日累计 | 没办法 | SQLite 里有每日账单明细 |
核心能力
1. 仪表盘(首页 /)
四张 ECharts 图:
- 本月合计(多币种并列:
$34,852.91 + ¥128,400.00,不强行换算) - 账号占比饼图(按 vendor 配色:AWS 橙 / 阿里红 / 腾讯蓝)
- 服务 Top 10(横向条形图,看钱花在哪类服务)
- 账号分布(堆叠柱,每个账号一根)
可按月份切换;切换的月份自动从 SQLite 读。
2. 月账单页(/monthly)
- 账号汇总表:每个账号的当月合计、币种、退款标记(负数行单独展示)
- 服务 Top 15 横向条形图
- 历史 Excel 下载列表:最近 6 个月的 xlsx 文件
- 🔍 详情 按钮 → 弹窗显示该账号当月所有账单明细行(带中文服务名)
- ❓ 退款说明 按钮 → 弹窗显示该账号当月所有负数行 + 为什么会出现负数
- 🔄 手动全量拉取 按钮 → SSE 实时推送拉取进度(每个账号、每页 API 调用都能看到)
3. 账号配置(/accounts,需要登录)
后台管理界面:
- 新增 / 编辑 / 禁用 AWS、阿里云、腾讯云账号
- AK/SK 在数据库里 Fernet 加密存储,页面只显示脱敏后 4 位(如
****abcd) - 从 .env 导入按钮(首次部署时一键把旧
.env里的账号搬过来) - 后台拉取日志卡片(新增账号时会跑 6 个月历史 + 当月,实时显示每步状态)
4. Excel 下载(/download/xlsx/{filename})
每月一份 xlsx,三 sheet:
- 概览:账号 × 币种 × 月度合计 + 状态(正常 /
Auth_Error/Error) - 按服务汇总:每个账号的服务级明细
- 统一明细(可选):三云的详细行级账单统一列结构
文件名 billing-YYYY-MM.xlsx,浏览器直接拉。
5. 定时刷新
每天凌晨 1 点自动跑:调 API → 写 SQLite → 从 SQLite 重建 xlsx。
调度用 launchd(macOS) 而非 cron —— 系统重启自动续上,比 cron 稳。Linux 也能改成 systemd timer。
架构
1 | ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ |
关键设计决策:
- 数据源唯一:所有页面读 SQLite,不直接调 API(拉取时把 API 结果 upsert 进 SQLite)
- xlsx 从 DB 重建:凌晨定时任务拉完 API 后,从 DB 重建 xlsx,保证离线也能导出
- 新增账号触发历史拉取:accounts 表插一条 → 自动后台拉最近 6 个月 + 当月
- 凭证 Fernet 加密:AK/SK 不存明文,页面只显示
****abcd
货币策略 · 有意不做汇率换算
每个账号保留厂商原生货币,不做换算:
- AWS → USD
- 阿里云 → CNY(账单上写人民币,但
Currency字段是 “USD” 时按 USD 走) - 腾讯云 → CNY / USD(看 sub-customer 合同类型)
页面里 USD 和 CNY 分别合计,不强行加总。Excel 每行带 货币 列。
这样设计是有意为之——汇率每月变,如果按某日汇率折算再加总,对账时反而对不上。让财务按她自己查到的汇率算更稳。
安全模型
- 凭证 Fernet 加密:AK/SK 落 SQLite 前用
BILLING_CREDENTIALS_KEY加密;key 在.env里 - 管理界面要密码:
/accounts路由要求 session 登录,密码哈希在.env(PBKDF2-SHA256 200k 轮) - 最小权限:每个厂商给 RAM/CAM 子账号只挂”账单只读”策略
- 本地优先:默认监听
0.0.0.0:8088,部署时建议加 nginx 反代 + basic auth,或者改成127.0.0.1只本机访问
技术栈
| 层 | 选型 | 为什么 |
|---|---|---|
| Web 框架 | FastAPI + Jinja2 | 一个进程同时提供 HTML 页面和 JSON API |
| 图表 | ECharts 5(CDN) | 暗色主题 + 厂商配色灵活、Tooltip 美观 |
| 数据 | SQLite(单文件) | 个人工具零运维;200MB 单文件 100 万行足够撑几年 |
| 调度 | launchd(macOS) | 系统重启自动续上,比 cron 稳 |
| Excel | openpyxl | 支持多 sheet + 表头合并 + 列宽自适应 |
| 云 SDK | boto3 + alibabacloud_bssopenapi + tencentcloud-sdk | 官方 SDK,省心 |
依赖列表(requirements.txt):
1 | boto3>=1.34 |
目录结构
1 | multi-cloud-billing/ |
快速开始
1 | cd ~/ops/multi-cloud-billing |
打开 http://localhost:8087/,登录账号管理页(/accounts)添加三云账号。
1 | # 手动拉一次(调试用) |
已知限制 · 老老实实承认
| 项 | 现状 | 影响 |
|---|---|---|
| 单账号明细行 | 上限 500 行(防止超大账号炸前端) | 详细看 /api/details/monthly?truncated=true |
| 阿里云分页 | PageSize=300(API 上限) | 月度 < 300 服务一般够,超出会只取首页 |
| 腾讯云限流 | 每秒 ≤ 1 次,整进程加锁串行 | 多账号拉一次需 1-2 分钟(已显示进度) |
| 多币种合计 | 不做汇率换算 | USD 和 CNY 分开展示 |
| 预算告警 | 没做 | 财务自己盯 Excel 通知 |
后续可做
- 预算 / 同比环比告警
- 服务按业务标签分类(”开发 / 生产 / 测试”分组汇总)
- AWS / 阿里云 Savings Plans 覆盖率统计
- 数据导出到 S3 / OSS 做长期归档
不是什么
跟前面两篇博客一样,先把边界说清楚:
- ❌ 不是云厂商控制台替代品:底层账单还是调各云官方 API,自托管只解决”汇总 + Excel + 历史趋势”
- ❌ 不是 FinOps 平台:没做预算告警 / 同比环比 / 成本归因这些”企业级 FinOps”功能
- ❌ 不是商业级高可用:单 SQLite 文件,崩了丢历史,但本地工具无所谓
- ❌ 不做汇率换算:上面”货币策略”单独说过
- ❌ 不替代财务系统:Excel 是给财务的中间产物,最终记账还是用专业财务软件
把分散在三个云控制台的”每月对账 15 分钟”压成”打开本地网页 5 秒看本月 + 切月份看趋势 + 一键下 Excel”——单 SQLite、单 FastAPI、launchd 调度,整套 Python 3.14 + 12 个依赖包就能跑。

