第一次在新机器上部署模型,我盯着下载进度条看了 40 分钟,进度条走到 12%,然后卡住不动了。
最后发现是超时重试在背后作怪:每传一段就断,断了之后从断点续传,但速度本身已经掉到接近零。折腾了一整个下午,才把权重完整拉下来。
这件事之后我整理了现在这套流程,按顺序走一遍基本不会再卡。
先确认你下的是权重,不是指针文件
这是最容易白忙一场的一步。
模型仓库里的大文件通常用 Git LFS 管理。直接 git clone 下来,你拿到的不是权重,是一个几百字节的文本指针:
version https://git-lfs.github.com/spec/v1
oid sha256:abc123...
size 15281129536
真正的 15GB 文件在 LFS 服务器上,需要额外的 LFS 客户端才会拉。而有些工具(包括一部分一键下载脚本)只做了普通 clone。
症状很典型:目录里有 model-00001-of-00004.safetensors 这个文件名,打开看却只有几百字节,加载时框架报各种看不懂的错——"unexpected size"、"header too large",或者干脆当成损坏文件。
检查方法很简单:
# 看文件真实大小
ls -lh model-00001-of-00004.safetensors
# 期望是十几 GB,看到几百字节就是指针
head -c 100 model-00001-of-00004.safetensors
先验证完整性,再开始下载。 这一步能省掉你后面所有的时间。
方法一:huggingface-cli / hf download
最直接的方式,速度取决于源站和你的出口带宽。
# 装 CLI
pip install -U "huggingface_hub[cli]"
# 下载指定模型的全部文件
hf download Qwen/Qwen2.5-7B-Instruct --local-dir ./models/qwen2.5-7b
# 只下配置文件和分词器,先验证路径通不通
hf download Qwen/Qwen2.5-7B-Instruct config.json tokenizer.json \
--local-dir ./models/probe
第二条命令是我现在每次都先跑的探针。下几个 KB 的文件只要几秒钟,如果这一步就失败,说明是网络或认证问题,没必要开始几十 GB 的下载。
开加速传输:
export HF_HUB_ENABLE_HF_TRANSFER=1
hf download Qwen/Qwen2.5-7B-Instruct --local-dir ./models/qwen2.5-7b
这个开关会启用多线程分块传输。但它不是万能的——在源站限速的环境里,多线程反而可能触发限流,表现是大量 429 或连接重置。这时候退回到单线程 + 断点续传反而更快。
方法二:断点续传
huggingface_hub 本身就支持断点续传,重复执行同一条命令会接着上次继续。中断之后直接重跑就行。
如果是从任意 URL 下,用 curl 或 wget:
# wget 的 -c 就是断点续传
wget -c -O model.safetensors "https://example.com/model.safetensors"
# curl 的 --continue-at -
curl -L -C - -o model.safetensors "https://example.com/model.safetensors"
有个坑:-C - 要求服务器支持 Range 请求。如果服务器返回的是 200 而不是 206,续传会失败,curl 会报 "Cannot resume"。这种情况下只能删掉重下。
另外,中断的原因比续传本身更重要。我那次卡住是因为单个文件传输到一定量后连接被切断,需要检查是不是:
- 中间有 NAT 或防火墙的空闲超时
- 源站对长连接有硬性限制
- 你自己的出口链路在丢包
# 测试长连接稳定性:下 100MB 看会不会中途断
curl -L --limit-rate 10M -o /dev/null -w "%{speed_download} %{size_download}\n" \
"https://example.com/big-file"
方法三:镜像站
很多模型托管平台在国内访问不稳定,官方也提供了镜像端点。
export HF_ENDPOINT=https://hf-mirror.com
hf download Qwen/Qwen2.5-7B-Instruct --local-dir ./models/qwen2.5-7b
镜像站要留意的点:
- 它可能不完整。有的镜像只同步了部分模型或部分版本,用之前先探针验证。
- 它可能滞后。新发布的模型镜像站要等一段时间才有。
- 它不是官方服务,稳定性和保留策略自己判断。
用镜像下载之后,校验哈希。这一步不能省——镜像站的完整性问题比源站多。
方法四:对象存储中转
这是我的最终方案,也是一次性的投入换来长期稳定的做法。
流程是:在自己可控的、离云主机近的对象存储里放一份权重,然后云主机从对象存储拉。
# 第一步:把权重传到对象存储(可以用 CLI 或 SDK)
# 第二步:生成一个有时效的签名 URL
# 第三步:在云主机上拉
wget -c -O model.safetensors "https://bucket.oss-cn-hangzhou.aliyuncs.com/model.safetensors?签名参数"
好处有三条:
- 同云厂商内网拉取可以走内网 endpoint,不走公网,速度和稳定性都上一个台阶。
- 不限速、不限源站策略,还能自己控速避免影响其他服务。
- 可以预热。运维流程里提前把新版本的权重传上去,机器开出来直接拉本地的。
需要注意的一点:内网 endpoint 通常要求同地域,跨地域还是走公网。而且对象存储和云主机要在同一个账号下、走 VPC 内网,这两点要一起满足。
拉完之后算个校验值存下来:
sha256sum model.safetensors > model.safetensors.sha256
# 下次用这个校验
sha256sum -c model.safetensors.sha256
方法五:直接打进镜像
适合部署固定的场景。把量化后的模型烤进镜像:
FROM vllm/vllm-openai:latest
COPY ./models/qwen2.5-7b-int4/ /models/qwen2.5-7b-int4/
ENV VLLM_MODEL_PATH=/models/qwen2.5-7b-int4
好处是彻底消灭"下载"这个环节——机器开出来镜像就带权重。但代价也明显:
- 镜像变成十几 GB,推送和拉取都慢
- 每个节点都要存一份,节点池大的时候存储成本上来了
- 镜像仓库有容量和带宽配额
我只在模型固定且节点池稳定的场景这么做。如果模型要频繁更新,这条路维护起来很痛苦。
缓存目录一定要挂持久盘
这个坑我踩过两次,每次都因为机器重启。
很多框架默认把权重缓存在容器内的某个目录,比如 ~/.cache/huggingface。容器重建、机器重启、编排滚动更新——缓存就没了,下次启动又是几十 GB 的下载。
# 显式指定缓存目录
export HF_HOME=/data/models/hf
export HF_HUB_CACHE=/data/models/hf/hub
export TRANSFORMERS_CACHE=/data/models/hf/transformers
# 或者用 huggingface_hub 的 API
python -c "
from huggingface_hub import snapshot_download
snapshot_download(
'Qwen/Qwen2.5-7B-Instruct',
local_dir='/data/models/qwen2.5-7b',
cache_dir='/data/models/hf/hub',
)
"
/data 要挂在持久卷上,不是容器可写层。这件事值得专门检查一遍:
docker inspect <容器> | grep -A 5 Mounts
落到一起的下载流程
我现在实际执行的顺序:
#!/usr/bin/env bash
set -euo pipefail
MODEL_ID="Qwen/Qwen2.5-7B-Instruct"
DEST="/data/models/qwen2.5-7b"
# 1. 探针:先确认网络和路径通
echo "== 探针 =="
HF_ENDPOINT="${HF_ENDPOINT:-}" hf download "$MODEL_ID" config.json --local-dir "$DEST.probe"
rm -rf "$DEST.probe"
# 2. 主下载(可断点续传,重复执行安全)
echo "== 下载 =="
HF_HUB_ENABLE_HF_TRANSFER=1 hf download "$MODEL_ID" --local-dir "$DEST"
# 3. 校验文件大小,看有没有下成指针
echo "== 校验 =="
find "$DEST" -name "*.safetensors" -exec ls -lh {} \;
find "$DEST" -name "*.safetensors" -size -10M -print | \
{ grep . && echo "有文件小于 10MB,很可能是指针文件" && exit 1; } || true
# 4. 清理临时目录
rm -rf "$DEST/.cache"
第 3 步那个 -size -10M 的检查是我加的自动化,原理就是前面说的"先确认下的是权重"。
收尾
权重下载这件事本身没什么技术含量,但它是整条部署链路里最不可控的一环——因为它依赖一个你控制不了的源站。
所以我现在的原则是:把下载从部署流程里移出去。下载是一次性的、可缓存的、可预热的事,不该出现在"机器开出来 → 服务就绪"这条关键路径上。放进镜像、放进对象存储、放进持久卷缓存,都是同一个思路。
至于各家的对象存储费用、镜像仓库配额、限速策略这些,各家差异很大而且经常调整,文章里说的都是做法不是报价,实际以官方页面和文档为准。