多云账户余额监控系统:从零搭建到 Docker 一键部署(Ubuntu 22.04 + Flask)
·
🌟 背景与目标
在企业级云环境中,往往使用多个云厂商服务(如阿里云、华为云、腾讯云),每个平台都有独立的计费系统。手动登录各平台查看余额效率低、易遗漏。
本文将带你从零开始,在 Ubuntu 22.04 系统上搭建一个 多云账户余额监控系统,具备以下能力:
✅ 支持阿里云、华为云、腾讯云余额查询
✅ Web 页面实时展示
✅ systemd 开机自启 & 日志管理
✅ 安全配置 AK/SK(环境变量隔离)
✅ docker-compose 一键部署方案
✅ 生产级可维护架构
🧰 准备工作
✅ 系统环境
- 操作系统:Ubuntu 22.04 LTS
- Python 版本:Python 3.10(系统默认)
- 用户权限:具备
sudo权限
# 查看系统版本
lsb_release -a
# 查看 Python 版本
python3 --version # 应输出 Python 3.10.x
第一步:安装基础依赖
1. 更新系统包索引
sudo apt update && sudo apt upgrade -y
2. 安装 Python 和 pip
Ubuntu 22.04 默认已安装 Python 3.10,但需手动安装 pip:
sudo apt install -y python3-pip python3-venv
验证安装:
pip3 --version
# 输出示例:pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10)
第二步:安装并配置云厂商 CLI 工具
🔹 阿里云 CLI (aliyun)
安装
curl -sSL https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz | tar -xzf - -C /tmp
sudo mv /tmp/aliyun /usr/local/bin/
配置 Profile
aliyun configure --profile Domain
aliyun configure --profile BanLv
aliyun configure --profile BigData
aliyun configure --profile Sms
每次输入对应的 AccessKey ID 和 Secret
📌 参考文档:阿里云 CLI 配置指南
🔹 腾讯云 CLI (tccli)
安装
pip3 install --upgrade tccli
配置 Profile
tccli configure --profile YingYin_Service
tccli configure --profile Cunchu_Service
输入对应的 SecretId 和 SecretKey
📌 参考文档:腾讯云 CLI 快速入门
第三步:创建项目目录与虚拟环境(推荐)
# 创建项目目录
sudo mkdir -p /opt/bssopenapi
cd /opt/bssopenapi
# 创建虚拟环境(可选但推荐)
python3 -m venv venv
source venv/bin/activate
第四步:安装 Python 依赖
pip3 install flask huaweicloudsdkcore huaweicloudsdkbss requests urllib3
✅ 说明:
flask:Web 服务框架huaweicloudsdk*:华为云 BSS SDKrequests:HTTP 请求库urllib3:底层 HTTP 客户端(用于处理 HTTPS 警告)
第五步:编写主程序(略)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask, render_template_string
import subprocess
import json
from datetime import datetime
import os
import urllib3
# =============================
# 禁用 InsecureRequestWarning 警告
# =============================
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# =============================
# 华为云 SDK 导入
# =============================
from huaweicloudsdkcore.auth.credentials import GlobalCredentials
from huaweicloudsdkcore.http.http_config import HttpConfig
from huaweicloudsdkbss.v2.region.bss_region import BssRegion
from huaweicloudsdkbss.v2 import BssClient, ShowCustomerAccountBalancesRequest
from huaweicloudsdkcore.exceptions import exceptions
app = Flask(__name__)
# =============================
# 阿里云账户配置
# =============================
ALIYUN_ACCOUNTS = [
{"profile": "Domain", "name": "Aliyun Domain"},
{"profile": "BanLv", "name": "Aliyun BanLv"},
{"profile": "BigData", "name": "Aliyun BigData"},
{"profile": "Sms", "name": "Aliyun SMS"},
]
def get_aliyun_balance(profile):
"""通过 aliyun CLI 查询阿里云账户余额"""
cmd = [
"/usr/local/bin/aliyun",
"--profile", profile,
"bssopenapi",
"QueryAccountBalance",
"--region", "cn-hangzhou"
]
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=10
)
if result.returncode != 0:
return {"error": result.stderr.strip()}
data = json.loads(result.stdout)
if not data.get("Success"):
return {"error": data.get("Message", "Unknown error")}
amount_str = data["Data"].get("AvailableAmount", "0")
currency = data["Data"].get("Currency", "CNY")
try:
amount = float(amount_str.replace(',', ''))
except (TypeError, ValueError):
amount = 0.0
return {"amount": amount, "currency": currency, "error": None}
except Exception as e:
return {"error": str(e)}
def get_huaweicloud_balance():
"""
使用华为云 SDK 查询账户余额
关键:使用正确的字段名 'amount' 而非 'available_amount'
"""
try:
ak = os.environ.get("CLOUD_SDK_AK")
sk = os.environ.get("CLOUD_SDK_SK")
if not ak or not sk:
return {"error": "Huawei Cloud AK/SK not set"}
credentials = GlobalCredentials(ak, sk)
config = HttpConfig()
config.ignore_ssl_verification = True
config.timeout = 10
client = BssClient.new_builder() \
.with_http_config(config) \
.with_credentials(credentials) \
.with_region(BssRegion.value_of("cn-north-1")) \
.build()
request = ShowCustomerAccountBalancesRequest()
response = client.show_customer_account_balances(request)
balances = response.to_dict().get("account_balances", [])
total_amount = 0.0
# account_type 映射表
ACCOUNT_TYPE_NAMES = {
1: "现金账户", 5: "免费试用金", 4: "合作伙伴奖励金", 6: "代金券账户"
}
for account in balances:
acc_type = account.get("account_type")
amount_str = account.get("amount", "0") # ✅ 正确字段
try:
amount = float(amount_str)
except (TypeError, ValueError):
amount = 0.0
total_amount += amount
return {
"amount": round(total_amount, 2),
"currency": "CNY",
"error": None
}
except exceptions.ClientRequestException as e:
return {"error": f"{e.error_code}: {e.error_msg}"}
except Exception as e:
return {"error": f"Huawei SDK Error: {str(e)}"}
def get_tencent_balance(profile, display_name):
"""调用 tccli 查询腾讯云账户余额"""
cmd = [
"tccli",
"--profile", profile,
"billing",
"DescribeAccountBalance",
"--cli-unfold-argument",
"--region", "ap-guangzhou"
]
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=10
)
if result.returncode != 0:
return {"error": result.stderr.strip()}
data = json.loads(result.stdout)
# ✅ 使用 RealBalance(单位:分),转为元
amount_in_fen = data.get("RealBalance", 0)
amount_in_yuan = amount_in_fen / 100.0 # 分 → 元
return {
"amount": round(amount_in_yuan, 2),
"currency": "CNY",
"error": None
}
except Exception as e:
return {"error": f"TCCLI Error: {str(e)}"}
@app.route('/')
def index():
results = []
# === 阿里云余额 ===
for acc in ALIYUN_ACCOUNTS:
resp = get_aliyun_balance(acc["profile"])
if resp["error"]:
balance_text = f"<span style='color:#d9534f;'>Error: {resp['error']}</span>"
else:
balance_text = f"<strong style='color:#5cb85c;'>{resp['amount']:,.2f}</strong> CNY"
results.append({
"display_name": acc["name"],
"balance": balance_text
})
# === 华为云余额 ===
hc_resp = get_huaweicloud_balance()
if hc_resp["error"]:
balance_text = f"<span style='color:#d9534f;'>Error: {hc_resp['error']}</span>"
else:
balance_text = f"<strong style='color:#5cb85c;'>{hc_resp['amount']:,.2f}</strong> CNY"
results.append({
"display_name": "Huawei Cloud",
"balance": balance_text
})
# === 腾讯云余额 ===
tencent_accounts = [
{"profile": "YingYin_Service", "name": "Tencent YingYin"},
{"profile": "Cunchu_Service", "name": "Tencent Cunchu"}
]
for acc in tencent_accounts:
resp = get_tencent_balance(acc["profile"], acc["name"])
if resp["error"]:
balance_text = f"<span style='color:#d9534f;'>Error: {resp['error']}</span>"
else:
balance_text = f"<strong style='color:#5cb85c;'>{resp['amount']:,.2f}</strong> CNY"
results.append({
"display_name": acc["name"],
"balance": balance_text
})
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
html_template = '''
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>☁️ Multi-Cloud Balance Monitor</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 0; background: #f4f6f9; color: #333; line-height: 1.6; }
.container { width: 90%; max-width: 600px; margin: 40px auto; padding: 20px; }
.header { text-align: center; margin-bottom: 30px; }
.header h1 { color: #0056b3; font-size: 28px; margin: 0; }
.card { background: white; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); padding: 24px; margin-bottom: 20px; }
.account-item { font-size: 18px; margin: 16px 0; display: flex; justify-content: space-between; align-items: center; }
.account-name { font-weight: 600; color: #333; min-width: 120px; }
.footer { text-align: center; margin-top: 30px; color: #999; font-size: 13px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>☁️ Multi-Cloud Account Balance Monitor</h1>
<p>Real-time monitoring for Aliyun, Huawei Cloud & Tencent Cloud</p>
</div>
<div class="card">
{% for r in results %}
<div class="account-item">
<span class="account-name">{{ r.display_name }}</span>
<span>{{ r.balance|safe }}</span>
</div>
{% endfor %}
</div>
<div class="footer">
<p>Last updated: <strong>{{ now }}</strong></p>
<p>Tip: Refresh page to get latest data</p>
</div>
</div>
</body>
</html>
'''
return render_template_string(html_template, results=results, now=now)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)
第六步:安全配置华为云 AK/SK
为了避免密钥硬编码,我们使用 .env 文件管理。
创建 .env 文件
sudo vim /opt/bssopenapi/.env
内容:
CLOUD_SDK_AK=your_huawei_cloud_ak_here
CLOUD_SDK_SK=your_huawei_cloud_sk_here
设置安全权限
sudo chmod 600 /opt/bssopenapi/.env
sudo chown root:root /opt/bssopenapi/.env
第七步:配置 systemd 服务(生产级运行)
创建 service 文件
sudo vim /etc/systemd/system/cloud-balance-monitor.service
内容:
[Unit]
Description=Multi-Cloud Balance Monitor (Aliyun + Huawei Cloud + Tencent Cloud)
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/bssopenapi
ExecStart=/usr/bin/python3 /opt/bssopenapi/app.py
Restart=always
RestartSec=5
# 加载安全环境变量
EnvironmentFile=/opt/bssopenapi/.env
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=cloud-balance-monitor
MemoryLimit=512M
[Install]
WantedBy=multi-user.target
启用服务
sudo systemctl daemon-reload
sudo systemctl enable cloud-balance-monitor.service
sudo systemctl start cloud-balance-monitor.service
查看状态与日志
# 查看服务状态
sudo systemctl status cloud-balance-monitor.service
# 查看实时日志
sudo journalctl -u cloud-balance-monitor.service -f
第八步:Docker Compose 一键部署(终极方案)
对于希望快速部署或跨环境迁移的用户,我们提供 docker-compose.yml 方案。
1. 安装 Docker 和 Docker Compose
# 安装 Docker
sudo apt install -y docker.io
sudo systemctl enable docker --now
# 安装 Docker Compose
sudo apt install -y docker-compose
2. 创建项目结构
mkdir -p ~/cloud-balance && cd ~/cloud-balance
mkdir config scripts
3. 编写 docker-compose.yml
version: '3.8'
services:
balance-monitor:
image: python:3.10-slim
container_name: cloud-balance-monitor
restart: unless-stopped
working_dir: /app
volumes:
- ./scripts:/app
- ./config/.env:/app/.env:ro
environment:
- PYTHONUNBUFFERED=1
command: >
bash -c "
pip install flask huaweicloudsdkcore huaweicloudsdkbss tccli &&
python /app/app.py
"
ports:
- "5000:5000"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
4. 准备文件
- 将
app.py放入~/cloud-balance/scripts/ - 将
.env(含华为云 AK/SK)放入~/cloud-balance/config/
5. 启动服务
docker-compose up -d
6. 查看日志
docker logs -f cloud-balance-monitor
🚀 访问服务
打开浏览器访问:
http://<your-server-ip>:5000
你将看到如下页面:
| 云厂商 | 账户名称 | 余额 |
|---|---|---|
| Aliyun | Domain | ¥1,234.56 |
| … | … | … |
| Huawei Cloud | Huawei Cloud | ¥30,334.14 |
| Tencent | YingYin_Service | ¥124,483.12 |
| Tencent | Cunchu_Service | ¥8,765.43 |
🔐 安全建议
| 项目 | 建议 |
|---|---|
| AK/SK 存储 | 使用 .env 文件 + 600 权限,禁止明文写入代码 |
| 服务运行用户 | 使用非 root 用户(当前为 root,生产可降权) |
| HTTPS | 建议配合 Nginx + Let’s Encrypt 实现 HTTPS |
| 网络访问 | 使用防火墙限制 /5000 端口仅内网访问 |
🛠️ 故障排查
❌ 华为云返回 0 余额?
- 检查是否误用了
available_amount→ 正确字段是amount - 确认 AK/SK 有
BSSReadOnlyAccess权限
❌ 腾讯云显示 0?
- 确保使用
RealBalance字段 - 单位是“分”,必须
/100转为“元”
❌ 提示 InsecureRequestWarning?
- 已通过
urllib3.disable_warnings()抑制 - 生产环境建议配置 CA 证书验证
🌈 扩展建议
| 功能 | 实现方式 |
|---|---|
| 钉钉/企业微信告警 | 当余额 < 阈值时,调用 Webhook |
| 数据持久化 | 将历史余额写入 SQLite 或 Prometheus |
| 多实例高可用 | 配合 Nginx 做负载均衡 |
| UI 升级 | 使用 Vue + Element Plus 做前端 |
📚 总结
本文完整实现了:
- ✅ Ubuntu 22.04 环境准备
- ✅ Python 3.10 + pip3 安装
- ✅ 阿里云、腾讯云 CLI 配置
- ✅ 华为云 SDK 安全集成
- ✅ systemd 服务管理
- ✅
docker-compose一键部署
你现在拥有了一个 稳定、安全、可扩展的多云余额监控系统。
🔗 参考链接
- 阿里云 CLI:https://help.aliyun.com/document_detail/121422.html
- 腾讯云 CLI:https://cloud.tencent.com/document/product/1097/34575
- 华为云 SDK for Python:https://support.huaweicloud.com/sdkreference-python-sdk/bss.html
- urllib3 TLS Warnings:https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
更多推荐
所有评论(0)