Quota allocation system: - Replace monthly budget with one-time quota allocation (prepaid model) - Support both adding (+) and deducting (-) quota with underflow protection - Stepped alerts at configurable percentages (e.g., 50%/80%/90%) - Auto-disable when quota exhausted (100%), alert state resets on new allocation - Quota allocation history with operator audit trail IAM management: - Create new IAM sub-accounts directly from AirGate (auto-generates API keys) - SecretKey shown once in dialog with copy-to-clipboard - Attach/detach IAM policies via UI (ArkFullAccess, TOSFullAccess, etc.) - Sync existing users from Volcengine - Project list pulled from Volcengine API for dropdown selection Security & auth: - API Key authentication for external systems (AirDrama integration) - SECRET_KEY enforced in production (raises error if missing with DEBUG=False) - APIKeyUser with proper pk/is_staff attributes for DRF compatibility Infrastructure: - Docker + docker-compose for backend and frontend - Nginx reverse proxy for frontend with /api/ forwarding - Entrypoint with auto-migrate and default admin creation - SQLite data persisted via Docker volume at /app/data/ Bug fixes from audit: - Fix frontend referencing non-existent fields (current_month_spending, effective_budget, budget_usage_percent) - Fix scheduler using naive datetime.now() → timezone.now() - Fix scheduler reading interval from settings instead of GlobalConfig DB - Fix docker-compose SQLite volume mounting as directory - Fix CORS origin with explicit port 80 - Remove dead config (VOLC_ACCESS_KEY/SK, MONITOR_INTERVAL from settings) - Remove unused imports Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1148 lines
40 KiB
Markdown
1148 lines
40 KiB
Markdown
# 火山引擎 IAM 子账号管控工具 -- 深度研究报告
|
||
|
||
> 研究日期:2026-03-19
|
||
> 目标:通过火山引擎 Open API,实现对 IAM 子账号的全面管控,包括权限隔离、消费监控、告警、自动停用等功能。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [整体架构方案](#1-整体架构方案)
|
||
2. [API 认证与签名机制](#2-api-认证与签名机制)
|
||
3. [IAM 用户管理 API](#3-iam-用户管理-api)
|
||
4. [权限策略管理](#4-权限策略管理)
|
||
5. [API 密钥管理](#5-api-密钥管理)
|
||
6. [计费与消费查询 API](#6-计费与消费查询-api)
|
||
7. [预算与告警机制](#7-预算与告警机制)
|
||
8. [子账号自动停用/恢复方案](#8-子账号自动停用恢复方案)
|
||
9. [项目管理与资源隔离](#9-项目管理与资源隔离)
|
||
10. [SDK 与工具链](#10-sdk-与工具链)
|
||
11. [可执行实施方案](#11-可执行实施方案)
|
||
12. [限制与注意事项](#12-限制与注意事项)
|
||
13. [参考文档](#13-参考文档)
|
||
|
||
---
|
||
|
||
## 1. 整体架构方案
|
||
|
||
### 1.1 核心需求
|
||
|
||
| 需求 | 实现方式 | 可行性 |
|
||
|------|----------|--------|
|
||
| 子账号不能看到主账号信息 | IAM 默认零权限 + 显式 Deny 策略 | **完全可行** |
|
||
| 子账号仅有 Seedance 2.0 + TOS 权限 | 仅附加 ArkFullAccess + TOSFullAccess 策略 | **完全可行** |
|
||
| 子账号能看到自己的账单 | 通过 AirGate 按项目维度查询,主账号代查展示 | **部分可行**(见下方说明)|
|
||
| 子账号不能看到其他账号消费/余额 | 不授予 billing/bss 权限 + 显式 Deny | **完全可行** |
|
||
| 消费达到阈值发告警 | 额度划拨制 + 阶梯式告警(50%/80%/90%)+ 飞书通知 | **完全可行** |
|
||
| 消费达到阈值自动停用 | 消费达到已划拨额度 100% 时自动停用 | **完全可行** |
|
||
| 一键恢复子账号 | 调用 IAM API 重新启用 | **完全可行** |
|
||
|
||
### 1.2 架构图
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────┐
|
||
│ 管控工具 (后端服务) │
|
||
│ │
|
||
│ ┌─────────┐ ┌──────────┐ ┌────────────┐ │
|
||
│ │ IAM管理 │ │ 消费监控 │ │ 告警引擎 │ │
|
||
│ │ 模块 │ │ 模块 │ │ 模块 │ │
|
||
│ └────┬─────┘ └────┬─────┘ └────┬───────┘ │
|
||
│ │ │ │ │
|
||
│ ┌────▼─────────────▼─────────────▼───────┐ │
|
||
│ │ 火山引擎 Open API 调用层 │ │
|
||
│ │ (HMAC-SHA256 签名认证) │ │
|
||
│ └────────────────────────────────────────┘ │
|
||
└──────────────────────────────────────────────────────┘
|
||
│ │ │
|
||
┌────▼────┐ ┌─────▼─────┐ ┌────▼─────┐
|
||
│ IAM API │ │Billing API│ │CloudMonitor│
|
||
│iam.vol..│ │billing.vol│ │open.vol.. │
|
||
└─────────┘ └───────────┘ └────────────┘
|
||
```
|
||
|
||
### 1.3 关键发现
|
||
|
||
> **重要**:火山引擎的 IAM 子用户**没有独立的计费账户**。所有费用归属主账号。子账号的消费追踪需要通过**项目(Project)**或**标签(Tag)**维度来实现,由主账号通过 Billing API 查询后聚合展示。
|
||
|
||
---
|
||
|
||
## 2. API 认证与签名机制
|
||
|
||
### 2.1 签名算法
|
||
|
||
火山引擎使用 **HMAC-SHA256** 签名(类似 AWS Signature V4)。
|
||
|
||
**签名流程:**
|
||
|
||
```
|
||
1. 构造规范请求 (Canonical Request)
|
||
= HTTP方法 + 路径 + 排序后的查询参数 + 规范头部 + 签名头列表 + Body哈希
|
||
|
||
2. 构造待签名字符串 (String to Sign)
|
||
= "HMAC-SHA256" + 时间戳 + 凭证范围 + SHA256(规范请求)
|
||
|
||
3. 派生签名密钥
|
||
kDate = HMAC-SHA256(SecretKey, 日期)
|
||
kRegion = HMAC-SHA256(kDate, "cn-north-1")
|
||
kService = HMAC-SHA256(kRegion, 服务名)
|
||
kSigning = HMAC-SHA256(kService, "request")
|
||
|
||
4. 计算签名
|
||
Signature = Hex(HMAC-SHA256(kSigning, 待签名字符串))
|
||
```
|
||
|
||
### 2.2 完整 Python 签名实现
|
||
|
||
```python
|
||
import datetime
|
||
import hashlib
|
||
import hmac
|
||
import requests
|
||
from urllib.parse import quote
|
||
|
||
|
||
class VolcengineClient:
|
||
"""火山引擎 API 客户端,处理 HMAC-SHA256 签名认证"""
|
||
|
||
def __init__(self, ak: str, sk: str, service: str, host: str,
|
||
region: str = "cn-north-1", version: str = "2018-01-01"):
|
||
self.ak = ak
|
||
self.sk = sk
|
||
self.service = service
|
||
self.host = host
|
||
self.region = region
|
||
self.version = version
|
||
|
||
def _norm_query(self, params: dict) -> str:
|
||
query = ""
|
||
for key in sorted(params.keys()):
|
||
if isinstance(params[key], list):
|
||
for v in params[key]:
|
||
query += quote(key, safe="-_.~") + "=" + quote(str(v), safe="-_.~") + "&"
|
||
else:
|
||
query += quote(key, safe="-_.~") + "=" + quote(str(params[key]), safe="-_.~") + "&"
|
||
return query[:-1].replace("+", "%20") if query else ""
|
||
|
||
def _hmac_sha256(self, key: bytes, content: str) -> bytes:
|
||
return hmac.new(key, content.encode("utf-8"), hashlib.sha256).digest()
|
||
|
||
def _hash_sha256(self, content: str) -> str:
|
||
return hashlib.sha256(content.encode("utf-8")).hexdigest()
|
||
|
||
def call(self, action: str, params: dict = None, body: str = "") -> dict:
|
||
"""调用火山引擎 API
|
||
|
||
Returns:
|
||
dict: API 响应。如果 ResponseMetadata 中包含 Error,会抛出 RuntimeError。
|
||
"""
|
||
params = params or {}
|
||
now = datetime.datetime.now(datetime.timezone.utc)
|
||
x_date = now.strftime("%Y%m%dT%H%M%SZ")
|
||
short_date = x_date[:8]
|
||
|
||
x_content_sha256 = self._hash_sha256(body)
|
||
|
||
all_params = {"Action": action, "Version": self.version, **params}
|
||
|
||
signed_headers_str = "content-type;host;x-content-sha256;x-date"
|
||
canonical_headers = (
|
||
f"content-type:application/x-www-form-urlencoded\n"
|
||
f"host:{self.host}\n"
|
||
f"x-content-sha256:{x_content_sha256}\n"
|
||
f"x-date:{x_date}"
|
||
)
|
||
|
||
query_string = self._norm_query(all_params)
|
||
canonical_request = "\n".join([
|
||
"GET", "/", query_string,
|
||
canonical_headers, "", signed_headers_str, x_content_sha256
|
||
])
|
||
|
||
credential_scope = f"{short_date}/{self.region}/{self.service}/request"
|
||
string_to_sign = "\n".join([
|
||
"HMAC-SHA256", x_date, credential_scope,
|
||
self._hash_sha256(canonical_request)
|
||
])
|
||
|
||
k_date = self._hmac_sha256(self.sk.encode("utf-8"), short_date)
|
||
k_region = self._hmac_sha256(k_date, self.region)
|
||
k_service = self._hmac_sha256(k_region, self.service)
|
||
k_signing = self._hmac_sha256(k_service, "request")
|
||
signature = self._hmac_sha256(k_signing, string_to_sign).hex()
|
||
|
||
headers = {
|
||
"Host": self.host,
|
||
"X-Date": x_date,
|
||
"X-Content-Sha256": x_content_sha256,
|
||
"Content-Type": "application/x-www-form-urlencoded",
|
||
"Authorization": (
|
||
f"HMAC-SHA256 Credential={self.ak}/{credential_scope}, "
|
||
f"SignedHeaders={signed_headers_str}, Signature={signature}"
|
||
)
|
||
}
|
||
|
||
# 重要:不能使用 requests.get(url, params=...),因为 requests 库会自行
|
||
# URL 编码参数,其编码方式可能与签名时使用的 _norm_query 不一致,
|
||
# 导致签名校验失败(401 错误)。必须手动拼接 query string。
|
||
url = f"https://{self.host}/?{query_string}"
|
||
r = requests.get(url, headers=headers)
|
||
resp = r.json()
|
||
|
||
# 检查 API 错误
|
||
error = resp.get("ResponseMetadata", {}).get("Error")
|
||
if error:
|
||
raise RuntimeError(
|
||
f"Volcengine API Error [{action}]: "
|
||
f"{error.get('Code', 'Unknown')} - {error.get('Message', '')}"
|
||
)
|
||
|
||
return resp
|
||
```
|
||
|
||
---
|
||
|
||
## 3. IAM 用户管理 API
|
||
|
||
**服务端点:** `https://iam.volcengineapi.com/`
|
||
**API 版本:** `2018-01-01`
|
||
**服务代码:** `iam`
|
||
|
||
### 3.1 用户生命周期 API
|
||
|
||
| Action | 说明 | 关键参数 |
|
||
|--------|------|----------|
|
||
| `CreateUser` | 创建子用户 | `UserName`(必填), `DisplayName`, `Email`, `MobilePhone` |
|
||
| `GetUser` | 查询用户详情 | `UserName` |
|
||
| `UpdateUser` | 更新用户信息 | `UserName`, `NewUserName`, `NewDisplayName` 等 |
|
||
| `ListUsers` | 列出所有用户 | `Limit`, `Offset`(分页) |
|
||
| `DeleteUser` | 删除用户 | `UserName` |
|
||
|
||
### 3.2 登录管理 API(控制台访问开关)
|
||
|
||
| Action | 说明 | 关键参数 |
|
||
|--------|------|----------|
|
||
| `CreateLoginProfile` | 开通控制台登录 | `UserName`, `Password`, `LoginAllowed`, `PasswordResetRequired` |
|
||
| `GetLoginProfile` | 查询登录状态 | `UserName` |
|
||
| `UpdateLoginProfile` | **启用/停用用户** | `UserName`, `LoginAllowed`(true/false) |
|
||
| `DeleteLoginProfile` | 删除登录能力 | `UserName` |
|
||
|
||
> **关键能力**:`UpdateLoginProfile` + `LoginAllowed=false` 可以**停用子账号的控制台访问**,设为 `true` 即可**一键恢复**。
|
||
|
||
### 3.3 GetLoginProfile 响应字段
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `LoginAllowed` | Boolean | 是否允许登录 |
|
||
| `LastLoginDate` | String | 最后登录时间 |
|
||
| `LastLoginIp` | String | 最后登录 IP |
|
||
| `LoginLocked` | Boolean | 是否被锁定 |
|
||
| `SafeAuthFlag` | Boolean | 是否开启 MFA |
|
||
|
||
### 3.4 用户组管理 API
|
||
|
||
| Action | 说明 |
|
||
|--------|------|
|
||
| `CreateGroup` | 创建用户组 |
|
||
| `AddUserToGroup` | 添加用户到组 |
|
||
| `RemoveUserFromGroup` | 移出用户组 |
|
||
| `ListGroupsForUser` | 查询用户所在组 |
|
||
| `ListUsersForGroup` | 查询组内用户 |
|
||
|
||
---
|
||
|
||
## 4. 权限策略管理
|
||
|
||
### 4.1 策略操作 API
|
||
|
||
| Action | 说明 |
|
||
|--------|------|
|
||
| `CreatePolicy` | 创建自定义策略 |
|
||
| `GetPolicy` | 查询策略详情 |
|
||
| `UpdatePolicy` | 更新策略 |
|
||
| `DeletePolicy` | 删除策略 |
|
||
| `ListPolicies` | 列出所有策略 |
|
||
| `AttachUserPolicy` | 将策略附加到用户 |
|
||
| `DetachUserPolicy` | 从用户分离策略 |
|
||
| `ListAttachedUserPolicies` | 查询用户已附加的策略 |
|
||
| `AttachPolicyInProject` | 在项目范围内附加策略 |
|
||
| `DetachPolicyInProject` | 在项目范围内分离策略 |
|
||
|
||
### 4.2 系统预置策略(关键)
|
||
|
||
| 策略名 | 适用场景 | 说明 |
|
||
|--------|----------|------|
|
||
| `ArkFullAccess` | Seedance 2.0 | 方舟平台完整管理权限(含模型、端点、微调) |
|
||
| `ArkStandardGlobalAccess` | Seedance 2.0 | 标准使用权限(不含模型上线) |
|
||
| `ArkReadOnlyAccess` | Seedance 2.0 | 只读权限 |
|
||
| `TOSFullAccess` | 对象存储 | TOS 完整管理权限 |
|
||
| `TOSReadOnlyAccess` | 对象存储 | TOS 只读权限 |
|
||
| `AccessKeySelfManageAccess` | API 密钥 | 用户仅能管理自己的 API 密钥 |
|
||
|
||
### 4.3 策略文档格式
|
||
|
||
```json
|
||
{
|
||
"Statement": [
|
||
{
|
||
"Effect": "Allow | Deny",
|
||
"Action": ["服务代码:操作名称"],
|
||
"Resource": ["trn:服务代码:区域:账号ID:资源路径"],
|
||
"Condition": {
|
||
"条件运算符": {
|
||
"条件键": ["值"]
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**TRN(资源名称)格式:** `trn:${ServiceCode}:${Region}:${AccountId}:${ResourcePath}`
|
||
|
||
示例:
|
||
- IAM 用户:`trn:iam::2100000000001:user/Bob`
|
||
- TOS 存储桶:`trn:tos:::my-bucket`
|
||
- TOS 对象:`trn:tos:::my-bucket/path/*`
|
||
|
||
### 4.4 推荐的子账号策略配置
|
||
|
||
#### 策略一:允许 Seedance 2.0 + TOS(使用系统策略)
|
||
|
||
**方案 A -- 全局授权**(不需要项目隔离时):
|
||
```
|
||
AttachUserPolicy: PolicyName=ArkFullAccess, PolicyType=System
|
||
AttachUserPolicy: PolicyName=TOSFullAccess, PolicyType=System
|
||
AttachUserPolicy: PolicyName=AccessKeySelfManageAccess, PolicyType=System
|
||
```
|
||
|
||
**方案 B -- 项目级授权**(推荐,需要隔离不同子账号的资源):
|
||
```
|
||
AttachUserPolicy: PolicyName=AccessKeySelfManageAccess, PolicyType=System # 全局
|
||
AttachPolicyInProject: PolicyName=ArkFullAccess, ProjectName=DeptA-Project # 限定在项目内
|
||
AttachPolicyInProject: PolicyName=TOSFullAccess, ProjectName=DeptA-Project # 限定在项目内
|
||
```
|
||
|
||
> **注意**:方案 A 和方案 B 不能混用。如果同时全局附加和项目级附加同一策略,全局策略会使项目限制失效。
|
||
|
||
#### 策略二:禁止查看主账号信息(自定义 Deny 策略)
|
||
|
||
> **注意**:此策略故意**排除了** `iam:CreateAccessKey`、`iam:UpdateAccessKey`、`iam:DeleteAccessKey`、`iam:ListAccessKeys`、`iam:GetAccessKeyLastUsed` 等密钥自管理操作,以避免与 `AccessKeySelfManageAccess` 策略冲突。因为 **Deny 优先于 Allow**,如果这里 deny 了 `iam:*`,子账号将无法管理自己的 API 密钥。
|
||
|
||
```json
|
||
{
|
||
"Statement": [
|
||
{
|
||
"Effect": "Deny",
|
||
"Action": [
|
||
"iam:ListUsers",
|
||
"iam:GetUser",
|
||
"iam:ListGroups",
|
||
"iam:GetGroup",
|
||
"iam:ListRoles",
|
||
"iam:GetRole",
|
||
"iam:ListPolicies",
|
||
"iam:GetPolicy",
|
||
"iam:ListAttachedUserPolicies",
|
||
"iam:ListAttachedRolePolicies",
|
||
"iam:ListEntitiesForPolicy",
|
||
"iam:GetLoginProfile",
|
||
"iam:GetSecurityConfig",
|
||
"iam:CreateUser",
|
||
"iam:UpdateUser",
|
||
"iam:DeleteUser",
|
||
"iam:CreateGroup",
|
||
"iam:UpdateGroup",
|
||
"iam:DeleteGroup",
|
||
"iam:CreateRole",
|
||
"iam:UpdateRole",
|
||
"iam:DeleteRole",
|
||
"iam:CreatePolicy",
|
||
"iam:UpdatePolicy",
|
||
"iam:DeletePolicy",
|
||
"iam:AttachUserPolicy",
|
||
"iam:DetachUserPolicy",
|
||
"iam:AttachRolePolicy",
|
||
"iam:DetachRolePolicy",
|
||
"iam:CreateLoginProfile",
|
||
"iam:UpdateLoginProfile",
|
||
"iam:DeleteLoginProfile",
|
||
"iam:AddUserToGroup",
|
||
"iam:RemoveUserFromGroup",
|
||
"iam:ListUsersForGroup",
|
||
"iam:ListGroupsForUser",
|
||
"iam:SetSecurityConfig",
|
||
"iam:CreateServiceLinkedRole",
|
||
"iam:DeleteServiceLinkedRole",
|
||
"iam:AttachUserGroupPolicy",
|
||
"iam:DetachUserGroupPolicy",
|
||
"iam:ListAttachedUserGroupPolicies",
|
||
"iam:AssumeRole"
|
||
],
|
||
"Resource": ["*"]
|
||
},
|
||
{
|
||
"Effect": "Deny",
|
||
"Action": [
|
||
"billing:*",
|
||
"bss:*"
|
||
],
|
||
"Resource": ["*"]
|
||
},
|
||
{
|
||
"Effect": "Deny",
|
||
"Action": [
|
||
"organization:*"
|
||
],
|
||
"Resource": ["*"]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
> **原理**:IAM 子用户**默认没有任何权限**。即使不加 Deny 策略,子用户也看不到主账号信息。但显式 Deny 可以防止其他策略意外授权。Deny 优先级始终高于 Allow。
|
||
>
|
||
> **关键设计**:Deny 策略中明确列出了要禁止的 IAM 操作,而**没有使用 `iam:*` 通配符**。这样不会阻断 `AccessKeySelfManageAccess` 授予的密钥自管理能力(`iam:CreateAccessKey`、`iam:UpdateAccessKey`、`iam:DeleteAccessKey`、`iam:ListAccessKeys`)。
|
||
|
||
#### 策略三:允许用户管理自己的 API 密钥
|
||
|
||
已有系统预置策略 `AccessKeySelfManageAccess`,直接附加即可。
|
||
|
||
#### 策略四:TOS 限定到指定存储桶
|
||
|
||
```json
|
||
{
|
||
"Statement": [
|
||
{
|
||
"Effect": "Allow",
|
||
"Action": ["tos:*"],
|
||
"Resource": [
|
||
"trn:tos:::department-bucket",
|
||
"trn:tos:::department-bucket/*"
|
||
]
|
||
},
|
||
{
|
||
"Effect": "Allow",
|
||
"Action": ["tos:ListBuckets"],
|
||
"Resource": ["*"]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. API 密钥管理
|
||
|
||
| Action | 说明 | 关键参数 |
|
||
|--------|------|----------|
|
||
| `CreateAccessKey` | 创建 API 密钥对 | `UserName`(可选,不填=为自己创建) |
|
||
| `ListAccessKeys` | 列出用户的密钥 | `UserName` |
|
||
| `UpdateAccessKey` | **启用/停用密钥** | `AccessKeyId`, `Status`(active/inactive) |
|
||
| `DeleteAccessKey` | 删除密钥 | `AccessKeyId` |
|
||
| `GetAccessKeyLastUsed` | 查询密钥最后使用 | `AccessKeyId` |
|
||
|
||
### 重要限制
|
||
|
||
- **每个用户最多 2 个 API 密钥**
|
||
- **SecretAccessKey 仅在创建时返回一次**,之后无法再获取
|
||
- 停用密钥后,使用该密钥的所有 API 调用将立即失败
|
||
|
||
### 停用子账号的 API 访问
|
||
|
||
```python
|
||
# 停用密钥 = 立即切断子账号的所有 API 调用能力
|
||
iam_client.call("UpdateAccessKey", {
|
||
"AccessKeyId": "AKLT****",
|
||
"Status": "inactive",
|
||
"UserName": "sub_user_1"
|
||
})
|
||
```
|
||
|
||
### 恢复子账号的 API 访问
|
||
|
||
```python
|
||
# 恢复密钥 = 一键恢复
|
||
iam_client.call("UpdateAccessKey", {
|
||
"AccessKeyId": "AKLT****",
|
||
"Status": "active",
|
||
"UserName": "sub_user_1"
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 计费与消费查询 API
|
||
|
||
**服务端点:** `https://billing.volcengineapi.com`
|
||
**API 版本:** `2022-01-01`
|
||
**服务代码:** `billing`
|
||
**QPS 限制:** 5 QPS
|
||
|
||
### 6.1 账单查询 API
|
||
|
||
| Action | 说明 | 粒度 |
|
||
|--------|------|------|
|
||
| `ListBillOverviewByCategory` | 按类别汇总 | 月 |
|
||
| `ListBillOverviewByProd` | 按产品汇总 | 月 |
|
||
| `ListBill` | 账单流水 | 月 |
|
||
| `ListBillDetail` | **明细账单**(最细粒度) | 日/月 |
|
||
| `ListSplitBillDetail` | 分账账单(按资源拆分) | 月 |
|
||
| `ListAmortizedCostBillDaily` | 每日摊销成本 | 日 |
|
||
|
||
### 6.2 ListBillDetail 关键参数
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `BillPeriod` | String | 是 | 格式 YYYY-MM,近 24 个月 |
|
||
| `Limit` | Integer | 是 | 每页数量 1-300 |
|
||
| `Offset` | Integer | 否 | 分页偏移 |
|
||
| `OwnerID` | Array[Long] | 否 | 按资源拥有者筛选 |
|
||
| `Product` | Array[String] | 否 | 按产品筛选 |
|
||
| `GroupTerm` | Integer | 否 | 0=明细, 1=实例, 2=产品, 3=账号 |
|
||
| `GroupPeriod` | Integer | 否 | 0=账期, 1=日, 2=详情 |
|
||
| `ExpenseDate` | String | 否 | 特定日期(需 GroupPeriod=1) |
|
||
| `InstanceNo` | String | 否 | 实例 ID 筛选 |
|
||
|
||
### 6.3 按子账号追踪消费的方法
|
||
|
||
> **核心问题**:IAM 子账号没有独立的计费维度。不能直接按 IAM UserName 查询消费。
|
||
|
||
**可行方案:**
|
||
|
||
| 方案 | 实现方式 | 精确度 |
|
||
|------|----------|--------|
|
||
| **按项目追踪** | 为每个子账号/部门创建独立项目,资源都放在项目中 | 高 |
|
||
| **按标签追踪** | 资源打上子账号标签(如 `owner=sub_user_1`) | 高 |
|
||
| **按 Ark 端点追踪** | Seedance/方舟按 Endpoint 分账(ListSplitBillDetail) | 中 |
|
||
| **按 TOS 存储桶追踪** | TOS 按 Bucket 分账 | 高 |
|
||
|
||
**推荐方案:项目 + 标签 双维度**
|
||
|
||
```
|
||
1. 创建项目 "DeptA-Project"
|
||
2. 子账号的权限限定在该项目范围内 (AttachPolicyInProject)
|
||
3. 资源打标签 tag: {"department": "DeptA", "owner": "sub_user_1"}
|
||
4. 通过 ListBillDetail + ListSplitBillDetail 按项目/标签筛选消费
|
||
```
|
||
|
||
### 6.4 账户余额查询
|
||
|
||
```python
|
||
billing_client = VolcengineClient(AK, SK, "billing", "billing.volcengineapi.com",
|
||
version="2022-01-01")
|
||
|
||
# 查询账户余额
|
||
balance = billing_client.call("QueryBalanceAcct")
|
||
# 返回:可用余额、冻结金额等
|
||
```
|
||
|
||
### 6.5 数据时效性
|
||
|
||
| 数据类型 | 可用时间 |
|
||
|----------|----------|
|
||
| 上月完整账单 | 每月 2 日 12:00 |
|
||
| 日粒度账单 | T+1 ~ T+2 天 |
|
||
| 分账账单 | ~2 天延迟 |
|
||
| 摊销成本 | ~2 天延迟 |
|
||
| **实时账单** | **不支持** |
|
||
|
||
---
|
||
|
||
## 7. 预算与告警机制
|
||
|
||
### 7.1 预算管理 API(Billing 模块)
|
||
|
||
| Action | 说明 |
|
||
|--------|------|
|
||
| `CreateBudget` | 创建预算 |
|
||
| `UpdateBudget` | 更新预算 |
|
||
| `ListBudget` | 查询预算列表 |
|
||
| `QueryBudgetDetail` | 查询预算详情 |
|
||
| `DeleteBudget` | 删除预算 |
|
||
| `ListBudgetAmountByBudgetID` | 查询预算金额 |
|
||
| `ListRecipientInformation` | 查询告警接收人 |
|
||
|
||
**预算可按以下维度筛选:**
|
||
- 区域、产品、标签、项目、账号 OwnerID、付款人 PayerID、可用区、计费模式
|
||
|
||
### 7.2 CloudMonitor Webhook 告警
|
||
|
||
**端点:** `https://open.volcengineapi.com?Action={Action}&Version=2018-01-01`
|
||
|
||
| Action | 说明 |
|
||
|--------|------|
|
||
| `CreateRule` | 创建告警规则(支持 Webhook) |
|
||
| `CreateWebhook` | 配置 Webhook 回调地址 |
|
||
| `CreateContacts` | 添加告警联系人 |
|
||
| `CreateContactGroup` | 创建通知组 |
|
||
|
||
**支持的通知渠道:**
|
||
- 站内信(默认开启)
|
||
- Email
|
||
- SMS
|
||
- 飞书(Feishu)
|
||
- 钉钉(DingTalk)
|
||
- 企业微信
|
||
- 自定义 Webhook(HTTP POST 回调)
|
||
|
||
### 7.3 Ark 推理限额(Seedance 专属)
|
||
|
||
火山方舟(Ark)平台有**推理限额**功能:
|
||
- 可设置每个模型的最大 Token 消耗量
|
||
- **达到限额后服务自动暂停**
|
||
- 最小调整间隔 2 小时
|
||
- 仅支持在线推理(不含批量)
|
||
- 目前仅支持通过控制台设置,**暂无公开 API**
|
||
|
||
### 7.4 AirGate 自建方案:额度划拨制 + 阶梯式告警
|
||
|
||
由于火山原生的预算告警仅支持站内信/邮件/短信通知,不支持自动停用。AirGate 采用**额度划拨制**自建:
|
||
|
||
```
|
||
主账号通过 AirGate 给子账号划拨额度(如 10 万元)
|
||
│
|
||
▼ 定时任务每小时查询 Billing API
|
||
累计消费不断增长,对比已划拨额度
|
||
│
|
||
├── 消费达到额度 50% → 飞书告警
|
||
├── 消费达到额度 80% → 飞书告警
|
||
├── 消费达到额度 90% → 飞书告警
|
||
└── 消费达到额度 100% → 自动停用子账号 + 飞书告警
|
||
|
||
额度用完 → 主账号在 AirGate 追加额度 → 告警状态自动重置 → 恢复子账号
|
||
额度给多了 → 主账号在 AirGate 扣减额度 → 告警状态自动重置
|
||
```
|
||
|
||
**关键设计:**
|
||
- **非月度制**:额度不按月重置,是一次性划拨,用完再充
|
||
- **可追加可扣减**:主账号可随时追加额度(+5万)或扣减额度(-3万),支持灵活调整
|
||
- **扣减保护**:扣减后总额度不能低于已消费金额(否则会立即触发停用)
|
||
- **阶梯式告警**:每个子账号可自定义告警百分比(如 [50, 80, 90]),每档只通知一次
|
||
- **额度变更即重置告警**:追加或扣减额度后,已触发的告警状态自动清空,按新的使用率重新计算
|
||
- **累计消费**:跨月累计,通过 Billing API 各月数据求和得出
|
||
- **操作留痕**:每次划拨/扣减都记录操作人、金额、备注,可追溯
|
||
|
||
---
|
||
|
||
## 8. 子账号自动停用/恢复方案
|
||
|
||
### 8.1 完全停用子账号(保留账号,可恢复)
|
||
|
||
需要**同时**执行两个操作才能完全停用:
|
||
|
||
```python
|
||
def get_user_access_keys(iam_client, username: str) -> list:
|
||
"""获取用户的所有 AccessKey ID"""
|
||
result = iam_client.call("ListAccessKeys", {"UserName": username})
|
||
keys = result.get("Result", {}).get("AccessKeyMetadata", [])
|
||
return [k["AccessKeyId"] for k in keys]
|
||
|
||
|
||
def disable_sub_user(iam_client, username: str, access_key_ids: list = None):
|
||
"""完全停用子账号(保留账号,可一键恢复)"""
|
||
|
||
# 0. 如果未传入 access_key_ids,自动查询
|
||
if access_key_ids is None:
|
||
access_key_ids = get_user_access_keys(iam_client, username)
|
||
|
||
# 1. 停用控制台登录
|
||
iam_client.call("UpdateLoginProfile", {
|
||
"UserName": username,
|
||
"LoginAllowed": "false"
|
||
})
|
||
|
||
# 2. 停用所有 API 密钥
|
||
for ak_id in access_key_ids:
|
||
iam_client.call("UpdateAccessKey", {
|
||
"AccessKeyId": ak_id,
|
||
"Status": "inactive",
|
||
"UserName": username
|
||
})
|
||
|
||
print(f"用户 {username} 已完全停用(控制台 + {len(access_key_ids)} 个 API 密钥)")
|
||
```
|
||
|
||
### 8.2 一键恢复子账号
|
||
|
||
```python
|
||
def enable_sub_user(iam_client, username: str, access_key_ids: list = None):
|
||
"""一键恢复子账号"""
|
||
|
||
# 0. 如果未传入 access_key_ids,自动查询
|
||
if access_key_ids is None:
|
||
access_key_ids = get_user_access_keys(iam_client, username)
|
||
|
||
# 1. 恢复控制台登录
|
||
iam_client.call("UpdateLoginProfile", {
|
||
"UserName": username,
|
||
"LoginAllowed": "true"
|
||
})
|
||
|
||
# 2. 恢复所有 API 密钥
|
||
for ak_id in access_key_ids:
|
||
iam_client.call("UpdateAccessKey", {
|
||
"AccessKeyId": ak_id,
|
||
"Status": "active",
|
||
"UserName": username
|
||
})
|
||
|
||
print(f"用户 {username} 已恢复(控制台 + {len(access_key_ids)} 个 API 密钥)")
|
||
```
|
||
|
||
### 8.3 停用 vs 删除的区别
|
||
|
||
| 操作 | 效果 | 可恢复 |
|
||
|------|------|--------|
|
||
| `UpdateLoginProfile(LoginAllowed=false)` | 停用控制台登录 | 一键恢复 |
|
||
| `UpdateAccessKey(Status=inactive)` | 停用 API 访问 | 一键恢复 |
|
||
| `DetachUserPolicy` | 移除权限但保留用户 | 重新附加即可 |
|
||
| `DeleteUser` | **永久删除用户** | **不可恢复** |
|
||
|
||
---
|
||
|
||
## 9. 项目管理与资源隔离
|
||
|
||
### 9.1 项目管理 API
|
||
|
||
**端点:** `https://open.volcengineapi.com`
|
||
|
||
| Action | 说明 |
|
||
|--------|------|
|
||
| `CreateProject` | 创建项目 |
|
||
| `ListProjects` | 列出项目 |
|
||
| `GetProject` | 获取项目详情 |
|
||
| `UpdateProject` | 更新项目 |
|
||
| `DeleteProject` | 删除项目 |
|
||
| `ListProjectResources` | 列出项目中的资源 |
|
||
| `MoveProjectResource` | 在项目间移动资源 |
|
||
| `ListProjectIdentities` | 列出项目中的用户/角色 |
|
||
|
||
### 9.2 项目级权限授权
|
||
|
||
```python
|
||
# 在项目范围内授权(子账号只能访问该项目下的资源)
|
||
iam_client.call("AttachPolicyInProject", {
|
||
"UserName": "sub_user_1",
|
||
"PolicyName": "ArkFullAccess",
|
||
"PolicyType": "System",
|
||
"ProjectName": "DeptA-Project"
|
||
})
|
||
```
|
||
|
||
**效果:** 子账号仅能操作 `DeptA-Project` 项目下的 Ark/Seedance 资源,无法看到其他项目的内容。
|
||
|
||
### 9.3 标签管理 API
|
||
|
||
| Action | 说明 |
|
||
|--------|------|
|
||
| `TagResources` | 给资源打标签 |
|
||
| `UntagResources` | 移除标签 |
|
||
| `ListTagsForResources` | 查询资源标签 |
|
||
|
||
**标签用于:**
|
||
- 资源分组与管理
|
||
- 按标签筛选账单(在 ListBillDetail 响应中的 `Tag` 字段)
|
||
- IAM 条件策略(基于标签的访问控制)
|
||
|
||
---
|
||
|
||
## 10. SDK 与工具链
|
||
|
||
### 10.1 推荐 SDK
|
||
|
||
| 语言 | 包名 | 安装 | 覆盖 IAM/Billing |
|
||
|------|------|------|-------------------|
|
||
| **Python**(推荐) | `volcengine-python-sdk` | `pip install volcengine-python-sdk` | 是 |
|
||
| Go | `volcengine-go-sdk` | `go get github.com/volcengine/volcengine-go-sdk` | 是 |
|
||
| Node.js | `@volcengine/openapi` | `npm install @volcengine/openapi` | 是 |
|
||
| Java | `volcengine-java-sdk` | Maven | 是 |
|
||
|
||
### 10.2 Python SDK 使用示例
|
||
|
||
```python
|
||
import volcenginesdkcore
|
||
import volcenginesdkiam
|
||
|
||
# 配置
|
||
configuration = volcenginesdkcore.Configuration()
|
||
configuration.ak = "YOUR_AK"
|
||
configuration.sk = "YOUR_SK"
|
||
configuration.region = "cn-beijing"
|
||
volcenginesdkcore.Configuration.set_default(configuration)
|
||
|
||
# IAM 操作
|
||
iam_api = volcenginesdkiam.IAMApi(
|
||
volcenginesdkcore.ApiClient(configuration)
|
||
)
|
||
|
||
# 列出用户
|
||
users = iam_api.list_users(volcenginesdkiam.ListUsersRequest(
|
||
limit=100,
|
||
offset=0
|
||
))
|
||
```
|
||
|
||
### 10.3 CLI 工具
|
||
|
||
```bash
|
||
# 安装 Volcengine CLI
|
||
# 从 https://github.com/volcengine/volcengine-cli/releases 下载
|
||
|
||
# 配置
|
||
ve configure set --profile default --region cn-beijing \
|
||
--access-key YOUR_AK --secret-key YOUR_SK
|
||
|
||
# 使用
|
||
ve iam ListUsers
|
||
ve iam CreateUser --UserName "sub_user_1" --DisplayName "Sub User 1"
|
||
ve billing ListBillDetail --BillPeriod "2026-03" --Limit 100
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 可执行实施方案
|
||
|
||
### 第一阶段:基础搭建
|
||
|
||
#### Step 1:创建子账号
|
||
|
||
```python
|
||
# 创建 IAM 客户端
|
||
iam = VolcengineClient(AK, SK, "iam", "iam.volcengineapi.com")
|
||
|
||
# 创建子用户
|
||
iam.call("CreateUser", {
|
||
"UserName": "dept_a_user",
|
||
"DisplayName": "部门A用户",
|
||
"Email": "dept_a@company.com",
|
||
"MobilePhone": "+8618000000000"
|
||
})
|
||
|
||
# 开通控制台登录
|
||
iam.call("CreateLoginProfile", {
|
||
"UserName": "dept_a_user",
|
||
"Password": "Initial@Pass123",
|
||
"LoginAllowed": "true",
|
||
"PasswordResetRequired": "true"
|
||
})
|
||
|
||
# 创建 API 密钥(记录返回的 SecretAccessKey!)
|
||
result = iam.call("CreateAccessKey", {"UserName": "dept_a_user"})
|
||
# result["Result"]["AccessKey"]["SecretAccessKey"] -- 仅此一次!
|
||
```
|
||
|
||
#### Step 2:配置权限
|
||
|
||
> **重要**:如果要通过项目隔离资源(Step 4),**不要**在此处全局附加 `ArkFullAccess` / `TOSFullAccess`,
|
||
> 否则全局策略会覆盖项目级限制,子账号将能访问所有项目的资源。
|
||
> 应当仅在项目范围内授权(见 Step 4),或者如果不需要项目隔离则可以全局附加。
|
||
|
||
```python
|
||
# 方案 A:不需要项目隔离时,全局授权
|
||
# iam.call("AttachUserPolicy", {
|
||
# "UserName": "dept_a_user",
|
||
# "PolicyName": "ArkFullAccess",
|
||
# "PolicyType": "System"
|
||
# })
|
||
# iam.call("AttachUserPolicy", {
|
||
# "UserName": "dept_a_user",
|
||
# "PolicyName": "TOSFullAccess",
|
||
# "PolicyType": "System"
|
||
# })
|
||
|
||
# 方案 B(推荐):需要项目隔离时,此处只附加密钥自管理策略
|
||
# Ark 和 TOS 的权限在 Step 4 中通过 AttachPolicyInProject 在项目范围内授权
|
||
|
||
# 允许自行管理 API 密钥(此策略需全局附加,不受项目限制)
|
||
iam.call("AttachUserPolicy", {
|
||
"UserName": "dept_a_user",
|
||
"PolicyName": "AccessKeySelfManageAccess",
|
||
"PolicyType": "System"
|
||
})
|
||
```
|
||
|
||
#### Step 3:创建并附加 Deny 策略
|
||
|
||
```python
|
||
import json
|
||
|
||
deny_policy = {
|
||
"Statement": [
|
||
{
|
||
"Effect": "Deny",
|
||
"Action": [
|
||
"iam:ListUsers", "iam:GetUser",
|
||
"iam:ListGroups", "iam:GetGroup",
|
||
"iam:ListRoles", "iam:GetRole",
|
||
"iam:ListPolicies", "iam:GetPolicy",
|
||
"iam:ListAttachedUserPolicies", "iam:ListAttachedRolePolicies",
|
||
"iam:ListEntitiesForPolicy", "iam:GetLoginProfile",
|
||
"iam:GetSecurityConfig",
|
||
"iam:CreateUser", "iam:UpdateUser", "iam:DeleteUser",
|
||
"iam:CreateGroup", "iam:UpdateGroup", "iam:DeleteGroup",
|
||
"iam:CreateRole", "iam:UpdateRole", "iam:DeleteRole",
|
||
"iam:CreatePolicy", "iam:UpdatePolicy", "iam:DeletePolicy",
|
||
"iam:AttachUserPolicy", "iam:DetachUserPolicy",
|
||
"iam:AttachRolePolicy", "iam:DetachRolePolicy",
|
||
"iam:CreateLoginProfile", "iam:UpdateLoginProfile",
|
||
"iam:DeleteLoginProfile",
|
||
"iam:AddUserToGroup", "iam:RemoveUserFromGroup",
|
||
"iam:ListUsersForGroup", "iam:ListGroupsForUser",
|
||
"iam:SetSecurityConfig",
|
||
"iam:CreateServiceLinkedRole", "iam:DeleteServiceLinkedRole",
|
||
"iam:AttachUserGroupPolicy", "iam:DetachUserGroupPolicy",
|
||
"iam:ListAttachedUserGroupPolicies",
|
||
"iam:AssumeRole"
|
||
],
|
||
"Resource": ["*"]
|
||
},
|
||
{
|
||
"Effect": "Deny",
|
||
"Action": ["billing:*", "bss:*"],
|
||
"Resource": ["*"]
|
||
},
|
||
{
|
||
"Effect": "Deny",
|
||
"Action": ["organization:*"],
|
||
"Resource": ["*"]
|
||
}
|
||
]
|
||
}
|
||
|
||
# 注意:PolicyDocument 不要额外 URL 编码,VolcengineClient._norm_query 会自动编码
|
||
iam.call("CreatePolicy", {
|
||
"PolicyName": "DenyAdminAndBilling",
|
||
"Description": "禁止访问 IAM 管理和计费信息",
|
||
"PolicyDocument": json.dumps(deny_policy)
|
||
})
|
||
|
||
iam.call("AttachUserPolicy", {
|
||
"UserName": "dept_a_user",
|
||
"PolicyName": "DenyAdminAndBilling",
|
||
"PolicyType": "Custom"
|
||
})
|
||
```
|
||
|
||
### 第二阶段:消费监控
|
||
|
||
#### Step 4:创建项目并分配
|
||
|
||
```python
|
||
# 项目管理 API(与 IAM 共用端点,但 Version 不同)
|
||
project_client = VolcengineClient(AK, SK, "iam", "iam.volcengineapi.com",
|
||
version="2021-08-01")
|
||
|
||
# 创建项目
|
||
project_client.call("CreateProject", {
|
||
"ProjectName": "DeptA-Project",
|
||
"Description": "部门A专属项目"
|
||
})
|
||
|
||
# 在项目范围内授权(子账号只能操作此项目下的 Ark 和 TOS 资源)
|
||
iam.call("AttachPolicyInProject", {
|
||
"UserName": "dept_a_user",
|
||
"PolicyName": "ArkFullAccess",
|
||
"PolicyType": "System",
|
||
"ProjectName": "DeptA-Project"
|
||
})
|
||
|
||
iam.call("AttachPolicyInProject", {
|
||
"UserName": "dept_a_user",
|
||
"PolicyName": "TOSFullAccess",
|
||
"PolicyType": "System",
|
||
"ProjectName": "DeptA-Project"
|
||
})
|
||
```
|
||
|
||
#### Step 5:消费查询脚本
|
||
|
||
```python
|
||
billing = VolcengineClient(AK, SK, "billing", "billing.volcengineapi.com",
|
||
version="2022-01-01")
|
||
|
||
def get_user_spending(bill_period: str, project_name: str = None) -> float:
|
||
"""查询指定项目/用户的消费金额(带分页处理)"""
|
||
total = 0.0
|
||
offset = 0
|
||
page_size = 300
|
||
|
||
while True:
|
||
params = {
|
||
"BillPeriod": bill_period,
|
||
"Limit": str(page_size),
|
||
"Offset": str(offset),
|
||
"GroupTerm": "0", # 明细级别(非聚合),确保 Project 字段可用
|
||
"GroupPeriod": "0", # 按账期
|
||
"NeedRecordNum": "1",
|
||
}
|
||
|
||
result = billing.call("ListBillDetail", params)
|
||
items = result.get("Result", {}).get("List", [])
|
||
record_num = int(result.get("Result", {}).get("Total", 0))
|
||
|
||
for item in items:
|
||
# 按项目筛选
|
||
if project_name and item.get("Project") != project_name:
|
||
continue
|
||
total += float(item.get("PayableAmount", "0"))
|
||
|
||
# 分页:如果还有更多数据,继续查询
|
||
offset += page_size
|
||
if offset >= record_num or not items:
|
||
break
|
||
|
||
return total
|
||
```
|
||
|
||
### 第三阶段:告警与自动停用(已在 AirGate 中实现)
|
||
|
||
> 以下逻辑已通过 AirGate 管理平台实现,不再需要手写脚本。
|
||
> 详见 `backend/utils/scheduler.py` 和 `backend/apps/monitor/views.py`。
|
||
|
||
**AirGate 实现的核心流程:**
|
||
|
||
1. 主账号通过界面给子账号**划拨额度**(如 10 万元)
|
||
2. 定时任务每小时调用 Billing API 查询累计消费
|
||
3. 消费达到额度的阶梯百分比(如 50%/80%/90%)时 → 飞书告警
|
||
4. 消费达到 100% → 自动停用子账号 + 飞书告警
|
||
5. 主账号可随时追加额度(告警状态自动重置)→ 恢复子账号
|
||
|
||
**告警状态管理:**
|
||
- 每个阶梯只通知一次,通过 `triggered_alerts` 字段(存数据库)去重
|
||
- 追加额度时自动重置 `triggered_alerts`,按新使用率重新计算
|
||
- 不需要月度重置,因为是额度制而非月度制
|
||
|
||
#### Step 6:AirGate 已实现的 API 接口
|
||
|
||
```
|
||
# 仪表盘
|
||
GET /api/v1/dashboard/ # 总览(用户数/消费/告警)
|
||
|
||
# 火山主账号管理
|
||
GET /api/v1/volc-accounts/ # 列出主账号
|
||
POST /api/v1/volc-accounts/ # 添加主账号(AK/SK 加密存储)
|
||
PUT /api/v1/volc-accounts/{id}/ # 更新主账号
|
||
DELETE /api/v1/volc-accounts/{id}/ # 删除主账号
|
||
POST /api/v1/volc-accounts/{id}/test/ # 测试密钥有效性
|
||
|
||
# IAM 子账号管理
|
||
GET /api/v1/iam-users/ # 列出所有子账号
|
||
POST /api/v1/iam-users/sync/ # 从火山同步全部子账号
|
||
POST /api/v1/iam-users/import/ # 导入指定子账号
|
||
GET /api/v1/iam-users/{id}/ # 查询子账号详情
|
||
PUT /api/v1/iam-users/{id}/update/ # 更新配置(告警阈值/开关)
|
||
POST /api/v1/iam-users/{id}/disable/ # 停用子账号
|
||
POST /api/v1/iam-users/{id}/enable/ # 恢复子账号
|
||
GET /api/v1/iam-users/{id}/policies/ # 查看权限策略
|
||
|
||
# 额度管理
|
||
POST /api/v1/iam-users/{id}/allocate/ # 追加额度(正数)或扣减额度(负数)
|
||
GET /api/v1/iam-users/{id}/quota-history/ # 查看额度变更记录(含追加和扣减)
|
||
|
||
# 消费查询
|
||
GET /api/v1/billing/overview/ # 消费总览
|
||
POST /api/v1/billing/refresh/ # 手动刷新消费数据
|
||
GET /api/v1/billing/balance/ # 主账号余额
|
||
|
||
# 全局配置
|
||
GET /api/v1/config/ # 查看全局配置
|
||
PUT /api/v1/config/ # 更新全局配置
|
||
|
||
# 告警记录
|
||
GET /api/v1/alerts/ # 告警历史(支持类型筛选)
|
||
|
||
# 项目列表
|
||
GET /api/v1/projects/ # 从火山拉取项目列表
|
||
```
|
||
|
||
---
|
||
|
||
## 12. 限制与注意事项
|
||
|
||
### 12.1 关键限制
|
||
|
||
| 限制项 | 说明 |
|
||
|--------|------|
|
||
| IAM 子账号无独立计费 | 所有费用归主账号,需通过项目/标签追踪 |
|
||
| Billing API 无实时数据 | 最快 T+1 天粒度,有 1-2 天延迟 |
|
||
| 每用户最多 2 个 API 密钥 | 无法创建更多 |
|
||
| SecretKey 仅返回一次 | 创建后立即保存 |
|
||
| Billing API QPS 限制 5 | 批量查询需注意限流 |
|
||
| Ark 推理限额无公开 API | 目前仅支持控制台操作 |
|
||
| 火山原生预算告警仅通知不自动执行 | AirGate 已自建额度划拨+阶梯告警+自动停用 |
|
||
|
||
### 12.2 安全建议
|
||
|
||
1. **主账号 AK/SK 务必安全存储**,建议使用环境变量或密钥管理服务
|
||
2. **定期轮换 API 密钥**,利用 `GetAccessKeyLastUsed` 检查不活跃的密钥
|
||
3. **遵循最小权限原则**,只授予必要的权限
|
||
4. **显式 Deny 策略优先**,防止权限漏洞
|
||
5. **监控日志**,使用 CloudTrail 审计 API 调用
|
||
|
||
### 12.3 消费监控的精确度问题
|
||
|
||
由于账单数据有 1-2 天延迟,消费监控存在滞后。AirGate 的应对策略:
|
||
- **额度划拨制**:划拨的额度应预留 1-2 天延迟的消费余量(如实际想控制 10 万,可划拨 9 万并设阈值 [50, 80, 90])
|
||
- **阶梯式告警**:在额度用尽前的多个节点提前告警,给管理员反应时间
|
||
- **高频轮询**:每小时查一次,虽然数据本身有延迟,但能在数据更新后第一时间触发告警
|
||
- 结合 Ark 推理限额功能(控制台手动设置,自动暂停,无延迟)作为兜底
|
||
|
||
---
|
||
|
||
## 13. 参考文档
|
||
|
||
### 官方文档
|
||
|
||
| 文档 | URL |
|
||
|------|-----|
|
||
| IAM API 概览 | https://www.volcengine.com/docs/6257/65842 |
|
||
| IAM 基本概念 | https://www.volcengine.com/docs/6257/64963 |
|
||
| 创建用户并授权 | https://www.volcengine.com/docs/6257/94013 |
|
||
| CreateAccessKey | https://www.volcengine.com/docs/6257/65000 |
|
||
| AttachUserPolicy | https://www.volcengine.com/docs/6257/65029 |
|
||
| LoginProfile 管理 | https://www.volcengine.com/docs/6257/65013 |
|
||
| 策略概述 | https://www.volcengine.com/docs/6257/65058 |
|
||
| 策略基本结构 | https://www.volcengine.com/docs/6257/65059 |
|
||
| 系统预置策略 | https://www.volcengine.com/docs/6257/1253730 |
|
||
| 自定义策略 | https://www.volcengine.com/docs/6257/1158323 |
|
||
| Billing API 概览 | https://www.volcengine.com/docs/6269/1165275 |
|
||
| ListBillDetail | https://www.volcengine.com/docs/6269/1127842 |
|
||
| 预算管理 | https://www.volcengine.com/docs/6269/1165274 |
|
||
| 分账账单 | https://www.volcengine.com/docs/6269/177196 |
|
||
| 计费权限管理 | https://www.volcengine.com/docs/6269/1186807 |
|
||
| 项目管理 | https://www.volcengine.com/docs/6649/166155 |
|
||
| TOS IAM 策略 | https://www.volcengine.com/docs/6349/102133 |
|
||
| Ark IAM 教程 | https://www.volcengine.com/docs/82379/1263493 |
|
||
| Ark 推理限额 | https://www.volcengine.com/docs/82379/1159200 |
|
||
| CloudMonitor API | https://www.volcengine.com/docs/6408/78940 |
|
||
| API 签名方法 | https://www.volcengine.com/docs/6369/67269 |
|
||
|
||
### GitHub 资源
|
||
|
||
| 资源 | URL |
|
||
|------|-----|
|
||
| Volcengine GitHub | https://github.com/volcengine |
|
||
| Python SDK | https://github.com/volcengine/volcengine-python-sdk |
|
||
| Go SDK | https://github.com/volcengine/volcengine-go-sdk |
|
||
| Node.js SDK | https://github.com/volcengine/volc-sdk-nodejs |
|
||
| OpenAPI Demos | https://github.com/volcengine/volc-openapi-demos |
|
||
| CLI 工具 | https://github.com/volcengine/volcengine-cli |
|
||
| TOS Python SDK | https://github.com/volcengine/ve-tos-python-sdk |
|
||
|
||
### 其他资源
|
||
|
||
| 资源 | URL |
|
||
|------|-----|
|
||
| API Explorer | https://api.volcengine.com/api-docs |
|
||
| SDK Center | https://api.volcengine.com/api-sdk |
|
||
| PyPI (新 SDK) | https://pypi.org/project/volcengine-python-sdk/ |
|
||
| npm | https://www.npmjs.com/package/@volcengine/openapi |
|
||
|
||
---
|
||
|
||
> **当前进度**:AirGate 管控工具已完成核心功能开发(Django 4.2 + DRF + Vue 3 + Element Plus),
|
||
> 包括 IAM 子账号管理、额度划拨、阶梯式告警、消费监控、飞书通知。
|
||
> 项目仓库:https://gitea.airlabs.art/seaislee/AirGate.git
|
||
>
|
||
> **待完成**:创建子账号功能、权限策略配置界面、Docker/K8s 部署配置、飞书联调、AirDrama API 对接。
|