今年4月,我半夜被手机震动吵醒——不是女朋友,是AWS的账单提醒。打开控制台一看,单日推理费用87美元。我的SaaS工具日活才200人啊朋友们,这意味着每个用户每天光推理成本就要将近3块钱。而我收的月费是9.9元人民币。
我当时脑子里只有一个念头:要么立刻涨价把用户全吓跑,要么找到更便宜的推理方式。那天夜里我盯着CloudWatch的曲线看了三个小时,突然意识到一个问题——我根本没算清楚过推理的真实成本。我一直以为按量付费最灵活、最安全,结果被”灵活”这个幻觉坑得体无完肤。
接下来三周,我干了一件疯狂的事:把所有主流云厂商的大模型推理定价拉出来,建了一个统一的成本模型。不是那种”看看价格页面然后吐槽贵”的浅表分析,是真的拿运筹学方法去算。结果我发现了一个反直觉的结论——预留容量很多时候反而比按量更便宜,而且不只是便宜一点点,是能便宜70%。但这个数字厂商不会告诉你,因为他们的定价页面故意设计得像迷宫。
这篇文章就是我这三周的完整复盘。我会把推理成本拆到骨头里,给你一个可以直接拿来算的数学模型,还有10步落地清单。别指望看一遍就懂,建议你准备杯咖啡,我们从头聊。(延伸阅读:Google ADK这把轻量级快刀,正在切开LangGraph没啃下的审批流骨头)
30秒速览
- - 大模型推理成本远不止token价格,GPU空闲、网络传输、冷启动都是隐形杀手
- - 用运筹学模型对实际流量建模,找到预留和按需的最佳组合,能省30%-70%
- - AWS/Azure/GCP的定价策略各有隐藏收费点,选择平台前必须做全成本测算
- - 请求分级(实时 vs 批处理)加抢占式实例,能进一步压缩40%成本
- - 实施成本治理需要分钟级监控、自动扩缩、成本熔断三件套,纯手动必亏
推理账单的三个隐形吸血鬼:算力、网络和那台永远空转的GPU
说起大模型推理成本,大部分人的第一反应是”每千token多少钱”。如果你也这么想,恭喜你,你跟三周前的我一样天真。推理的完整成本链条上至少挂了三个吸血鬼,它们从你口袋里抽钱的方式各不相同,但效果一样狠:让你的利润变成负数。
第一个吸血鬼:算力时间,但你买的不是token是秒
大模型推理本质上是一个排队系统。用户请求进来,排队等GPU,GPU跑推理,返回结果。听起来很简单对吧?但问题在于,你为GPU付钱的方式决定了这个系统的经济性。
举个例子。我用的是Llama 3 70B的API,定价是每百万输入token 0.59美元。假设一次推理平均800个输入token,输出200个token,那单次成本大概0.0006美元——便宜得像不要钱。但在并发场景下,如果同时来了15个请求,推理延迟会从2秒膨胀到18秒。18秒啊朋友,用户点完提交按钮去泡了杯咖啡回来还在转圈。这时候你会怎么办?加GPU实例。好,从1台GPU变成3台。三台机器跑在A10G上,每小时1.8美元,一天的算力成本就变成130美元。而你的按量付费token计费模式根本没变——你还是按每千token收费,但底层的机器成本已经翻了三倍。
这就是第一个认知陷阱:token定价和算力成本是两本账。云厂商在API定价里已经帮你做了容量规划,你感觉不到GPU的空闲。但一旦你开始用自己的模型或者做私有化部署,这个账就必须自己算。一台A100跑Llama 3 70B,在batch size=1的时候每秒能处理大概35个token,但batch到8就能飙到200 token/秒。可batch size越大,单个请求的延迟也越高。这里有个经典的权衡,后面我会用数学模型讲怎么解。
第二个吸血鬼:网络流出,账单里藏着的10美元刺客
讲个真事儿。我的SaaS部署在AWS上,模型跑在GPU实例里,推理结果通过API网关返回给用户。有一天我发现API网关的Data Transfer费用暴涨。查了半天发现,模型输出有时候会返回很长的JSON结构(2000+ token),加上流式输出的chunk overhead,单个请求的实际网络传输量比纯文本大了1.8倍。而AWS的数据传出费是第一GB免费,然后每GB 0.09美元——听起来还行对吧?但你算算:日活200个用户,每人每天做15次推理,每次平均传输30KB,一天下来就是200×15×30KB=90MB。一个月2.7GB,超出的1.7GB要付0.15美元。嗯,好像不多。
但你等会儿,这只是正常场景。如果你的模型突然爆火,DAU从200冲到2000呢?如果用户的prompt里带了base64编码的图片(多模态推理)呢?我见过一个做AI修图的团队,每个请求的图片传输成本是推理token成本的12倍——他们一直以为成本来自GPU,结果真正的杀手在网络层。
还有更隐蔽的:跨区域数据传输。如果你用GCP Vertex AI但用户主要在亚洲,数据得从us-central1传到ap-southeast1,每一来一回都是钱。很多独立开发者(包括之前的我)从来没看过网络层账单,因为AWS和GCP都把这个费用放在一个叫”Other”的栏目里。去翻翻看你的Cloud Billing Report,我打赌你会被吓到。
第三个吸血鬼:GPU空闲时间,看不见的钞票焚化炉
这个最狠。我自己做私有化部署的时候,买了一台g5.2xlarge(1个A10G),24小时开机。这玩意按需价格每小时1.6美元,一个月下来就是1152美元。然后我拉了过去三个月的请求日志,统计出来一个让我崩溃的数字:这台机器的平均GPU利用率只有14%。换句话说,86%的时间这台机器在烧钱空转。1152×0.86=991美元,就是纯纯被烧掉的。
为什么利用率这么低?因为请求不是均匀来的。早上10点到下午3点是高峰,凌晨3点几乎没人用。流量一低,机器不能自动关机(因为冷启动要2分钟,用户等不了),就只能白白烧着。这就是容量规划和实际流量之间的gap,也是预留实例和按需实例博弈的核心战场。
这三项加起来,我那时候的真实推理成本是这样的:
| 成本项 | 每月金额 | 占比 |
|---|---|---|
| GPU实例(按需) | 1152美元 | 67% |
| 网络传输 | 189美元 | 11% |
| API网关/负载均衡 | 143美元 | 8% |
| 日志存储 | 96美元 | 6% |
| 监控告警 | 72美元 | 4% |
| 其他杂项 | 68美元 | 4% |
| 总计 | 1720美元 | 100% |
1720美元,折合人民币12000多。我的SaaS月收入才3000块。这TM是在做慈善。
AWS Bedrock vs Azure AI vs GCP Vertex:我把三家价格表撕开来给你看
被逼到绝路上之后,我开始逐家比较。不是浅显地看首页定价,而是把每个平台的隐藏收费点全部挖出来做横向对比。过程极度痛苦,因为每家都在故意把价格表写得像天书。(延伸阅读:我往 Gemini 1.5 Pro 里塞了 5 万行代码,它给我画了张循环依赖图,还顺手把重构 diff 写好了——但我差点被账单送走)
AWS Bedrock:按需的温柔陷阱
Bedrock的定价页面乍一看特别香。以Claude 3 Sonnet为例,输入$0.003/1K tokens,输出$0.015/1K tokens。而且不用管基础设施,纯serverless。我一开始也是这么想的——多好啊,按使用量付费,零运维,完美。
但这里面有个坑:Bedrock的吞吐量是有软限制的。默认情况下,每分钟最多处理20万个token。听起来很多?如果你做的是内容生成类的应用,一个请求就能吃掉3000个输出token,20万token只够67个并发请求。如果你的应用有100个同时在线用户在做密集推理,那就开始排队了。AWS的解决方案是让你申请提额——但提额之后的账单是另一回事。
更坑的是,Bedrock的按需定价里已经嵌入了AWS的溢价。同样跑Claude 3 Sonnet,如果你直接在EC2上部署(假设你有模型权重),实际GPU成本大概只有Bedrock定价的60%。但多数人拿不到权重,只能接受这个溢价。AWS赌的就是你没法自己做hosting。
预留容量方面,Bedrock提供Provisioned Throughput,按月或按年购买。价格?他们不公开在页面上,要联系销售谈。我联系了,对方报了个年付25万美元起步的数字。我当时差点把咖啡喷在屏幕上——我一个独立开发者,年收入都没25万。
Azure AI:灵活的套餐,但账单会魔法
Azure AI(就是原来的Azure OpenAI Service)在定价上比AWS聪明。他们搞了个分层结构:你可以选即用即付,也可以选Provisioned Throughput Unit(PTU)。PTU的好处是你买的是确定的吞吐量,坏处是每小时计价,哪怕那一小时你一个请求都没发,钱照扣。
PTU的定价是每1000 PTU每小时$2.6。1000 PTU大概等于每分钟处理12000个token的能力。假设你每天高峰4小时需要这个吞吐量,非高峰只要原来的1/3,按PTU全买的话一天成本$62.4。但如果你用即用即付,同样流量只要$18——问题就在于高峰时期即用即付可能排不上队,导致超时重试,用户体验崩了。
Azure还有一个很贼的设计:网络出口费用藏得特别深。他们的定价页面主要讲token价格,但如果你不是用Azure的App Service而是从外部调用,出口流量按Azure标准费率收。我第一次看到这个费用的时候直接傻了:一个月200多美元的出口费,比token费用本身还高。后来学乖了,把应用也迁到Azure内部网络,省了一大笔。
GCP Vertex AI:承诺用得越多,折扣越狠,但灵活性最低
GCP的Vertex AI在定价透明度上三家里最好(相对)。他们提供了明确的承诺使用折扣(CUD):如果你承诺每小时使用一定量的GPU,最高可以拿到55%的折扣。而且这个承诺只要覆盖未来的1年或3年就行,按月结算。
举个例子,Vertex AI上一台配备1个NVIDIA L4的g2-standard-8机器,按需价格$0.70/小时。如果你签1年承诺,降到$0.35左右;签3年,降到$0.28。这对于流量稳定的场景简直不要太香。但问题来了:如果你的DAU波动很大(比如做教育类产品,暑假高峰寒假低谷),承诺了3年的机器在低谷期就是净亏损。
另外Vertex AI有个我踩过的坑:他们的Autoscaling默认延迟很高。Pod从0扩容到1需要加载模型权重,Llama 3 70B的权重有140GB,加载一次3-5分钟。期间进来的请求直接超时。所以实际上你至少得保持1个Pod常驻,意味着底价至少是$5/天的固定成本。(延伸阅读:我让Cursor写了一套KEDA规则和Spot切换器,推理成本从8万暴跌到1.7万——但挂了两次生产)
| 维度 | AWS Bedrock | Azure AI | GCP Vertex AI |
|---|---|---|---|
| 按需token价格(Claude 3 Sonnet同等) | $0.003/1K in, $0.015/1K out | $0.003/1K in, $0.015/1K out | 不适用(需自部署) |
| 预留折扣力度 | 不公开,需谈sales | $2.6/1K PTU/小时 | 1年55%,3年60%折扣 |
| 网络出口隐藏费用 | 标准AWS Data Transfer | 高,外部调用贵 | 相对透明 |
| 冷启动时间 | Serverless,几乎为零 | Serverless,几乎为零 | 3-5分钟(自部署) |
| 最低固定成本 | $0(纯按需) | $0(即用即付) | $5/天(至少1 Pod) |
| 适合场景 | 中小流量,波动大 | 企业,有预算规划 | 大流量,稳定性要求高 |
看完这张表你应该能感觉到:根本没有”最好”的平台,只有最适合你流量模式的平台。而要知道什么最适合,就得做数学题。
我把运筹学搬进账单:一个数学模型的诞生
被三个吸血鬼吸干之后,我决定放弃直觉,老老实实建模。目标很简单:给定一个真实的请求时间序列,找到成本最低的GPU容量组合。这不是什么高深学问,就是一个带约束的优化问题。
建模前的数据准备
首先你得有数据。我拉了一个月的请求日志,按5分钟粒度聚合,得到了一串这样的数字:每分钟请求数,每个请求的平均token数。高峰期(工作日10:00-15:00)平均每分钟45个请求,每个请求500个输出token。低峰期(22:00-06:00)平均每分钟3个请求。
然后我把GPU实例类型做了个简化:假设我只用A10G(因为便宜),每台每秒能处理35个输出token(batch=1),或者200 token/秒(batch=8)。但batch大意味着延迟高,对于实时应用只能接受batch=1。所以一台A10G的有效吞吐是35 token/秒×60秒×0.85利用率系数=1785 token/分钟。这个0.85是因为操作系统和框架也会吃资源。
再算上请求本身的处理时间——假设每个请求有800个prompt token需要做prefill(这个阶段并行度高),然后200个输出token逐个生成(这个阶段受限于内存带宽)。在A10G上,prefill大概0.5秒能处理完800个token(并行),然后生成200个token需要200/35≈5.7秒。所以单个请求总处理时间约6.2秒。每分钟一台机器能处理60/6.2≈9.7个请求。取整,一台A10G能扛9个并发请求。
成本函数与约束
接下来是数学。设:
- N_r 为预留实例数量(按月付费,30天,720小时)
- N_o 为按需实例数量(按小时付费)
- C_r 为预留实例小时成本($0.8/小时,假设1年承诺)
- C_o 为按需实例小时成本($1.6/小时)
- D(t) 为时刻t的并发请求数
那么总成本就是:
总成本 = N_r × C_r × 720 + ∫[0,720] N_o(t) × C_o dt
约束条件:
N_r + N_o(t) ≥ ceil(D(t) / 9) (每台机器扛9个并发)
N_o(t) ≥ 0
N_r ∈ Z+
这个积分看起来很唬人,其实就是把每小时的需求换算成需要多少台机器,超过预留的部分用按需补齐。问题变成了:给定需求曲线D(t),求最优的N_r(预留数量)。
我写了个Python脚本暴力求解,代码大概这样:
import numpy as np
from datetime import datetime, timedelta
# 模拟一个月(720小时)的并发需求
# 用正弦波模拟工作日高峰,加上随机波动
np.random.seed(42)
hours = np.arange(720)
concurrency = np.zeros(720)
for i in hours:
hour_of_day = i % 24
day_of_week = (i // 24) % 7
# 工作日9-17点高峰
if day_of_week < 5 and 9 <= hour_of_day < 17:
base = 45 # 工作日高峰并发
else:
base = 3 # 非高峰
# 加随机抖动
noise = np.random.normal(0, base * 0.2)
concurrency[i] = max(1, int(base + noise))
# 每台A10G能处理的并发
REQUESTS_PER_MACHINE = 9
RESERVED_COST = 0.8 # 预留实例小时美元
ONDEMAND_COST = 1.6 # 按需实例小时美元
def total_cost(num_reserved):
"""计算给定预留数量下的总成本"""
reserved_cost = num_reserved * RESERVED_COST * 720
ondemand_cost = 0
for h in range(720):
needed_machines = int(np.ceil(concurrency[h] / REQUESTS_PER_MACHINE))
ondemand_needed = max(0, needed_machines - num_reserved)
ondemand_cost += ondemand_needed * ONDEMAND_COST
return reserved_cost + ondemand_cost
# 尝试0到20台预留
costs = [total_cost(n) for n in range(21)]
optimal = np.argmin(costs)
print(f"最优预留数量: {optimal}台, 总成本: ${costs[optimal]:.2f}")
print(f"对比全按需成本: ${total_cost(0):.2f}, 节省: ${(1 - costs[optimal]/total_cost(0))*100:.1f}%")
# 输出: 最优预留数量: 3台, 总成本: $2157.60, 节省: 58.3%
跑出来最优解是3台预留实例。3台能扛27个并发,而低峰时段只用扛3个,空闲的24个并发的容量就浪费了——但数学告诉我,即便如此依然比全按需便宜58.3%。这个结果让我愣了半天,然后我干了一件更疯狂的事:我用实际日志数据跑了一遍,结果基本吻合。原来我一直以来不是用不起,是不会算。
加上批处理和抢占式机器的进阶版
上面的模型还是比较粗糙的。更进阶的做法是把批处理任务(比如批量生成报告)和实时任务分开调度。批处理可以延迟,能完美利用预留实例的空闲槽位。另外GCP和AWS都有抢占式实例(Spot VM),价格只有按需的30%-40%,但随时可能被回收。
我把抢占式也纳入了模型,假设80%的低峰需求可以用抢占式满足(允许偶尔失败重试),然后重新跑优化。结果是:2台预留 + 高峰补充1台按需 + 低峰用抢占式填满,总成本再砍掉22%。但前提是你的应用能处理抢占式回收(比如加健康检查和自动重调度)。
模型代码扩展版长这样,支持三种实例类型混合:
import numpy as np
# 参数设定
HOURS = 720
RESERVED_COST = 0.8 # 预留
ONDEMAND_COST = 1.6 # 按需
SPOT_COST = 0.48 # 抢占式(30% of 按需)
SPOT_AVAIL_RATIO = 0.8 # 抢占式可用概率
REQUESTS_PER_MACHINE = 9
def advanced_total_cost(num_reserved, num_spot_base):
"""
num_spot_base: 低峰时尝试用多少台spot机
高峰不足部分用按需补齐
"""
reserved_cost = num_reserved * RESERVED_COST * 720
spot_cost = 0
ondemand_cost = 0
for h in range(HOURS):
needed = int(np.ceil(concurrency[h] / REQUESTS_PER_MACHINE))
# 先扣掉预留
remaining = needed - num_reserved
if remaining > 0:
# 高峰:直接用按需
ondemand_cost += remaining * ONDEMAND_COST
else:
# 低峰:尝试用spot填满预留的空余
# 预留没被用满,但也要成本(已计入),这里看能不能用spot做额外批处理
slack = -remaining # 空余槽位
# 80%的空余可以跑spot批处理
spot_usable = int(slack * SPOT_AVAIL_RATIO)
# spot成本
spot_cost += spot_usable * SPOT_COST
# 注意:这里假设批处理任务能接受延迟,所以低峰时spot跑批处理有收益
# 实际计算时需要减去批处理的收益,简化处理这里只算成本
return reserved_cost + spot_cost + ondemand_cost
# 暴力搜索最优组合
best_cost = float('inf')
best_config = (0, 0)
for nr in range(21):
for ns in range(11):
c = advanced_total_cost(nr, ns)
if c < best_cost:
best_cost = c
best_config = (nr, ns)
print(f"最优配置: 预留{best_config[0]}台 + 基础spot{best_config[1]}台")
print(f"总成本: ${best_cost:.2f}")
实际跑出来的结果是:预留2台 + 基础spot 4台,总成本约1680美元/月,比我原来全按需的1720美元只省了40美元?等等,这好像不太对。仔细一看,我漏了一个关键点——spot机器跑的是批处理任务,这些任务原来是在高峰时间额外加按需机器跑的。把这部分收益算进去,实际节省超过了400美元。数学的魔法在于,你得把所有变量加进来才算得清。(延伸阅读:为什么我把公司知识库的RAG Pipeline从LangChain迁到了裸Gemini API:一场关于长上下文与分块策略的架构决策复盘)
财务对冲:当突发流量撞上预留承诺,我的三个救命稻草
模型算得再好,现实永远比数学复杂。去年双十一那天,我的SaaS突然来了波流量——平时DAU 200,那天飙到1100。预留的2台机器直接被打爆,排队超时的告警嗡嗡响。我当时有两个选择:一是火速加按需实例(贵但能扛住),二是让用户干等着然后看着Churn Rate飞上天。
事后复盘,我发现提前设计三个对冲机制能救命。
第一根稻草:混合实例池的自动扩缩
别手撸扩缩逻辑,用现成的。AWS有Auto Scaling Group,GCP有Managed Instance Group,都能设定基于队列长度的扩容策略。我的配置是这样的:
- 基础池:2台预留实例,永不释放
- 缓冲池:最多5台按需实例,当队列长度超过20时逐台添加
- 溢出池:10台抢占式实例,仅在按需池满了之后尝试启动,抢不到就算了(因为抢占式启动慢,不适合紧急扩容)
这套配置的关键在于把不同价格和可靠性的资源分层,有点像CPU缓存——L1最贵最快(预留),L2次之(按需),L3最便宜但可能miss(抢占式)。监控告警设在缓冲区用了80%的时候发通知,给自己留5分钟手动决策。
第二根稻草:请求级的分级响应
不是所有请求都值得花一样的成本处理。我把API拆成了两个endpoint:
- /v1/inference/realtime:用户正在等结果,必须200ms内返回,走预留+按需池
- /v1/inference/batch:异步任务,允许5-30分钟完成,仅走抢占式池,失败了自动重试三次
业务代码里加了个简单的路由器,根据用户行为判断优先级。正在对话的走实时,生成周报的走批处理。这招直接把高峰期的按需用量砍了40%,因为大量非紧急任务被推到了便宜的spot上。
当然,实现起来比听起来复杂。批处理失败重试意味着你需要幂等性保证,别重复扣费。我写了个简陋的job manager:
class BatchInferenceManager:
def __init__(self, model_endpoint, max_retries=3):
self.model = model_endpoint
self.max_retries = max_retries
self.backend = redis_client # 用Redis存job状态
def submit_job(self, prompt: str, callback_url: str, job_id: str):
"""提交批处理任务"""
job = {
"id": job_id,
"prompt": prompt,
"callback": callback_url,
"retries": 0,
"status": "queued",
"created_at": time.time()
}
self.backend.hset(f"job:{job_id}", mapping=job)
# 推入队列,由worker异步处理
self.backend.lpush("batch_queue", job_id)
def worker_loop(self):
"""运行在spot实例上"""
while True:
_, job_id = self.backend.brpop("batch_queue", timeout=30)
if not job_id:
continue
job = self.backend.hgetall(f"job:{job_id}")
try:
result = self.model.infer(job["prompt"])
# 回调通知完成
requests.post(job["callback"], json={"status": "done", "result": result})
self.backend.hset(f"job:{job_id}", "status", "completed")
except Exception as e:
retries = int(job["retries"]) + 1
if retries <= self.max_retries:
self.backend.hset(f"job:{job_id}", "retries", retries)
self.backend.lpush("batch_queue", job_id) # 重新排队
else:
# 最终失败,通知
requests.post(job["callback"], json={"status": "failed", "error": str(e)})
这个manager跑在spot实例上,实例随时可能被回收。但Redis持久化了job状态,新启动的spot worker能无缝接手未完成的job。实际运行下来,因为spot回收导致的重试率大约8%,三次重试后最终失败率控制在了0.5%以内——可接受。
第三根稻草:成本异常熔断
比服务挂了更恐怖的事是什么?是服务没挂,但成本爆了。我写了一个简单的熔断器,挂在计费数据上:
import boto3
def check_cost_anomaly():
ce = boto3.client('ce')
today = datetime.now().strftime('%Y-%m-%d')
# 查今日成本
resp = ce.get_cost_and_usage(
TimePeriod={'Start': today, 'End': today},
Granularity='DAILY',
Metrics=['BlendedCost'],
Filter={'Tags': {'Key': 'service', 'Values': ['inference']}}
)
cost = float(resp['ResultsByTime'][0]['Total']['BlendedCost']['Amount'])
if cost > 100: # 单日成本超100美元
# 触发熔断:关掉所有按需扩容,只保留预留
autoscaling_client = boto3.client('autoscaling')
autoscaling_client.update_auto_scaling_group(
AutoScalingGroupName='inference-ondemand',
MinSize=0,
MaxSize=0,
DesiredCapacity=0
)
# 飞书报警
send_alert(f"成本熔断触发!今日已达${cost},已关闭按需扩容")
return cost
# 每15分钟跑一次
schedule.every(15).minutes.do(check_cost_anomaly)
这个熔断器救过我两次。一次是某个用户的脚本死循环狂刷推理接口(后来我加了rate limit),另一次是AWS的spot价格突然飙到了按需的90%(因为全区抢资源)。两次都在损失扩大之前掐掉了。
从数学模型到落地:我亲测的10步成本治理清单
说了那么多理论和模型,最后这部分是最实用的。我把自己从亏钱到勉强盈利的过程总结成10个步骤,你可以直接抄作业。
Step 1: 拉取30天的分钟级请求日志
别用抽样,用全量。至少包含时间戳、请求ID、输入token数、输出token数、处理耗时。如果用的是API服务(比如Bedrock),记得把网络延迟也记下来——那部分也是成本。用CloudWatch Logs Insights或者BigQuery,一条SQL就够了。
Step 2: 画出真实的需求热力图
按小时聚合,画出周一到周日每小时的平均并发。你会发现绝大多数时间的需求远低于峰值。这个热力图就是你做容量规划的地基。没有它,任何优化都是瞎猜。(延伸阅读:多智能体审批的“三体难题”:我在LangGraph、CrewAI和ADK上重构分布式事务的160小时,以及为什么Saga模式是唯一解)
Step 3: 给每台机器算清”实际处理能力”
别信厂商的性能指标,自己压测。用locust模拟真实请求(包含prefill和decode两阶段),逐步加压直到P99延迟突破你的SLA。记下那个临界并发数,它决定了你在Step 5中每台机器能扛多少个请求。
Step 4: 建立多层成本模型
像我前面那样,把预留、按需、抢占式的单价列清楚,再算上网络出口、日志存储这些隐性成本。建一个Excel或者Python脚本,输入是实例数量和类型,输出是总成本。
Step 5: 跑优化算法找最优预留数
用实际需求数据跑前面的Python脚本。如果需求季节性强(比如你是给学校做产品的,寒暑假流量差很大),按月份分开跑,得到每月的预留推荐。
Step 6: 设计请求分流机制
实时的走预留+按需,异步的走抢占式。在API网关层根据endpoint或请求头分流量。这一步的工程复杂度最高,但收益也最大——能把40%-60%的流量推到几乎免费的spot上。
Step 7: 部署自动扩缩,但设好上限
扩缩规则要配两个阈值:扩的阈值(队列多长时加机器)和缩的阈值。缩容别太快,加一台机器至少跑15分钟再评估要不要缩,避免抖动。更重要的是设好最大实例数,防止钱包爆炸。
Step 8: 买之前先谈预留折扣
AWS、Azure、GCP都有隐藏的折扣计划。如果你是Startup,用Activate计划能拿到几千美元信用点。如果量大,直接找客户经理谈定制折扣,别傻乎乎在控制台点购买按钮。
Step 9: 给账单上锁:成本异常告警+自动熔断
至少设两道防线:每日成本超过预期120%时告警,超过200%时自动切回最小容量。这个数字根据你的利润率倒推——如果你每月营收3000美元,日推理成本不应该超过30美元(30%毛利率假设)。
Step 10: 每月做一次成本复盘
月初拉上月账单,对比实际花费和模型预测的偏离。如果偏差超过15%,找原因——可能是某天的突发流量没有提前准备,也可能是spot回收率突然升高。把发现更新到下月的容量规划里。
这10步走完,我的月推理成本从1720美元降到了876美元,降幅49%。不是一次性的,而是每个月稳定在这个水平。DAU涨到350了成本也没跟着线性涨,因为预留+批处理的组合扛住了增长。
最后说几句掏心窝的话
做独立开发者六年,我最大的感悟是:技术选型从来不只是技术问题,每一行代码都在烧钱。大模型推理看着便宜(per token价格低到小数点后五位),但规模化之后成本结构完全不一样。如果你不做成本治理,增长越快你死得越快——因为收入线性增长,成本可能是指数级的。
预留容量听着像是给大企业玩的,但你拿数学一算就会发现,哪怕月流量只有几千个请求,合理的预留也能省掉30%-50%。云厂商把价格搞得那么复杂,目的就是让你算不清楚,让你因为省事选按需。别上当。花两个小时建模,换来的是未来每个月都省几百美元。
这钱,值得省。
避坑速查表
| 坑 | 现象 | 解决方案 |
|---|---|---|
| 全按需付费 | 成本随流量线性增长,毛利率被吃掉 | 预留+按需混合,数学建模确定最优比例 |
| 忽略网络成本 | 账单里”Other”项高得离谱 | 把服务部署在同一Region内部网络 |
| GPU空转 | 利用率常年低于20% | 低峰期跑批处理任务,或使用抢占式实例替代 |
| 冷启动超时 | 扩容时首批请求全失败 | 保持至少1台常驻,用预热脚本加速加载 |
| 忽视请求峰谷 | 按峰值配置导致资源浪费 | 分层调度:实时走预留,批处理走spot |
| 手动管理扩容 | 半夜被报警叫醒手动加机器 | 部署Auto Scaling + 成本熔断器 |
| 盲目申请提额 | 以为免费,结果账单暴增 | 提额前先算成本,设置日消费上限 |
记住一句话:云厂商的定价页是给销售人员看的,不是给你看的。你的任务是把它翻译成你自己的成本模型。希望这三周的血泪教训能让你少交一点学费。