1. 先说清楚:为什么要把“下载”这件事拆成“本地 + 远程”两步
前阵子帮某实验室A同学部署一个推理服务,远程服务器在美国某云厂商的机房里,系统是干净的无图形化Ubuntu。模型用的是某个几十GB的开源权重,我必须把文件从HuggingFace拉到那台远程服务器上。一开始A同学的想法很简单:直接在远程服务器上跑huggingface-cli download。结果一试就傻眼——要么连接超时,要么下载速度只有几十KB/s,更惨的是下到一半直接断掉,没有断点续传,得从头再来。
后来我改用"本地电脑连huggingface镜像站下载文件,再通过rsync传到远程服务器"的思路,整个流程顺畅很多。这也正是这个标题想解决的问题:如果你本地能访问HuggingFace镜像站,而远程服务器访问HuggingFace很慢甚至根本不通,怎么把模型文件准确、完整地搬运过去?
这里有两个前提需要先明确:
- 你有本地操作环境,可以是自己的笔记本、公司电脑,只要能访问镜像站即可。
- 远程服务器允许你通过SSH登录,并能通过
scp、rsync或 SFTP 接收文件。
这两个条件满足一个,剩下的事情就是组合命令。但如果远程服务器网络状况一般,直接在上面下载也不是完全不行——我会在第二节里专门讲远程直连镜像站的配置方法,这个方案在终端玩家中非常常用。
顺便说一句,HuggingFace镜像站本质上是把 huggingface.co 上的 repo 内容做了缓存加速。它保持和官方相同的API结构,所以huggingface_hub这个SDK只要换一个HF_ENDPOINT环境变量,就能无缝切换到镜像源。这就意味着,你不需要修改任何业务代码,只要改变环境变量就能走镜像。下面我把两条路径都拆开讲。
2. 远程服务器直连镜像站:改环境变量,然后正常跑 huggingface-cli
如果你不介意让远程服务器自己下载,那是第一步尝试。原理很简单:HuggingFace官方SDK读取HF_ENDPOINT这个环境变量,把它指向镜像站的地址,所有请求都会打到镜像站上。很多教程只告诉你设置环境变量,但没说清楚设置后发生了什么,这里稍微展开一下。
2.1 设置环境变量,并用一条命令验证连通性
在远程服务器上执行:
export HF_ENDPOINT=https://hf-mirror.com这会作用于当前shell会话。如果你想永久生效,写入配置文件即可:
echo 'export HF_ENDPOINT=https://hf-mirror.com' >> ~/.bashrc source ~/.bashrc然后验证一下SDK是否真的走了镜像:
huggingface-cli version如果输出正常,直接尝试下载一个小模型:
huggingface-cli download --resume-download google-bert/bert-base-uncased --local-dir ./bert-base这里--local-dir和--resume-download是现在推荐的标准写法。官方旧文档里常出现--cache-dir,它的行为是把下载内容存放在HuggingFace风格的缓存目录里,层级比较深,找文件要扒半天。而--local-dir会直接把文件铺到指定目录,所见即所得,适合部署场景。
注意,这里下载的不是单个文件,而是整个repo的snapshot。如果大模型有几个GB,控制台会显示进度条。实测下来,远程服务器走镜像的速度通常比直连HuggingFace快不少,但具体速度取决于服务器所在机房到镜像站节点的线路质量,不是所有地方都能跑满带宽。
2.2 为什么推荐用snapshot_download而不是直接wget
很多人觉得既然是下载文件,用wget https://hf-mirror.com/...不就行了吗?理论上可以,但问题在于大模型repo通常不止一个权重文件,而是包含config.json、tokenizer.json、多个bin/safetensors分片,文件名还可能有特殊字符。手动拼URL后,你会发现每次只能手动拉单文件,太容易漏文件。
更稳的是用Python SDK:
from huggingface_hub import snapshot_download model_dir = snapshot_download( repo_id="meta-llama/Llama-2-7b-chat-hf", local_dir="/data/models/llama2-7b-chat", max_workers=8 ) print(model_dir)在调用前,同样需要设置HF_ENDPOINT环境变量,或者用代码指定:
import os os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"2.3 参数细节:断点续传、并发、忽略指定文件
实际操作中,一定要关注这几个参数:
| 参数 | 作用 | 经验值 |
|---|---|---|
--resume-download | 断点续传,下载中断后继续 | 默认应开启 |
max_workers | 并发下载文件数 | 4~8,别过大,容易触发限流 |
--local-dir | 下载到固定目录 | 部署时优先用 |
--ignore-patterns | 跳过某些文件 | 例如跳过tf模型、onnx等不需要的文件 |
例如,只需要safetensors格式和必要配置,不需要.bin和.onnx,可以这样:
huggingface-cli download --resume-download --local-dir ./model organization/model --ignore-patterns "*.bin" "*.onnx" "*.h5"这里用引号包裹通配符很重要,否则shell会帮你展开,可能匹配不到。
另外,远程服务器上直接跑下载前,建议先检查磁盘空间。一个70B模型的safetensors分片可能有十几个,总共130多GB。用df -h先看剩余空间,免得下载到一半磁盘写满。写满后虽然能续传,但后续任务会非常被动。
3. 本地从镜像站下载,再rsync上传到远程服务器
远程直连镜像虽然方便,但不是所有服务器都能访问镜像站。比如你的远程服务器在一个隔离内网里,或者网络策略只允许HTTP代理访问白名单域名,这时候最好的办法就是在自己电脑上把文件准备好,再通过SSH传上去。
这也是题目强调的"本地使用镜像站下载文件到远程服务器"。整个过程拆三步:本地下载、本地与远程之间的传输、远程端校验。
3.1 本地从镜像站拿到一份完整的模型文件清单
如果你要下载的是一个完整的repo,直接在本地执行:
export HF_ENDPOINT=https://hf-mirror.com huggingface-cli download --resume-download --local-dir ./llama-2-7b meta-llama/Llama-2-7b-chat-hf --max-workers 4这样下载完后,./llama-2-7b目录下就是完整的快照。它包含.cache子目录,里面是类似huggingface-metadata的校验信息,传完整目录没问题。
但有时你只需要几个大文件,比如某个repo里有config、tokenizer和models,而你只需要models。这时可以用不带缓存的单文件下载:
huggingface-cli download meta-llama/Llama-2-7b-chat-hf config.json --local-dir ./model注意,这个命令在旧版本中可能会创建./model/.cache/huggingface目录,因为需要记录blobs。版本建议至少huggingface_hub>=0.23.0,新版本--local-dir逻辑更干净。如果不确定版本,升级一下:
pip install -U huggingface_hub3.2 rsync传输:断点续传和完整性校验同时搞定
本地文件准备好后,上传远程。我强烈建议用rsync而不是scp,原因有三:
- rsync支持断点续传,传输中断后重启会自动接着传。
- rsync会对比文件大小和时间戳,已经传完的不会重复传。
- rsync支持
-P参数,等同于--partial --progress,传输中能看到实时进度。
一条我常用的命令:
rsync -avP --partial-dir=.rsync-tmp ./llama-2-7b/ user@remote-server:/data/models/llama-2-7b/-a归档模式保留权限和时间戳,-v输出详细信息,-P显示进度并允许续传,--partial-dir=.rsync-tmp的意思是每个文件先传到临时目录,传完再移动到目标。这点很重要:如果传输中断,临时目录里的文件不会污染正式目录,而最终文件只有完整传输才会出现在目标位置。
如果你怕传输过程中SSH断开,建议用tmux或screen包一层:
tmux new -s sync rsync -avP ...然后可以Ctrl+B,D脱离会话。等它慢慢传。第二天回来重新附着:
tmux attach -t sync如果传输已经完成,rsync会显示 "sent X bytes",不会重新传。
3.3 没装Python环境的本地机器:用wget/curl拉镜像直链
有的场景很极端——本地电脑不能装Python,或者仅仅临时用一下。这时候直接拼静态链接也可以。镜像站的文件直链格式和官方一致:
https://hf-mirror.com/{repo_id}/resolve/main/{file_path}例如下载bert-base-uncased的config.json:
wget https://hf-mirror.com/bert-base-uncased/resolve/main/config.json下载一个safetensors分片:
wget https://hf-mirror.com/bert-base-uncased/resolve/main/model.safetensors如果有多个文件,先通过镜像站API拿到文件列表:
curl -s https://hf-mirror.com/api/models/bert-base-uncased | python3 -c " import json, sys data = json.load(sys.stdin) for s in data.get('siblings', []): print(s['rfilename']) "注意,siblings返回的是相对文件名,比如model.safetensors、config.json,可以循环拼接直链。这个做法被很多上下文受限/内网部署场景采用,因为它不依赖HuggingFace SDK,只需HTTP工具。
3.4 在远程服务器上验证下载的模型没有损坏
传到远程服务器后,最怕的是在传输过程中文件损坏。HuggingFace的repo文件上传时计算了SHA256,镜像站也保留了校验信息。你可以从镜像站API拿到每个文件的SHA256:
curl -s https://hf-mirror.com/api/models/bert-base-uncached | python3 -c " import json, sys data = json.load(sys.stdin) for sib in data.get('siblings', []): print(sib['rfilename']) "如果要拿SHA值,通常需要在文件直链末尾加?download=true,然后通过下载头信息获取。不过更省事的做法是:本地下载完模型后,用sha256sum生成所有文件的哈希清单,上传远程后再比对一次。因为本地来自镜像站原始缓存,这个哈希可以当作基准。
# 本地生成校验清单 find ./llama-2-7b -type f -exec sha256sum {} \; > checksums.txt # 上传ecs后,在远程服务器上执行验证 cd /data/models/llama-2-7b sha256sum -c checksums.txt我的经验是:一次几十GB的传输完成后,总会有一两个文件因为网络波动出现损坏。所以校验这一步不能省。
4. 实操中我踩过的坑和完整排查链路
4.1 443超时和SSL证书错误,根本不是同一个问题
第一次在远程服务器上运行时,A同学跟我说"huggingface-cli下载超时"。我上去一看,报错内容其实是Connection to huggingface.co timed out。这种问题通常不是DNS挂掉,而是服务器所在机房的出网线路到HuggingFace的TCP包被丢弃。
此时如果把HF_ENDPOINT指向镜像站,大部分超时问题会消失。但镜像站也有自己的网络节点,如果你的服务器所在区域恰好和镜像节点线路连接很差,还是会超时。
如果设置完HF_ENDPOINT后发现报错变成SSL: CERTIFICATE_VERIFY_FAILED,那说明镜像站的证书链在你服务器上没有正确加载。排查方式:
curl https://hf-mirror.com/api/models/bert-base-uncased -v观察输出中的SSL certificate verify ok。如果提示证书错误,可能是系统时间不对,或者CA证书库版本太旧:
sudo apt update && sudo apt install -y ca-certificates4.2 使用--local-dir时目录结构里多出.cache,不是bug
很多人在下载完发现local-dir下有一个隐藏的.cache文件夹,里面是.locks和git-blob之类的临时记录。第一反应是想删掉它。
不要急着删。这个缓存作用是维护本地目录中每个文件的来源和commit version。删除后不会立刻影响模型文件,但后续如果你想清理HuggingFace缓存重新下载,或者需要知道当前文件的版本,就会丢信息。
真正删缓存应该使用官方命令:
huggingface-cli scan-cache huggingface-cli delete-cache它会列出缓存目录下所有已下载的repo,按大小排序,你可以交互式选择删除。这种方法比手动rm更安全,因为会同步处理symlink和blob文件。
4.3 远程传输中断后,sha256对不上的一个隐蔽原因
用rsync传输时,如果目标文件已经存在但大小一致,rsync默认会比较时间戳。假设远程服务器上已经有一个从别处复制的同名文件,恰好大小一样但内容不同,rsync就可能跳过它,导致最终校验失败。
解决方法是加-c参数:
rsync -avPc ./llama-2-7b/ user@remote-server:/data/models/llama-2-7b/-c会强制用校验和来判断文件是否需要更新,而不仅看大小和时间。代价是计算量大一点,但大文件传输值得。第一次传完后,如果中途中断过,后续重跑rsync,我建议不要加-c,因为它会全量重新校验所有文件,很耗时;用默认的时间戳+大小逻辑即可,如果担心,可以在传输完成后单独用sha256核对。
4.4 远程服务器磁盘空间不够,但下载已经开始了
如果在huggingface-cli下载过程中提示No space left on device,进度条会停住,且已经写入的部分文件占满磁盘。此时即使加上--resume-download,也需要先清理空间才能继续。
我的处理方式分三步:
- 先用
du -sh看哪个目录占用大,必要时删掉不需要的旧模型。 - 如果远程服务器有外挂数据盘,把下载目录改到数据盘。
- 设置
HF_HOME或HF_HUB_CACHE指向空间充裕的路径:
export HF_HOME=/data/hf mkdir -p /data/hf然后再跑下载命令。这种方法特别适合那种系统盘只有40GB的轻量服务器。
5. 几条可以直接照抄的经验建议
5.1 本地缓存公共模型,避免反复下载
开发阶段建议在本地维护一个"公共模型仓库目录"。把经常用到的bert、llama等放在/models下,每次有新任务直接rsync。因为镜像站虽然快,但下载几十GB还是要时间的。本地备份一份能省很多时间。
我通常会写一个简单的同步脚本:
MODEL_DIR="/models" REMOTE_USER="user" REMOTE_HOST="remote-server" REMOTE_MODEL_DIR="/data/models" for model in bert-base-uncased llama-2-7b-chat-hf; do rsync -avP $MODEL_DIR/$model/ ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_MODEL_DIR}/$model/ done5.2 不要为了快而用多线程拉爆镜像
使用max_workers=8以上下载时,部分镜像节点会有限流策略。表现是速度反而下降,甚至连接被重置。我实测下来,max_workers=4比较稳妥,尤其是下载大型repo时。如果下载一个大文件而不是很多小文件,max_workers意义不大——单文件走的是类似断点续传的单线程逻辑。
5.3 最后一个小技巧:使用tmux保住所有任务
不管是在本地跑huggingface-cli download,还是用rsync上传,都建议放在tmux里执行。如果你用的是Windows本地机器,可以用wsl里的tmux,或者直接用PowerShell的Start-BitsTransfer做本地下载;但为了跨平台一致,我还是推荐在Linux子系统或macOS终端里用tmux。
有一次传输一个80GB的模型,我没有用tmux,结果SSH会话被公司网络强制断开,rsync进程直接被杀掉。重启会话后重新跑,虽然rsync支持续传,但我中断了将近40GB的进度。用tmux之后,类似问题再也没出现过。
tmux new -s model_sync # 在里面执行你的下载或rsync命令 # Ctrl+B D 退出之后再想恢复,只需:
tmux attach -t model_sync你会发现任务还在正常跑。这是所有长任务的基础操作,没有之一。
5.4 结合镜像站和本地下载,还能解决"需要登录的模型"
有些模型在HuggingFace上需要申请权限、登录后才能下载,比如一部分门控模型。镜像站同样支持token认证,只要提供你的HF token即可:
export HF_TOKEN=hf_xxxxxx huggingface-cli download --token $HF_TOKEN --resume-download your-org/your-model --local-dir ./your-model如果你不想在命令行明文写token,可以提前登录:
huggingface-cli login输入token后,它会存储在~/.cache/huggingface/token。之后用镜像站下载时,SDK会自动带上这个token。上传到远程服务器后,如果你也需要在服务器上直接下载门控模型,不如把token环境变量也同步过去,省得再次登录。
结尾想多说一句
从"远程服务器卡死"到"本地一键下载、rsync秒传",真正困难的地方往往不是命令本身,而是对网络拓扑和工具边界的理解。镜像站解决的是下载入口的问题,rsync解决的是异地传输的问题,sha256校验解决的是放心的问题。把这三样串在一起,不管你的远程服务器在哪个机房、本地网络什么条件,基本都能跑通。
我实际使用中发现,这套流程最大的价值在于可重复。第一次搭好之后,我后来换过好几台服务器,每次都是同一个套路:本地拉镜像,rsync推上去,check完之后开始推理。没有再被HuggingFace的连接问题坑过。如果你也经常在本地和远程服务器之间搬运模型,建议把本文的命令做成脚本存下来,以后能省非常多的折腾时间。