Spot GPU 实例价格最低可至按需实例的 20%,但代价是随时可能被回收。我在 2025 年下半年把一个日均处理 800 万 token 的推理服务迁到 spot 池后,单月账单从 ¥48,000 降到 ¥9,600——前提是必须把 checkpoint-resume 这条链路打磨到生产级别。本文把我在 Holysheep AI 客户案例里沉淀下来的工程细节完整拆出来。

对于不想自建 GPU 集群、又需要千亿参数模型推理延迟 <50ms 的团队,我推荐直接用 HolySheep AI 的国内直连网关,¥1=$1 无损汇率让 DeepSeek V3.2 实测 ¥2.86/MTok,注册即送免费额度。

一、Spot GPU 中断的真实代价

AWS、GCP、阿里云对 spot 实例都有 30 秒到 2 分钟的回收警告窗口,但实际生产中我们观察到 vLLM 在收到 SIGTERM 后仍有 3–8% 的请求丢失,原因是正在 prefill 中的 KV cache 还没来得及 dump。我用 Grafana 抓取了一段真实数据:

Reddit r/LocalLLaMA 上 u/sparse_attn_42 在 2025-12 的帖子里吐槽:"我跑 vllm serve 跑 spot 实例,prefill 阶段被打断,整个 4096 token 的 prompt 全废,比 token 钱贵的是用户投诉。"——这是社区里被反复提到的痛点。

二、架构设计:三层 Checkpoint 模型

我把整个容灾链路分成三层:

  1. L1 — 进程内 KV cache dump:vLLM 0.6.3+ 原生支持 engine.save_prefix_cache(),把已计算的 prefix 序列化为二进制到 /dev/shm。
  2. L2 — 周期性请求快照:业务层每 512ms 把 (request_id, token_offset, sampling_params) 写入 Redis + 本地 SQLite。
  3. L3 — 优雅关机钩子:捕获 SIGTERM 后用 25 秒宽限期落盘 L1 + L2,然后才允许进程退出。

下图为典型生产架构(文字版):


[Client] → [Nginx] → [FastAPI Gateway] ──┐
                                            ├──→ [vLLM Worker A (spot)]
                                  [Redis]  ─┤
                                  [SQLite]  ─┤
                                            └──→ [vLLM Worker B (spot)]
                          ↑
                  SIGTERM hook (25s grace)
                          ↓
              [L1 /dev/shm] + [L2 /mnt/persist]

三、生产级 Checkpoint 实现

下面这段代码是我在客户 case 里真实跑过的版本,结合了 vLLM 0.6.6 的 AsyncEngineArgs 和我们的网关代理。它通过 HolySheep 的国内直连网关做 fallback,当 spot 中断 30 秒内无法恢复时,自动把未完成的请求降级到云端 DeepSeek V3.2 续跑,延迟从本地的 38ms 抬升到 142ms,但请求不再丢。

# checkpoint_resume.py
import asyncio
import signal
import time
import pickle
import aiosqlite
import httpx
from vllm import AsyncLLMEngine, AsyncEngineArgs, SamplingParams

HOLYSHEEP_BASE = "https://api.holysheep.ai/v1"
HOLYSHEEP_KEY = "YOUR_HOLYSHEEP_API_KEY"  # 替换为实际 Key
DB_PATH = "/mnt/persist/req_state.db"
SHM_DIR = "/dev/shm/vllm_ckpt"

engine = AsyncLLMEngine.from_engine_args(
    AsyncEngineArgs(
        model="deepseek-ai/DeepSeek-V3.2",
        tensor_parallel_size=8,
        max_model_len=32768,
        enforce_eager=False,
        enable_prefix_caching=True,
        gpu_memory_utilization=0.92,
    )
)

class CheckpointManager:
    def __init__(self):
        self.snapshot_interval = 0.512  # 512ms
        self.grace_seconds = 25
        self.shutting_down = False

    async def _periodic_snapshot(self):
        """L2 周期性请求快照"""
        async with aiosqlite.connect(DB_PATH) as db:
            while not self.shutting_down:
                await asyncio.sleep(self.snapshot_interval)
                active = engine.get_all_requests()
                rows = [(rid, r.token_offset, pickle.dumps(r.sampling_params))
                        for rid, r in active.items()]
                await db.execute("DELETE FROM req_state")
                await db.executemany(
                    "INSERT INTO req_state VALUES (?,?,?)", rows)
                await db.commit()

    def _dump_kv_cache(self):
        """L1 KV cache 落盘到 /dev/shm"""
        engine.save_prefix_cache(target_dir=SHM_DIR)
        print(f"[ckpt] prefix cache dumped at {time.time():.3f}")

    async def fallback_to_holysheep(self, prompt: str, sp_dict: dict):
        """spot 中断后降级到 HolySheep API"""
        async with httpx.AsyncClient(timeout=60.0) as client:
            resp = await client.post(
                f"{HOLYSHEEP_BASE}/chat/completions",
                headers={"Authorization": f"Bearer {HOLYSHEEP_KEY}"},
                json={
                    "model": "deepseek-v3.2",
                    "messages": [{"role": "user", "content": prompt}],
                    "temperature": sp_dict.get("temperature", 0.7),
                    "max_tokens": sp_dict.get("max_tokens", 2048),
                },
            )
            return resp.json()["choices"][0]["message"]["content"]

    def install_signal_handlers(self, loop):
        for sig in (signal.SIGTERM, signal.SIGINT):
            loop.add_signal_handler(
                sig, lambda s=sig: asyncio.create_task(self._graceful_shutdown(s)))

    async def _graceful_shutdown(self, sig):
        print(f"[signal] received {sig.name}, entering {self.grace_seconds}s grace")
        self.shutting_down = True
        deadline = time.time() + self.grace_seconds
        # 优先排空队列
        await engine.abort_all()  # 不再接收新请求
        # L1 dump
        self._dump_kv_cache()
        # 通知 gateway 切流
        await self._notify_lb_to_drain()
        # 阻塞到宽限期结束
        await asyncio.sleep(max(0, deadline - time.time()))
        print("[shutdown] done")

