存储与模型加载
大模型部署里,存储通常不参与每个 token 的核心计算,但它会决定模型能不能快速启动、能不能稳定扩容、能不能在多副本服务中顺利分发。
可以先用一句话理解:
存储负责把模型权重可靠、快速、可重复地送到推理进程和 GPU 前面。
如果存储链路设计不好,GPU 可能很强,但服务仍然会出现冷启动慢、滚动发布拖很久、多个副本同时启动把网络盘打满、缓存目录膨胀、容器镜像过大等问题。
1. 存储在模型服务中的作用
模型服务中的存储不只是“放文件”的地方。它通常参与这些环节:
- 下载模型权重、tokenizer、配置文件和量化文件。
- 保存 Hugging Face、ModelScope、vLLM、SGLang 等框架的本地缓存。
- 在服务启动时读取 checkpoint 或 safetensors 分片。
- 给多副本服务分发同一份模型。
- 支撑热更新、版本回滚和灰度发布。
- 保存日志、trace、请求样本和评测输出。
- 在训练或微调场景中保存 checkpoint、optimizer state 和数据集。
对于在线推理来说,存储最直接影响的是启动和发布阶段,而不是稳定运行后的每 token 解码速度。模型一旦加载到显存或内存中,普通请求通常不会反复读完整权重文件。
但这不代表存储不重要。生产环境里经常真正卡住的是这些问题:
- 新实例拉起太慢,扩容跟不上流量。
- 发布时大量副本同时下载模型,打满对象存储、网络盘或出口带宽。
- 缓存目录被多个版本模型撑满,节点磁盘耗尽。
- 网络盘抖动导致加载失败或启动时间不稳定。
- 容器镜像过大,镜像分发慢于模型本身加载。
2. HDD、SATA SSD 和 NVMe SSD
不同存储介质的差异主要体现在顺序读取、随机读取、延迟和并发能力上。
| 类型 | 特点 | 适合场景 | 不适合场景 |
|---|---|---|---|
| HDD | 容量大、成本低、随机访问慢 | 冷归档、离线备份、低频数据集 | 在线模型启动、高并发加载 |
| SATA SSD | 比 HDD 快,成本适中,带宽有限 | 中小模型、本地开发、普通缓存盘 | 多个大模型同时加载 |
| NVMe SSD | 带宽高、延迟低、并发能力强 | 生产推理、本地模型缓存、多副本节点 | 成本敏感的大规模冷存储 |
| 网络盘 | 便于共享和集中管理,性能依赖网络和服务端 | 集群共享、统一模型仓库 | 强冷启动 SLA、突发多副本同时加载 |
| 对象存储 | 容量弹性好,适合版本管理和分发源 | 模型制品源、归档、跨区域分发 | 直接作为高频启动读盘路径 |
大模型权重通常是几十 GB 到数百 GB。加载时多为大文件顺序读取,但也会有多个分片并发读取、metadata 查询、tokenizer 小文件读取和框架缓存检查。
因此,生产节点上更常见的方案是:
对象存储 / 模型仓库
-> 节点本地 NVMe 缓存
-> 推理进程加载到系统内存
-> 拷贝或映射到 GPU 显存
本地 NVMe 的价值不只是“更快”,还包括减少对远端仓库的重复读取,让服务重启和滚动发布更稳定。
3. 模型权重体积怎么估算
模型文件大小主要由参数量和精度决定。粗略估算方式是:
权重大小 ≈ 参数量 × 每个参数占用字节数
| 精度 | 字节/参数 | 7B 约占用 | 70B 约占用 |
|---|---|---|---|
| FP32 | 4 | 28 GB | 280 GB |
| FP16 / BF16 | 2 | 14 GB | 140 GB |
| INT8 / FP8 | 1 | 7 GB | 70 GB |
| INT4 / 4-bit | 0.5 | 3.5 GB | 35 GB |
实际磁盘占用还会包括:
config.json、generation_config.json等配置文件。- tokenizer 文件,例如
tokenizer.json、tokenizer.model、vocab.json。 - safetensors 分片索引,例如
model.safetensors.index.json。 - 量化元数据、adapter 权重或 LoRA 权重。
- 多版本缓存、临时下载文件和未清理的旧 revision。
需要注意,磁盘权重大小不等于显存占用。显存还要计算 KV Cache、runtime、workspace 和并发余量,见 显存 和 模型占用显存估算。
4. 模型加载链路
一次典型的模型启动可以拆成几步:
检查本地缓存
-> 下载缺失文件
-> 读取 config 和 tokenizer
-> 读取权重分片
-> 反序列化 / mmap
-> 按 device map 或并行策略放置权重
-> 初始化推理框架 runtime
-> 预热请求或 CUDA graph
每一步都可能成为瓶颈。
| 阶段 | 常见瓶颈 | 表现 |
|---|---|---|
| 下载 | 外网带宽、对象存储限流、代理不稳定 | 首次启动极慢或失败 |
| 缓存检查 | 小文件多、网络盘 metadata 慢 | 启动前长时间无 GPU 占用 |
| 权重读取 | 磁盘带宽不足、多副本争抢 | 读盘打满,CPU 和 GPU 等待 |
| 反序列化 | CPU、系统内存不足 | 内存峰值高,甚至 OOM |
| 显存放置 | PCIe、GPU 拓扑、并行切分 | 多卡加载慢或某些卡等待 |
| runtime 初始化 | CUDA、NCCL、kernel 编译或缓存 | 首次请求慢,预热时间长 |
排查时不要只看 nvidia-smi。如果 GPU 长时间空闲,可能不是 GPU 慢,而是权重还在下载、读盘、解压、反序列化或等待文件锁。
5. safetensors 和分片加载
现在很多模型使用 safetensors 格式,而不是传统 PyTorch .bin 文件。
safetensors 的优势包括:
- 文件格式更简单,避免任意代码执行风险。
- 支持按张量读取,适合分片和懒加载。
- 可以配合 mmap 减少不必要的数据拷贝。
- 大模型通常按多个
model-00001-of-000xx.safetensors分片保存。
常见文件结构类似:
model.safetensors.index.json
model-00001-of-00008.safetensors
model-00002-of-00008.safetensors
...
tokenizer.json
config.json
分片不代表一定更快。它的好处是便于管理超大模型、并行加载和按设备切分,但实际速度仍取决于:
- 本地磁盘吞吐。
- 并发读取数量。
- 文件系统和网络盘对并发小文件 / 大文件的支持。
- CPU 反序列化和内存带宽。
- 推理框架是否真正并行加载。
如果多个进程同时加载同一模型,分片并发读取可能把本地盘或网络盘打满。生产环境里通常要限制同时冷启动的副本数,或提前把模型预热到节点缓存中。
6. Hugging Face cache 和缓存目录
Hugging Face Transformers、Hub、Datasets 等工具默认会把模型下载到用户目录下的缓存中。常见环境变量包括:
| 环境变量 | 作用 |
|---|---|
HF_HOME | Hugging Face 总缓存根目录 |
HF_HUB_CACHE | Hub 模型和数据文件缓存目录 |
TRANSFORMERS_CACHE | Transformers 旧版常见缓存变量 |
HF_DATASETS_CACHE | datasets 数据缓存目录 |
TORCH_HOME | PyTorch 相关缓存目录 |
生产环境建议显式设置缓存路径,例如放到本地 NVMe:
export HF_HOME=/data/cache/huggingface
export HF_HUB_CACHE=/data/cache/huggingface/hub
这样做有几个好处:
- 避免缓存落到系统盘,把
/撑满。 - 方便监控模型缓存目录大小。
- 多个服务可以约定统一缓存位置。
- 节点重启后仍可复用已下载模型。
需要同时注意权限和并发问题。多个容器或多个用户共享同一缓存目录时,可能出现文件锁等待、权限不一致、半下载文件残留等问题。生产中更稳妥的做法是让模型制品在发布前完成下载和校验,服务启动时只读本地缓存。
7. 网络盘、本地盘和对象存储怎么选
模型文件可以放在本地盘、网络盘、对象存储或镜像里。不同方案没有绝对优劣,关键看启动 SLA、规模和运维方式。
| 方案 | 优点 | 风险 |
|---|---|---|
| 本地 NVMe | 启动快,抖动小,不依赖远端实时读取 | 每个节点都要同步模型,占用本地容量 |
| 网络盘 | 多节点共享,管理简单 | metadata 和吞吐可能成为瓶颈,故障影响面大 |
| 对象存储 | 适合版本化、归档和跨集群分发 | 首次下载慢,受限于网络、限流和代理 |
| 容器镜像内置模型 | 部署制品自包含,离线环境方便 | 镜像巨大,构建和分发慢,版本更新笨重 |
| init container 预拉模型 | 启动流程清晰,模型和服务镜像解耦 | 首次启动仍依赖下载链路 |
常见生产推荐是把对象存储或模型仓库作为源,把本地 NVMe 作为热缓存:
远端模型仓库负责版本和审计
本地 NVMe 负责快速启动
服务进程只从本地路径加载
这样可以同时兼顾版本管理和启动性能。
8. 多副本服务的模型分发
多副本部署时,最容易忽略的是“同时启动”的冲击。
例如一个 80 GB 模型,如果 20 个副本同时从同一个远端仓库下载,就会产生 1.6 TB 的短时间读取和网络传输。即使单个副本加载速度可以接受,集群级别也可能把对象存储、网络盘、NAT 网关或节点磁盘打爆。
常见优化方式:
- 分批滚动发布,限制同时冷启动的副本数。
- 在节点准备阶段预拉模型,而不是让业务进程启动时下载。
- 使用本地镜像仓库或模型缓存代理。
- 在同一节点上复用本地模型目录。
- 对模型文件做 checksum 校验,避免重复下载损坏文件。
- 保留最近几个稳定版本,便于快速回滚。
发布系统要把“模型下载完成”和“服务 readiness”分开看。服务端口打开不代表模型已加载完成;模型文件在本地也不代表 GPU runtime 已经预热完成。
9. 容器镜像是否应该打包模型
是否把模型直接放进容器镜像,取决于环境。
适合打包进镜像的情况:
- 模型较小。
- 离线或内网环境无法稳定访问模型仓库。
- 发布频率低,镜像分发系统足够快。
- 强调制品自包含和可复现。
不适合打包进镜像的情况:
- 模型几十 GB 到数百 GB。
- 模型更新频繁。
- 多个服务共享同一模型。
- 镜像仓库、节点拉取和 CI 构建速度已经是瓶颈。
- 需要灰度多个模型版本或快速回滚。
大模型在线服务里,更常见的做法是:
服务镜像:只包含代码、依赖和启动脚本
模型制品:放在模型仓库、对象存储或节点缓存
部署配置:指定 model path / revision / checksum
这样模型版本和服务代码版本可以独立演进,镜像也不会因为模型变更反复构建。
10. 热加载、冷启动和预热
冷启动指实例从没有模型可用,到完成模型加载并能接收请求。它通常包括:
- 拉取镜像。
- 下载或挂载模型文件。
- 加载权重到内存 / 显存。
- 初始化 CUDA、NCCL 和推理框架。
- 执行预热请求。
- 通过 readiness 检查。
热加载通常指进程已运行,切换或新增模型版本。它的难点是资源峰值:
- 新旧模型可能短时间同时占用磁盘、内存和显存。
- 多模型服务需要处理请求路由和版本隔离。
- 加载失败时要能回滚到旧模型。
- tokenizer、prompt 模板和 generation config 也要随模型版本一起切换。
生产环境里通常要把预热作为正式启动的一部分。预热可以提前触发 CUDA context、kernel 编译、显存池初始化、KV Cache 管理器初始化和首个 batch 调度,避免第一个真实用户请求承担全部成本。
11. 常见问题排查
模型首次启动特别慢
可能原因:
- 模型正在从远端下载,而不是读本地缓存。
- 缓存目录在系统盘或慢盘上。
- 网络代理、对象存储或模型仓库限流。
- 容器每次重建后缓存丢失。
- 多个副本同时下载同一模型。
处理方向:
- 固定
HF_HOME或模型缓存目录到本地 NVMe。 - 在部署前预拉模型并校验 checksum。
- 分批扩容,避免同时冷启动。
- 监控下载耗时、读盘吞吐和缓存命中率。
模型文件已经在本地,但加载仍然很慢
可能原因:
- 本地盘是 HDD 或低速网络盘。
- safetensors 分片很多,metadata 或并发读取慢。
- CPU 反序列化、内存带宽或 PCIe 传输成为瓶颈。
- 推理框架初始化或 kernel 预热耗时。
- 多个进程同时加载同一份权重。
处理方向:
- 区分下载耗时、读盘耗时、反序列化耗时和显存放置耗时。
- 使用本地 NVMe。
- 限制并发启动数量。
- 给系统内存留出加载峰值冗余。
- 增加明确的启动阶段日志。
缓存目录越来越大
可能原因:
- 多个模型 revision 长期保留。
- 量化版本、LoRA 版本和原始版本同时存在。
- 临时下载文件或失败残留没有清理。
- 多个框架各自维护缓存目录。
处理方向:
- 统一缓存根目录。
- 定期清理不再使用的模型版本。
- 保留可回滚版本,不保留无限历史。
- 对缓存目录设置容量监控和告警。
网络盘偶发加载失败
可能原因:
- 网络盘吞吐或 metadata 服务抖动。
- 多副本同时启动造成读放大。
- 文件锁、权限或挂载参数不一致。
- 网络盘短暂不可用导致读取中断。
处理方向:
- 生产推理优先从本地缓存加载。
- 网络盘作为分发源或共享源,而不是每次启动的直接热路径。
- 对模型文件做完整性校验。
- readiness 检查必须覆盖模型加载结果。
12. 选型建议
不同场景的存储策略可以粗略这样选:
| 场景 | 建议 |
|---|---|
| 个人学习 | SATA SSD 或 NVMe,显式设置模型缓存目录 |
| 单机推理服务 | 本地 NVMe 保存模型,避免每次启动重新下载 |
| 多副本在线服务 | 远端模型仓库 + 节点本地缓存 + 分批滚动发布 |
| Kubernetes 部署 | init container 预拉模型,业务容器只读本地挂载 |
| 离线内网部署 | 可以考虑镜像内置小模型,大模型更适合独立制品分发 |
| 训练 / 微调 | 高吞吐共享存储或并行文件系统,并规划 checkpoint 容量 |
一个实用原则是:
模型版本用远端仓库管理,模型启动用本地高速盘承载。
13. 总结
存储不会直接替代 GPU 算力,但会决定模型服务能不能快速、稳定、可重复地启动。
判断存储方案时,可以按这几个问题检查:
- 模型首次下载和二次启动分别需要多久。
- 模型缓存是否落在可控、可监控、容量足够的目录。
- 多副本同时启动时,远端仓库、网络和本地盘是否会被打满。
- 容器镜像和模型制品是否解耦。
- 模型版本、checksum、回滚和缓存清理是否有明确机制。
- readiness 是否真正代表模型加载和预热完成。
真正可靠的部署,不是让每个实例临时去网上找模型,而是让模型分发、缓存、加载和预热都成为发布流程中的显式步骤。