深夜十一点,海鲜市场收摊前的最后半小时。摊主面前还有两筐鱼没卖完,明天就不新鲜了,于是挥泪甩卖:原价三十块一斤的鲈鱼,现在十块。你是一个明天要办五十桌流水席的厨师,预算紧张,眼睛一亮——但摊主补了一句规矩:“这些特价鱼,要是有人按正价来买,我优先卖给他,你手里的得收回来。” 你纠结了:便宜是真的便宜,可鱼随时可能被收走,宴席怎么办?聪明的厨师会怎么做?他会把鱼处理到一半就塞进冰箱冷藏(检查点),眼睛盯着摊主的一举一动(中断预测),被收走就立刻去隔壁摊补货(迁移),收摊时拿出账本细算,省下的钱远大于被收走的损失。云厂商的 Spot 与 Preemptible 实例,就是这场“收摊前的特价鱼”。AI 训练动辄烧掉几十万 GPU 费用,而云上 50%-90% 的廉价算力就在眼前,代价是它可能随时被“收回”。本文用一场海鲜市场的买卖,讲透如何用最便宜的方式驯服这股随时会断供的算力潮。
理解 Spot 与 Preemptible 实例的完整策略,是 FinOps 工程师和 AI 基础设施团队把 GPU 账单削减 50% 以上的必备技能,也是成本优化课程中最具杠杆效应的一环。
可视化演示
演示Spot/Preemptible实例的成本节省、中断风险与检查点恢复机制,展示以性价比换取中断容忍的取舍。
本节配有交互动效(进入视口后自动加载,约 3KB)。
成本节省 Spot利用空闲算力打折 节省率:按需 vs Spot价格差 低至按需价的数成,成本大幅下降。
实例概念认知
厨师逛海鲜市场,先得明白“正价鱼”和“特价鱼”的区别。正价鱼三十块一斤,摊主保证供应、随到随买;特价鱼十块一斤,唯一的代价是:只要有正价顾客来,摊主就把你手里的鱼收走。这就是把“保证供应”四个字从合同里删掉换来的折扣。
云厂商每天也有大量“没卖完的鱼”——闲置的 GPU 和 CPU 算力。机器空转照样耗电、折旧,不如低价卖掉。于是 AWS 的 Spot、Google Cloud 的 Preemptible、Azure 的 Spot VM 相继出现:它们以按需价格的一到五折售卖,随时可能被厂商回收。在 AI 训练和推理场景中,容错性强的离线批处理任务、带检查点的大模型训练,天然适合这类“特价算力”。
折扣到底有多大?一个公式就能看清:
理解了“特价鱼的规矩”,你就明白它不便宜得莫名其妙:便宜的代价,是放弃了“永不收走”的承诺。后面几节,全是厨师应对这个承诺缺失的招数。
- 按需价格 \\(P_{\text{ondemand}}\\)摊主卖正价鱼的挂牌价,即按需实例每小时费用,它是所有成本比较的锚点。
- Spot 市场价格 \\(P_{\text{spot}}\\)特价鱼当前的挂牌价,由空闲算力的即时供需决定,时常波动。
- 价格比值 \\(\dfrac{P_{\text{spot}}}{P_{\text{ondemand}}}\\)你实际为算力付的是正价的几折,0.3 就是三折拿货。
- 节省率 \\(\text{SavingRate}\\)用 1 减去价格比值,得到比按需便宜的比例,这是 Spot 诱惑力的来源。
获取策略设计
特价鱼便宜,但你不能闭着眼睛冲进去就买。有经验的厨师会先定一个心理价位:这条鱼超过多少钱我就不买了?心理价位就是 Spot 的出价上限(bid)。摊主喊价,市场价低于或等于你的心理价位,成交;超过,你就收手等下一波。
出价怎么定?很多人误以为“出价越高越稳”,实际上成交后按市场价计费,而不是按你的出价——出高价不会让摊主高看你一眼,更不会减少收鱼概率,只会白花冤枉钱。实践中,出价上限一般设置为接近但不超过按需价格,因为市场价很少超过按需价;同时要在多个可用区(不同摊位)之间分散询价,有些摊位鱼多价低,容量池更大,命中率更高。
取实例的成交规则用公式表达:
心理价位定在“不会亏本”的水平,摊主喊价超过了就收手。淘鱼也是有纪律的,盲目加价既买不来稳定,也换不来便宜。
- 出价上限 \\(P_{\text{bid}}\\)你心里为这条鱼定的最高价,超过这个价宁可不买。
- 市场价 \\(P_{\text{market}}\\)摊主此刻的挂牌价,即 Spot 实例实时成交价,随供需上下浮动。
- 成交条件 \\(P_{\text{bid}} \ge P_{\text{market}}\\)你的心理价位不低于挂牌价时,这单买卖才成立。
检查点机制
流水席上最怕什么?鱼被收走了,你处理到一半的食材也一起没了。聪明的厨师会在处理到某个节点时,把半成品整个塞进冰箱冷冻起来——这就是检查点(checkpoint)。就算鱼被收走,你损失的无非是最后一次入冰箱之后那点功夫,冰箱里的半成品随时能解冻继续。
在 AI 训练里,检查点就是周期性把模型权重、优化器状态、当前训练步数等快照保存到持久存储。中断发生后,新实例从最近的检查点恢复,而不是从头训练。这里有个关键权衡:检查点周期太短,频繁写存储拖慢训练、增加成本;周期太长,中断时回退损失就大。生产实践通常把周期设在 5-15 分钟,刚好覆盖平均中断恢复时间。
中断带来的期望回退工作量,等于检查点周期的一半:
冰箱是厨师的安全网。有了它,“被收走”从灾难变成了小插曲——代价只是偶尔从半格之前重新下锅。
- 检查点周期 \\(T_{\text{ckpt}}\\)两次“入冰箱”之间的时间间隔,即每隔多久保存一次训练状态。
- 期望损失 \\(\mathbb{E}[W_{\text{loss}}]\\)一次中断平均丢掉多少训练进度,是一个统计期望值。
- 周期的一半 \\(\dfrac{T_{\text{ckpt}}}{2}\\)中断落在任意位置的概率均等,平均离最近检查点半格。
中断预测
如果摊主收鱼前愿意喊一嗓子:“这条鱼要收回了,你准备一下!”你会不会从容很多?云厂商深谙此道:GCP 在抢占前约 30 秒发出通知,AWS 提供最长 2 分钟的中断预告,Azure 同样有 30 秒警告,AWS 甚至提供提前 15 分钟的 Spot 中断预测,命中率高达九成以上。
收到预告后的动作是标准流程:立即把最新模型状态写入检查点、优雅地退出进程、通知调度器“这里要塌了,去别处续跑”。这一套组合拳,把“被动挨打”变成“主动让位”。所谓主动让位,就是在厂商真正回收之前自己先退——反正近一分钟的进度已经保住了,与其被强杀,不如体面离场。
从预告到完成保存,计算一下有效准备时间:
摊主喊“收鱼啦”,第一动作永远是开冰箱门。预告窗口把不确定性变成了可规划的流程,这是 Spot 能在生产环境大规模落地的前提。
- 预告窗口 \\(T_{\text{notice}}\\)摊主提前喊话的时间,云厂商给出的中断预告时长(30 秒到 2 分钟)。
- 保存耗时 \\(T_{\text{save}}\\)把半成品塞进冰箱的时间,即写检查点到持久存储的开销。
- 有效准备时间 \\(T_{\text{buffer}}\\)预告减去保存耗时后剩下的富余,富余越多,收尾越从容。
迁移方案
鱼被收走了,宴席不能停。厨师的办法是:这家摊不行,隔壁摊还有鱼。在云上,“隔壁摊”就是另一个可用区或另一个实例组。当你把 Spot 实例部署成多可用区的实例组,一台被回收,调度器立刻在另一个可用区拉起新实例,作业自动重投。
想让迁移足够快,前提是“数据不入锅”——把训练数据放在共享存储(如 EFS、Ceph)上,新实例启动时直接挂载,不必把几百 GB 数据集重新搬一次。模型权重通过检查点同步,优化器状态同理。这样整个切换可以做到分钟级甚至秒级,对上层作业几乎透明。
N 个“摊位”互相兜底,整体可用性如何?一组公式:
好厨师不怕收摊,怕的是没有备选摊位。迁移方案的意义,就是把“单点赌运气”变成“多点拼概率”。
- 单池可用率 \\(a_i\\)第 \\(i\\) 个摊位这周“不被收鱼”的概率。
- 单池失效率 \\((1 - a_i)\\)这个摊位掉链子的概率。
- 全部失效率之积 \\(\displaystyle\prod_{i=1}^{n}(1 - a_i)\\)所有摊位同时掉链子的概率,冗余让这个值指数级缩小。
- 整体可用率 \\(A_{\text{total}}\\)用 1 减去“同时全挂”的概率,得到至少一个摊位活着的机会。
成本量化评估
收摊了,账本摆上桌。特价鱼便宜是事实,但今晚到底省了多少?不能只看单价:你付钱买回来的鱼,有一部分在被收走时扔掉了;冰箱的冷冻费、跑隔壁摊的交通费,都是隐性开销。算总账,才是 FinOps 精神。
对应到云账单:表面上 Spot 打了三折,但实际成本还要加上——中断回退浪费掉的算力费(已经付钱的运行时间)、检查点存储费、重试和迁移的额外资源费。业界经验是“5% 的中断率吃掉约 15% 的表面折扣”,务必用真实账单做对照实验,而不是盯着标价激动。
净节省的公式把每一笔都摆上台面:
省下的 vs 浪费的,账本说了算。成本量化的意义,就是让“好像省了”变成“确实省了”,并持续用数据驱动后续的出价和检查点调优。
- 按需总成本 \\(C_{\text{ondemand}}\\)全部用正价鱼办宴席的价钱,即全按需运行的账单。
- Spot 实际花费 \\(C_{\text{spot}}\\)特价鱼本身花掉的运行费用。
- 检查点开销 \\(C_{\text{ckpt}}\\)冰箱的电费和冷媒,即快照存储与读写费用。
- 重试开销 \\(C_{\text{retry}}\\)鱼被收走后补货带来的额外成本,即重投作业、迁移资源的花费。
- 净节省 \\(\text{NetSaving}\\)从按需账单里逐项扣掉以上开销,剩下的才是真省下的钱。
风险管控实践
再老练的厨师,也不敢把五十桌宴席全押在特价鱼上。稳妥的做法是:七成鱼买特价的,手里始终屯一条正价鱼在关键时刻救急。这就是混合实例池——大部分工作负载跑 Spot 享受折扣,关键的在线服务或最高优先级任务用按需实例兜底。
落地的工程实践包括:Kubernetes 节点池按 spot 与 on-demand 混合配置,配合 Pod 优先级与弹性伸缩;先在非核心业务灰度验证中断表现,再逐步扩大 Spot 占比;建立告警与审计,跟踪中断频率、节省率 SLO 与账单变化。这套方案已在多个大规模生产环境中验证,训练集群在 10% 以内中断率下稳定跑完数月。
混合池的期望成本,取决于“鱼被收走”的概率和恢复代价:
特价鱼为主、正价鱼兜底,这是所有大规模生产实践的共识。风险管控不是拒绝便宜,而是用机制把“可能被收走”变成“收走也没关系”。
- Spot 基础成本 \\(C_{\text{spot}}\\)大多数时间稳稳跑在特价算力上的运行费用。
- 中断概率 \\((1 - p_{\text{avail}})\\)一条鱼被摊主收走的概率,它是风险费的权重。
- 恢复成本 \\(C_{\text{recover}}\\)每次被收走后的善后开销(重试、迁移、补数据)。
- 期望成本 \\(\mathbb{E}[C]\\)把日常费用和风险费用加权相加,得到混合池的长期平均开销。
AI 应用场景
大模型预训练成本减半
阶段:模型训练成本优化
解决的问题:千亿参数大模型的预训练动辄烧掉数百万美元 GPU 账单。传统方式全部使用按需实例,成本线性增长;引入 Spot 实例后,以 70% 节点跑 Spot、30% 按需兜底的混合池,配合每 15 分钟一次的检查点,即使中断率在 5%-10%,整体账单仍可下降约 50%。
具体案例:某大模型团队用 PyTorch 分布式检查点加并行文件系统,将训练状态每 10 分钟落盘一次;Spot 节点被回收后,调度器在 3 分钟内于另一可用区拉起新节点,从最近检查点续训,最终把预训练成本从 120 万美元降到 62 万美元。
离线批处理推理管道
阶段:批处理推理与数据管道调度
解决的问题:图像批量处理、评测集推理、数据增强等离线任务吞吐波动大,对单任务实时性要求低。传统按需方案在高峰期成本飙升;Spot 实例天然适配“可重试、可排队”的批任务,中断只是让任务回到队列重新调度,成本直降 60% 以上。
具体案例:某 CV 团队每天处理 200 万张图片的质检流水线,使用托管批量服务搭配 Spot 节点池,任务失败自动重投;一个月后账单下降 63%,单张图片处理成本从 0.8 分钱降到 0.3 分钱。
弹性推理服务混合池
阶段:在线推理服务稳定性兜底
解决的问题:在线推理不能中断,但峰谷差异极大,全按需成本过高。通过“按需/预留实例撑基线 + Spot 扛突发”的混合架构,结合弹性伸缩与中断预测,既保证 P99 延迟稳定,又把峰值扩容成本削减一半。Spot 被回收时,流量自动摘除并转移至按需节点池。
具体案例:某企业知识库问答服务的夜间高峰流量是平日的 3 倍,SRE 团队让 80% 的扩容实例跑在 Spot 上,配合 2 分钟预告窗口优雅排空连接,两个月节省成本 47%,零请求中断。
Python 演示
import math def price_at(t): wave = 0.52 + 0.16 * math.sin(t * 0.31) wave += 0.10 * math.sin(t * 0.09 + 1.8) phase = t % 42 spike = 0.0 if phase > 38: spike = 0.55 * math.exp(-(phase - 40) ** 2 / 1.8) return min(wave + spike, 1.45) class SpotWorker: def __init__(self, bid, on_demand_price, ckpt_interval, ckpt_cost): self.bid = bid self.on_demand_price = on_demand_price self.ckpt_interval = ckpt_interval self.ckpt_cost = ckpt_cost self.progress = 0.0 self.last_ckpt = 0.0 self.ckpt_count = 0 self.interrupt_count = 0 self.run_cost = 0.0 self.running = False def tick(self, t, price): if price <= self.bid: if not self.running: self.running = True self.progress += 1.0 self.run_cost += price * self.on_demand_price if self.progress - self.last_ckpt >= self.ckpt_interval: self.last_ckpt = self.progress self.ckpt_count += 1 self.run_cost += self.ckpt_cost else: if self.running: self.running = False self.interrupt_count += 1 self.progress = self.last_ckpt def summary(self, target): done = self.progress >= target return { "done": done, "progress": int(self.progress), "cost": round(self.run_cost, 2), "interrupts": self.interrupt_count, "checkpoints": self.ckpt_count, } def main(): worker = SpotWorker(bid=0.72, on_demand_price=1.0, ckpt_interval=8, ckpt_cost=1.0) for t in range(500): worker.tick(t, price_at(t)) if worker.progress >= 100: break stats = worker.summary(target=100) on_demand_total = 100.0 * worker.on_demand_price print("stats:", stats) print("on-demand cost:", on_demand_total) print("spot net cost:", stats["cost"]) if stats["done"]: saved = (1 - stats["cost"] / on_demand_total) * 100 print("saved: %.1f%%" % saved) else: print("job not finished, need more retry") if __name__ == "__main__": main()
wave = 0.52 + 0.16 * math.sin(t * 0.31)用正弦波叠加模拟市场价格的自然波动if phase > 38: spike = ...周期性尖峰模拟“正价顾客来了”导致的价格暴涨if price <= self.bid:成交条件:市场价低于出价才能继续运行self.run_cost += price * self.on_demand_price只要在跑就要付钱,被回滚的进度同样计费if self.progress - self.last_ckpt >= self.ckpt_interval:每跑满一个检查点周期就“入冰箱”一次self.progress = self.last_ckpt中断时回退到最近检查点,这是损失的核心来源saved = (1 - stats["cost"] / on_demand_total) * 100用总账算实际节省率,而非表面折扣
常见误区
误区“Spot 经常被回收,那我把出价设成按需价的 2 倍,厂商优先保留我的实例,肯定稳了吧!”
正解摊主收鱼看的是“有没有正价顾客”,而不是谁出价高。Spot 的回收触发条件是市场价超过你的出价,而市场价很少超过按需价;出价超过按需价既不会提升容量优先级,也不会减少中断概率,唯一的效果是拉高你的账单。正确做法是出价设为略低于按需价,用检查点和冗余去对抗中断。
为何常见:把 Spot 竞价市场误当成“价高者得”的普通拍卖,忽略了它的本质是“正价优先”的特价尾货市场。
误区“标价一折,那我把训练集群全换成 Spot,账单直接砍掉九成!”
正解表面折扣不等于实际节省。中断回退会浪费已付费的算力,检查点存储、重试迁移都会产生额外开销;5% 的中断率可能吃掉 15% 的表面折扣。真实节省必须以账单为准,用本文的净节省公式逐项核算,而不是盯着单价兴奋。
为何常见:人天生容易被“折扣比例”锚定,忽略了总账里的隐性成本,这是 FinOps 新手最常见的思维陷阱。
误区“怕中断丢进度?那我每分钟存一次检查点,把保存周期拉到极限短!”
正解检查点本身要写存储、占带宽、拖慢训练;周期过短会让保存开销盖过中断损失,得不偿失。期望回退损失是周期的一半,合理做法是把周期设为中断恢复时间可接受的量级(通常 5-15 分钟),找准“损失”与“开销”的平衡点。
为何常见:只盯着“丢得少”这一侧,忽略了“存得贵”的另一侧,没有建立成本权衡意识。
误区“一听能被回收就吓得退避三舍,所有任务都跑按需,宁可多花一倍钱。”
正解Spot 的“不稳定”是概率性的,不是灾难性的。配上检查点、中断预测与跨区迁移,生产实践已经证明 5%-10% 的中断率下仍能省下 50% 以上的成本。真正该拒绝的不是 Spot,而是没有预案就裸奔的使用方式。
为何常见:把“可能被中断”误读为“频繁中断且无法恢复”,放大了风险而忽视了成熟的工程防护手段。
面试题
Q1. Spot 实例的核心特点是什么?
- A.价格与按需一致,无任何区别
- B.价格较低,但可能被回收← 正确答案
- C.价格较低,且永不被回收
- D.价格较高,但稳定可靠
答案:B。Spot 利用云厂商空闲算力大幅降价,代价是实例可能随时被回收,对应“特价鱼可能被正价顾客抢走”。A 否定了价格差,C 否定了回收机制,D 混淆了它与按需实例的定位。
Q2. 减少中断对训练任务影响的关键配套机制是?
- A.检查点与断点续训← 正确答案
- B.更高的公网带宽
- C.更大的本地临时盘
- D.固定的公网 IP 地址
答案:A。检查点把训练状态周期性地“塞进冰箱”,中断后从最近快照续跑,把损失压缩在半个保存周期内。带宽、临时盘和 IP 都不解决“进度丢失”这个根本问题。
Q3. Spot 实例出价上限的设置原则是?
- A.价格越高命中概率越大
- B.出价越低越能节省成本
- C.不超过按需价格即可← 正确答案
- D.与按需价格完全无关
答案:C。市场价很少超过按需价,出价上限设为略低于按需价即可稳定命中;出价超过按需价不会减少中断,只会多花钱。市场价高于出价才触发回收,所以太低会增加中断风险,太高则纯属浪费。
Q4. 收到中断预告后首先应该做的事是?
- A.立即终止进程并清理数据
- B.保存检查点并优雅退出← 正确答案
- C.继续训练直到强制停机
- D.调高出价等待价格回落
答案:B。预告窗口的意义就是把“被动强杀”变成“主动收尾”:第一时间保存检查点、优雅退出、通知调度器迁移。清理数据是自毁,硬扛和调价都改变不了回收事实,只会增加损失。
Q5. 检查点周期设置得过短的主要代价是?
- A.存储与 IO 开销显著增大← 正确答案
- B.训练精度不会发生变化
- C.中断次数不会明显减少
- D.出价命中率必然得到提高
答案:A。检查点要写持久存储、占用带宽并中断计算流,周期越短开销越大;而中断频率由市场供需决定,与检查点周期无关。这就是“期望回退损失 = 周期一半”背后的权衡:周期短损失小,但保存成本高。
Q6. 中断后续跑时,训练进度回退到哪里取决于?
- A.最近一次检查点的位置← 正确答案
- B.实例规格的大小
- C.数据集样本的多少
- D.出价上限的高低
答案:A。续跑永远从最近检查点开始,中断点与最近检查点之间的那部分进度被丢弃。实例规格、数据集规模和出价只影响运行速度、总时长或命中率,不改变回退基准点。
Q7. 混合部署 Spot 与按需实例池的主要目的是?
- A.同时兼顾成本节省与稳定性← 正确答案
- B.显著提升单卡计算性能
- C.简化业务代码编写逻辑
- D.提升集群网络传输带宽
答案:A。混合池让大部分任务享受 Spot 折扣,关键任务由按需兜底,在成本与风险之间取平衡。性能、代码复杂度、带宽都与“混合”的动机无关,属于干扰项。
Q8. 以下哪些场景适合优先使用 Spot 实例?
- A.带检查点的大模型训练← 正确答案
- B.可重试的离线批处理任务← 正确答案
- C.在线实时交易系统
- D.容错的数据管道作业← 正确答案
答案:ABD。能容忍中断、可重试、有检查点的任务都适合 Spot。在线实时交易对延迟和连续性要求极高,中断可能造成直接资损,属于必须用按需/预留的关键负载。比喻:流水席可以等,点菜单不能等。
Q9. 为什么实际节省率低于表面折扣率?
- A.检查点存储带来的开销← 正确答案
- B.中断重试产生的额外成本← 正确答案
- C.迁移与重新调度的开销← 正确答案
- D.按需价格本身也在上涨
答案:ABC。净节省必须从按需成本里扣除检查点、重试、迁移三类隐性开销,低估它们会让“感觉省了”变成“其实少省”。按需价格上涨影响的是对比基准,不属于 Spot 特有的隐性开销。
Q10. “主动让位”策略的核心思想是?
- A.预测中断前主动保存并退出← 正确答案
- B.等待实例被强制回收
- C.通过投诉避免中断发生
- D.出价翻倍阻止价格回收
答案:A。主动让位是在预告窗口内完成检查点与优雅退出,把“被动挨打”变成“可控收尾”,同时触发调度器在其他池续跑。强停、投诉或翻倍出价都无法改变回收事实,反而损失更大。