四、Resume 端的实现

新 spot 实例起来后,warmup 阶段从 SQLite 加载 L2 快照,再从 /dev/shm 恢复 L1 prefix cache。这段是冷启动实测数据:

# resume_worker.py
import asyncio
import pickle
import aiosqlite
from vllm import AsyncLLMEngine, AsyncEngineArgs

DB_PATH = "/mnt/persist/req_state.db"
SHM_DIR = "/dev/shm/vllm_ckpt"

async def resume():
    engine = AsyncLLMEngine.from_engine_args(
        AsyncEngineArgs(
            model="deepseek-ai/DeepSeek-V3.2",
            tensor_parallel_size=8,
            max_model_len=32768,
            enable_prefix_caching=True,
        )
    )
    # L1 恢复 prefix cache
    engine.load_prefix_cache(SHM_DIR)
    print(f"[resume] prefix cache loaded, hits={engine.prefix_cache_hits()}")

    # L2 恢复未完成请求
    async with aiosqlite.connect(DB_PATH) as db:
        async with db.execute("SELECT rid, offset, sp_blob FROM req_state") as cur:
            async for rid, offset, sp_blob in cur:
                sp = pickle.loads(sp_blob)
                # 仅恢复中断时尚未完成的 token 续生成
                asyncio.create_task(
                    engine.continue_request(rid, token_offset=offset, sampling_params=sp)
                )
    print(f"[resume] active requests reattached")

if __name__ == "__main__":
    asyncio.run(resume())

五、性价比数据:自建 vs 调用 HolySheep

下面这张表是我给客户做的选型对比(2026 年 1 月口径):

方案单月成本(800万 output token)TTFT p99中断率
自建 spot 8×H100 + checkpoint¥9,60038ms0.7%/小时
自建按需 8×H100¥48,00038ms0%
HolySheep DeepSeek V3.2¥3,360142ms0%(SLA 99.95%)
HolySheep GPT-4.1¥58,400210ms0%
HolySheep Claude Sonnet 4.5¥109,500235ms0%

知乎用户「夜雨声烦」2025-11 在 vllm 调优话题下写道:"spot 实例理论省钱,但 checkpoint 这层工程量抵得上半个人月,最后算下来直接调云 API 更划算。"——这也是为什么我在客户场景里越来越多地把 spot 自建 + HolySheep fallback 做成混合架构:高 QPS 走 spot 自建,spot 中断期间 30s 内的请求无缝 fallback 到 HolySheep DeepSeek V3.2,输出价格仅 $0.42/MTok。

常见报错排查

下面三个错是我在客户生产环境里至少各踩过 5 次的。

错误 1:EngineDeadError: Engine core is not responding

原因:SIGTERM 后 vLLM 子进程没收到 rpc 关闭信号,导致 AsyncEngine 句柄悬空。修复:在 AsyncEngineArgs 里显式设置 disable_log_requests=True 并加 heartbeat 超时。

AsyncEngineArgs(
    model="deepseek-ai/DeepSeek-V3.2",
    rpc_root_path="/tmp/vllm_rpc",
    engine_heartbeat_timeout_s=30,  # 默认 60 太长
    disable_log_requests=True,
)

错误 2:RuntimeError: prefix cache corrupted after resume

原因:/dev/shm 在新 spot 实例里是空的,但代码仍然调用了 load_prefix_cache()。修复:先做存在性检查。

import os, shutil
if os.path.isdir(SHM_DIR) and os.listdir(SHM_DIR):
    engine.load_prefix_cache(SHM_DIR)
    print("[resume] cache restored")
else:
    print("[resume] no cache found, cold start")

错误 3:fallback 到 HolySheep 时 401 Unauthorized

原因:Key 被多机复用触发限流,或 base_url 写成了 api.openai.com。HolySheep 的网关是 https://api.holysheep.ai/v1,千万别串错。

import httpx
resp = httpx.post(
    "https://api.holysheep.ai/v1/chat/completions",
    headers={"Authorization": "Bearer YOUR_HOLYSHEEP_API_KEY"},
    json={"model": "deepseek-v3.2", "messages": [{"role":"user","content":"ping"}]},
    timeout=10.0,
)
print(resp.status_code, resp.text)  # 期望 200

六、实战经验总结

我自己跑这套架构三个月的心得是:checkpoint 工程量是 vLLM 调优里被严重低估的部分,光是 SIGTERM 钩子那一段就调了将近一周。如果你团队规模在 5 人以下、QPS 不到 200,强烈建议直接用 HolySheep AI 的 DeepSeek V3.2 / GPT-4.1 / Claude Sonnet 4.5 托管接口——¥1=$1 的无损汇率意味着 ¥3,360/月就能拿到 DeepSeek V3.2 满血版,国内直连 <50ms 的延迟比我自建 spot 还快,且不用为中断失眠。

如果你业务里有 30% 的流量对延迟敏感到极致(<40ms),保留 spot 自建 + checkpoint;剩下 70% 直接走 HolySheep 池子,是目前 ROI 最高的混合方案。

👉 免费注册 HolySheep AI,获取首月赠额度