Modal卷快照:模型加载与缓存恢复实践

围绕 Modal Volume 的提交、重新加载和模型启动加载,说明如何排查缓存未刷新、volume busy、文件可见但模型仍旧等问题,并区分 Volume 可见状态、vLLM 前缀缓存与容器内模型状态。

做模型部署时,模型文件与模型实例要分开处理:权重存入 Modal Volume,加载动作放进 @modal.enter;缓存未刷新时,先检查卷内变更是否已提交并重新加载,再检查进程是否仍持有旧模型。Modal 的 模型权重指南把 Volume 作为权重存储位置,并建议通过 @modal.enter 在新容器启动时加载;其 Volumes 指南说明,容器创建时挂载当时最新状态,之后的提交需要显式重新加载。本文所说的“卷快照”只按已说明的提交与可见性机制处理,不把它当作可任意回滚的模型备份。

模型部署前先准备模型文件与路径

开始前写清四项:完整权重文件及其目录结构;Volume 名称、挂载路径和远端子目录;模型加载器需要的配置;调用端点与请求格式。不要把本地路径直接当成容器路径,也不要只准备启动脚本而漏掉权重文件。

Modal 卷快照:提交、恢复与常见出错点

以下创建、提交、刷新和故障边界均按 Modal Volumes 指南整理。CLI 创建入口是 modal volume create;创建 v2 时加入 --version=2。截至 2026-10-01 核对的说明仍把 v2 标为 Beta,并不建议用于关键任务数据;v1 不能自动升级到 v2,数据需要复制到新的 v2 Volume。

已有本地权重时,官方给出的上传命令是:

modal volume put model-weights-vol path/to/model path/on/volume

它把本地 path/to/model 上传到指定 Volume 的 path/on/volume。也可以在代码中惰性创建:

vol = modal.Volume.from_name("my-volume", create_if_missing=True)

再把 Volume 挂载到函数的 /data:

@app.function(volumes={"/data": vol})

容器内的 Volume 修改需要提交后才能在当前容器之外可见。写入端使用提交操作,读取端关闭相关文件后刷新:

vol.commit()
vol.reload()

后台会在 Function 或 Sandbox 执行期间每隔数秒提交一次,容器关闭时还会执行最终快照和提交;但后台提交不会让其他已有容器自动刷新挂载视图。Volume 中存在未关闭文件时,.reload() 会因 volume busy 失败。

对于 Volumes v2,也可以在 Sandbox 或 modal shell 中进入挂载点后执行:

sync

并发修改同一个文件时采用 last-write-wins,Modal 不支持分布式文件锁。v1 提交小变更时,应避免超过 5 个并发提交,以免后写入的数据覆盖先前写入但尚未提交的内容。

存储用量按每天一次的总存储快照计算;删除数据后,最多仍可能为该存储计费 4 天。因此,删除文件不等于费用观察期立即结束。

模型加载:放进容器启动阶段

同一份 模型权重指南建议用 @modal.enter 在新容器启动时加载权重,而不是每次调用都重新加载。这里的“一次”是每个容器一次,并非整个应用全局只执行一次。带 @modal.enter 的方法不能接收动态参数;需要按参数选择模型时,应使用带 modal.parameter 的参数化 Cls。

Modal 的冷启动说明指出,容器只有在全部 enter 方法完成后才算 warm,也才会接收输入。把加载移入 enter 并不会消除这段延迟,只是把它移到预热阶段;在可行时,大文件读取应并发进行,而不是依次执行。

缓存恢复:区分卷视图与模型状态

完成提交和 .reload() 后,先确认文件视图已经更新。按上述生命周期设计,刷新 Volume 不会自动替换内存中已经构造的旧模型对象;如果容器仍使用旧权重,需要由初始化逻辑明确重新构造模型。

Volume 也不等于 Memory Snapshots。后者通过 enable_memory_snapshot=True 启用,并且只会为已部署的 App 创建,相关边界见 Memory Snapshots 文档。需要恢复进程状态时,不要把 Volume 的文件提交机制与这一功能混为一谈。

验证文件可见与 vLLM 响应

先在容器中检查实际挂载路径。将 YOUR_VALUE 替换为权重文件或目录的容器路径:

from pathlib import Path

model_path = Path("/data/YOUR_VALUE")
print(model_path.exists())

预期结果为 True,并确认模型加载器使用的是同一路径。这个检查只能证明路径可见,不能单独证明权重能够成功加载。

如果后端使用 vLLM,可按 vLLM Benchmark CLI 文档验证运行中的端点。ShareGPT 支持在线和离线基准测试,文档给出的下载与在线服务命令如下:

wget https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json
vllm bench serve \
  --backend vllm \
  --model NousResearch/Hermes-3-Llama-3.1-8B \
  --endpoint /v1/completions \
  --dataset-name sharegpt

报告中的 TTFT 从客户端发送请求计到收到首个流式输出;ITL 是连续流式输出之间的间隔,TPOT 则按请求计算并排除首 token。重复对同一服务器运行 vllm bench serve 可能复用前缀缓存中的提示词并抬高吞吐量,因此不能把重复结果直接归因于模型加载变化。

本文步骤按正文所链接的官方文档整理,未进行运行或测量;使用前应按当前版本复核。

资料来源