1. 这是什么
阿里云 CLI 支持在不真正执行一个 OpenAPI 调用的前提下,先拿到它的精确报价。它回答的是:
"如果我执行这条命令,会花多少钱?"
这与事后账单(费用中心 / bssopenapi QueryAccountBill)是互补的两半:
| 询价(本手册) | 账单 | |
|---|---|---|
| 时机 | 执行前 | 执行后 |
| 回答 | 这次操作将要花多少 | 已经花了多少 |
| 用途 | 变更审批、Agent 执行前确认、成本预演 | 对账、报表、审计 |
报价与交易系统同源——去掉询价参数真正执行时,产生的订单金额就是询价给出的数字(已实测多笔新购/变配/网络变更,预估与账单分毫不差)。
2. 快速开始
# 1. 确认版本(询价能力需 CLI ≥ 3.4.2;PricingContext 请更新至最新版本)
aliyun version
# 2. 查看当前支持询价的 API 清单(实时,随后端持续扩展)
aliyun list-supported-pricing-apis
# 3. 任意受支持的命令后加 --estimate-cost,即只报价、不执行
aliyun ecs RunInstances --RegionId cn-hangzhou --InstanceType ecs.g6.large \
--ImageId <image> --InstanceChargeType PrePaid --Period 1 --PeriodUnit Month \
--estimate-cost
--estimate-cost 只报价、不执行、不产生任何变更,可放心加。
3. 核心概念
3.1 API 三元组
询价按 popCode / popVersion / apiName 三元组定位一个 OpenAPI。CLI 会自动从命令推断三元组,无需手工指定。
3.2 两条调用路径(结果一致)
同一个产品有两种命令形态,--estimate-cost 在两者上都可用,行为一致:
| 路径 | 形态 | 示例 | 参数风格 |
|---|---|---|---|
| 内置 | PascalCase 原始 API 名 | aliyun ecs RunInstances --RegionId ... |
原始参数 --RegionId |
| 插件 | kebab-case 子命令 | aliyun ecs run-instances --region-id ... |
友好参数 --instance-id |
插件路径需先安装对应产品插件:
aliyun plugin install --names aliyun-cli-ecs。
3.3 --estimate-cost
全局开关(布尔)。出现即把本次调用路由到询价服务,返回 JSON 报价。要求命令能定位到具体 API(需带 product + API 名)。
3.4 --estimate-cost-context(询价上下文 / PricingContext)
询价专用的补充输入,常用于多步变配的询价,用于基于一个未来的状态进行变配询价,例如当前ECS实例固定带宽为5M,但用户希望看到从10M变配为100M的费用,可以通过这个功能实现。详见 §6。
3.5 输出结构
--estimate-cost 输出为 JSON,顶层是 price + requestId。关键字段:
{
"price": {
"pricingMode": "single", // single=一口价;delta=变配差价
"success": true,
"currency": "CNY",
"priceSummary": {
"effectiveModuleSum": 39.86, // ★ 折后总额,通常看这个
"originalPrice": 65.14, // 原价
"tradePrice": 39.86, // 应付
"quantity": 1.0, // 数量乘数(如 --Amount 3 则为 3)
"pricingUnit": "1 Month", // 计价单位(见下方"解读要点")
"currency": "CNY",
"modules": [ // 逐计费项拆解
{
"moduleCode": "InstanceType", "originalCost": 45.14,
"invoiceDiscount": 25.28, "costAfterDiscount": 19.86 },
{
"moduleCode": "SystemDisk", "originalCost": 20.0,
"invoiceDiscount": 0.0, "costAfterDiscount": 20.0 }
]
},
"upstreamRequestId": "..." // 底层计费系统请求ID,排查用
},
"requestId": "..." // 网关请求ID
}
变配(**pricingMode: "delta"**) 会多出 components.baseline / components.target 和 calculatedAmount(需补的差价):
"price": {
"pricingMode": "delta",
"components": {
"baseline": {
"effectiveModuleSum": 47.17 }, // 旧配置剩余价值
"target": {
"effectiveModuleSum": 76.06 } // 新配置剩余时长价格
},
"calculatedAmount": 28.89 // ★ 实际需补差价
}
解读要点(重要)
先看
**pricingUnit**:有值(如
"1 Month"、"Once")→ 按它读,金额就是"每单位"的钱。缺失→ 说明这是按量报价,金额 = 你传入的预估量 × 单价,量纲就是你传入的那笔量(如 100GB)。不要误读成月费或一次性扣款。
pricingMode: delta看calculatedAmount(净差价),不要只看 baseline/target 的毛额。success: false时读errorCode/errorMessage,不要用退出码猜。
4. 命令参考
4.1 发现:哪些 API 支持询价
# 全量清单(自动翻页聚合)
aliyun list-supported-pricing-apis
# 查某产品(popCode 大小写以清单为准,如 Ecs / Rds / polardb)
aliyun list-supported-pricing-apis --cli-query 'supportedApis[?popCode==`Ecs`].apiName'
# 查某 API 是否被支持(有输出即支持)
aliyun list-supported-pricing-apis --cli-query "supportedApis[?apiName=='ModifyInstanceNetworkSpec']"
4.2 询价:--estimate-cost
# 内置路径(PascalCase)
aliyun ecs ModifyPrepayInstanceSpec --region cn-hangzhou \
--InstanceId i-xxx --InstanceType ecs.e-c1m2.large --estimate-cost
# 插件路径(kebab-case)
aliyun ecs modify-prepay-instance-spec --region cn-hangzhou \
--instance-id i-xxx --instance-type ecs.e-c1m2.large --estimate-cost
4.3 询价上下文:--estimate-cost-context
# 一个 flag 跟多对(推荐,一行搞定)
... --estimate-cost --estimate-cost-context EstimatedInternetTrafficOutGB=100 InternetChargeType=PayByTraffic
# 也可重复指定
... --estimate-cost --estimate-cost-context A=1 --estimate-cost-context B=2
规则:
必须 与
--estimate-cost同时使用(单独用会报错)。值按第一个
**=**切分(值里可含=);空值允许,空键报错。值一律作字符串传;服务端会把纯数字字符串自动转成数字参与计算(如
"100"→ 100)。
5. Step-by-step 案例:ECS 更换公网 IP 的多步询价
真实场景:一台固定带宽实例要更换公网 IP,需 5 步。目标:在执行第一步之前,预知整个流程的费用。
| 步骤 | 操作 | API | 是否涉费 |
|---|---|---|---|
| 1 | 带宽计费转按流量(固定带宽不能直接转 EIP) | ModifyInstanceNetworkSpec | ✅ |
| 2 | 固定公网 IP 转 EIP | ConvertNatPublicIpToEip | ✅ |
| 3 | 解绑 EIP | UnassociateEipAddress | ❌ 免费 |
| 4 | 设带宽 >0,分配新公网 IP | ModifyInstanceNetworkSpec | ✅ |
| 5 | 释放旧 EIP | ReleaseEipAddress | ❌ 免费 |
设
INS为实例 ID、地域 cn-hangzhou、预估月流量 100GB、新带宽 5Mbps。全程只询价,不执行。
步骤 1:带宽计费转按流量
转按流量后的费用取决于流量,而流量不是 API 参数、只存在于你的业务预期里 → 用 --estimate-cost-context 传预估用量:
aliyun ecs modify-instance-network-spec --region cn-hangzhou \
--instance-id $INS --network-charge-type PayByTraffic \
--estimate-cost \
--estimate-cost-context EstimatedInternetTrafficOutGB=100
# → InternetTrafficOut ¥80.00 / 100GB(即 0.8 元/GB)
语义提示:询价给出的是发生指定用量的未来价格。
步骤 2:固定公网 IP 转 EIP
无需上下文,直接插件命令询价:
aliyun ecs convert-nat-public-ip-to-eip \
--instance-id $INS --biz-region-id cn-hangzhou \
--estimate-cost
# → 转换后 EIP 的持有/流量单价(随实例当前计费模式:按带宽报"元/天",按流量报"元/GB")
步骤 3:解绑 EIP(免费)
# 对不涉费 API 询价,CLI 明确告知不涉费:
aliyun vpc unassociate-eip-address --region cn-hangzhou \
--allocation-id <eip-id> --instance-id $INS --instance-type EcsInstance \
--estimate-cost
# → Error: no pricing information ... (this OpenAPI either incurs no cost or
# has no pricing mapping registered yet) —— 免费步骤,预期如此
步骤 4:设带宽 >0,分配新公网 IP
难点:询价时实例还是固定带宽,但真正执行到第 4 步时已完成第 1 步(已按流量)。用 --estimate-cost-context 传状态覆盖,让报价基于"未来状态":
aliyun ecs modify-instance-network-spec --region cn-hangzhou \
--instance-id $INS --network-charge-type PayByBandwidth --internet-max-bandwidth-out 5 \
--estimate-cost \
--estimate-cost-context InternetChargeType=PayByTraffic EstimatedInternetTrafficOutGB=100
# → 基于"已按流量"的状态给出新 IP 相关费用
步骤 5:释放旧 EIP(免费)
aliyun vpc release-eip-address --region cn-hangzhou --allocation-id <old-eip-id> --estimate-cost
# → 免费操作;效果是停止旧 IP 计费(省钱项,非支出项)
汇总:执行前的账单预览
把 3 个涉费步骤的询价结果加总,即可在按下第一次回车之前得出整单预览:
| 步骤 | 性质 | 询价 |
|---|---|---|
| 1 转按流量 | 未来流量费 | ¥0.8/GB(× 预估量) |
| 2 IP 转 EIP | 转换后单价 | 按模式报价 |
| 4 新 IP 带宽 | 即时订单 / 未来费 | 见上 |
| 3、5 | 免费 | ¥0 |
目前跨步骤汇总在客户端完成(逐步询价 + 用 PricingContext 模拟状态依赖 + 相加)。"提交一组 API、返回整单总账"的服务端编排是后续方向。
6. PricingContext 深入
6.1 它承载两类东西
| 类型 | 例子 | 何时需要 |
|---|---|---|
| 用量假设 | EstimatedInternetTrafficOutGB=100 |
按量计费,费用 = 量 × 单价,而"量"只有你知道 |
| 状态覆盖 | InternetChargeType=PayByTraffic |
多步流程中,后一步依赖前一步执行后的状态 |
两者都装进同一个 --estimate-cost-context。
6.2 wire 格式(供理解 / 高级用法)
--estimate-cost-context 的键值对会被嵌套进请求体 parameters 内部的 PricingContext 对象,作为 API 参数的兄弟字段:
{
"popCode": "Ecs", "popVersion": "2014-05-26", "apiName": "ModifyInstanceNetworkSpec",
"parameters": {
"RegionId": "cn-hangzhou", "InstanceId": "i-xxx",
"NetworkChargeType": "PayByTraffic",
"PricingContext": {
"EstimatedInternetTrafficOutGB": "100" }
}
}
7. 错误处理与排查
| 现象 | 含义 / 处理 |
|---|---|
no pricing information for .../... |
该 API 不涉费,或尚未支持询价 |
No pricingTarget matched the input request |
参数组合没命中任何计价规则;常见于缺少必需的 PricingContext(如按流量缺 EstimatedInternetTrafficOutGB)。错误信息里会列出各 target 的 when 条件,对照补参数 |
--estimate-cost-context requires --estimate-cost |
忘了加 --estimate-cost |
invalid --estimate-cost-context 'xxx' |
缺 = 或键为空,按 Key=Value 修正 |
success: false + errorCode |
业务失败,读 errorMessage;带 upstreamRequestId 可找计费侧排查 |
报价成功但无 pricingUnit |
按量报价,金额是"预估量 × 单价",别当月费(见 §3.5) |
附:命令速查
| 目的 | 命令 |
|---|---|
| 列出支持询价的 API | aliyun list-supported-pricing-apis |
| 询价(不执行) | aliyun <product> <Api> ... --estimate-cost |
| 询价 + 上下文 | ... --estimate-cost --estimate-cost-context K1=V1 K2=V2 |
| 事后账单(互补) | aliyun bssopenapi QueryAccountBill --BillingCycle YYYY-MM |