Hugging Face Storage 成为 SkyPilot 一级后端:零出站费跨云存储
Run AI workloads on any cloud, store on Hugging Face: zero-egress storage with SkyPilot
Hugging Face Storage 现为 SkyPilot 的一级后端。用户通过 `hf://` URL 和现有 HFTOKEN 即可将 Hugging Face Bucket(读写)或模型/数据集/Space 仓库(只读)挂载到 SkyPilot 任务中,支持 MOUNT(FUSE 懒加载)或 COPY 模式。SkyPilot 可将任务调度到 20+ 云、Kubernetes、Slurm 及本地集群的任意可用 GPU 上。Hugging Face 不收取出站及 CDN 费用,故跨云读取数据无额外成本。存储价格 $12–18/TB/月,低于 AWS S3 加出站费。Bucket 基于 Xet,增量检查点和模型变体仅存储和传输改动部分。
Hugging Face 和 SkyPilot 把存储和算力真正解耦了,跨云读取模型零出口费,用过 SkyPilot 的团队可以立刻省下那块 egress 账单。
对大多数团队来说,模型和数据集都存放在某一朵云某个区域的一个存储桶里。而你能拿到的 GPU——无论是用于开发、训练还是推理服务——却越来越多地坐落在与你数据不同的另一朵云上。一旦这两者分离,你仅仅是为了把自己的数据读取到自己的 GPU 上,就要支付一笔跨云传输税。我们与 Hugging Face 携手,把这两半合二为一:你的模型和数据集留在 Hub 上,而 SkyPilot 则在任何拥有 GPU 的集群上运行计算(开发、训练或推理服务)。只需一个 hf:// URL 和你已经拥有的 HF_TOKEN,就能把 Hugging Face Bucket 或任意 Hub 仓库挂载到 SkyPilot 任务中,然后在任何有容量的地方启动它。Hugging Face 不收取出口流量费用,因此在任何云上,把数据读取到这些 GPU 上都不花一分钱。
以下是新增内容:
- 你的 Hub 数据可用于任何任务。
store: hf可将 Hugging Face Bucket(读写)或任意 model / dataset / Space repo(只读)挂载到 SkyPilot 任务中,只需一个hf://URL 和你现有的HF_TOKEN,通过MOUNT或COPY即可完成。 - 在任何 GPU、任何云上运行它。SkyPilot 可在 20+ 云、Kubernetes、Slurm 和本地环境中为该任务寻找算力,因此同一次运行会使用你可用的预留或按需 GPU,无论来自哪家厂商。
- 无需出口流量即可读取你的数据。 Hugging Face Storage 不收取任何出口流量费或 CDN 费用,因此无论 SkyPilot 将任务调度到何处,它都会直接从同一个存储桶读取你的模型和数据集,无需按云复制副本,也不会产生将它们拉取进来的出口流量账单。
- 基于 Xet 的去重。 存储桶构建于 Xet 之上,因此增量检查点和模型变体只会存储和传输发生变化的那些数据块。
- 共同打造。Hugging Face 与 SkyPilot 联合发布了这一功能,Hugging Face 团队还向上游贡献了
hf-mountFUSE 修复,使其能够在非特权容器中运行。
Hugging Face Storage 现已成为 SkyPilot 的一等后端
SkyPilot 任务此前已经可以通过将云对象存储(S3、GCS、Azure、R2 以及更多)挂载到本地路径来读写它们。Hugging Face Storage 现在也加入了这一行列,作为 store: hf,通过 hf:// 协议访问:
file_mounts:
# A Hugging Face Bucket, read-write, for checkpoints, logs, processed data.
/checkpoints:
source: hf://buckets/my-org/qwen-sft
store: hf
mode: MOUNT # or COPY
# A model repo, mounted read-only.
/base-model:
source: hf://Qwen/Qwen3.5-4B
store: hf
mode: MOUNT
# A dataset repo, pinned to a revision, read-only.
/data:
source: hf://datasets/my-org/my-dataset@main
store: hf
mode: MOUNT
这一个 hf:// 协议覆盖了整个生命周期:从仓库中读取 模型和数据集,在训练时将检查点写入 Bucket,将完成的模型发布回仓库,并在提供服务时将其拉取到推理服务器上。大多数团队已经将他们的模型和数据集保存在 Hub 上,因此无需迁移步骤,也无需创建新的存储账户。
MOUNT 使用 Hugging Face 的 hf-mount FUSE 后端,因此一个 bucket 或 repo 会作为本地路径出现在 SkyPilot 的其他 FUSE 挂载点旁边(gcsfuse、blobfuse2、rclone、goofys)。拉取发生在文件系统层:当你的代码发起一次 read() 时,驱动只从 Xet 后端拉取那些字节,因此只有你实际访问的数据才会经过网络,而 hf-mount 会保留一份磁盘缓存,让重复读取留在本地。这个磁盘缓存正是 SkyPilot 在 MOUNT_CACHED 下为其其他后端提供的行为,而在那里,普通的 MOUNT 反而会每次读取都从 bucket 流式传输,不在本地保留任何内容。对于 hf 存储,MOUNT 和 MOUNT_CACHED 行为相同,因此两种模式都会保留缓存。
由于读取是惰性的,一个进程可以在整个文件下载完成之前就开始处理一个大文件,而不是先阻塞等待完整复制。这让 GPU 几乎立即保持忙碌,在数据流入的同时进行训练,而不是在数据集或 checkpoint 复制下来时闲置(并计费)。这在第一个 epoch 时收益最大,因为那时还没有任何缓存。COPY 走的是另一条路,通过 huggingface_hub 预先下载,没有特殊要求。
认证用的就是你已有的 token。在你的环境中设置 HF_TOKEN,并通过 --secret HF_TOKEN 把它交给一次运行;无论作业落在哪个云上,SkyPilot 都会用它来完成挂载。无论作业落在 AWS、GCP、Azure、Nebius、Lambda 还是你自己的 Kubernetes 集群上,一个 token 都能用,因此无需在各云之间来回管理 bucket 密钥。
无出口流量费用:存储不再决定你在哪里运行
GPU 容量如今很少来自单一来源。为了获得足够的 H100 和 H200,团队会同时在多家供应商处持有预留和承诺容量(在超大规模云上的一块资源、在 neocloud 上的一个集群,或许还有本地机架),并在有配额的地方运行。SkyPilot 正是为此而生:一份作业规格,跨 20+ 云、Kubernetes 和本地环境调度,落在任何一个空闲的预留集群上。
对象存储一直是症结所在。对象存储是按区域和按云划分的,因此要为位于另一家供应商数据中心的 GPU 或推理服务器提供数据,就意味着要么在每家供应商的存储桶里都保留一份数据副本,要么付费跨云拉取。大多数云在数据离开其网络的那一刻就收取出口流量费(从 AWS 出去大约 $0.09/GB),而且往往在同一朵云内的区域之间也收费。把基础模型拉到每个推理节点上,或者从另一朵云上的集群对数据集迭代好几个 epoch,都会在你已经预留的 GPU 之上再叠加一笔高昂账单。团队最终只能把每次运行钉在持有数据的那家供应商上,让其余容量闲置。
Hugging Face Storage 在成本真正造成痛点的环节——读取侧——消除了这笔开销。凭借 无出口流量费或 CDN 费用以及 $12-18/TB/月的存储价格(相比之下 AWS S3 约为 $23/TB 外加出口流量费),同一个存储桶可以从上述每一个集群访问,而且无论 GPU 在哪里运行,从它读取都是免费的。写回仍然要付你的计算云通常的出口流量费,与写入任何云外存储一样,但对大多数 AI 工作负载而言,读取占主导:一个数据集要流式传输很多个 epoch,或者模型权重要被拉到每一个新的训练或推理节点上。于是你不再需要把每次运行钉在持有数据副本的那家供应商上。
一个快速基准测试
为了收集一些基准测试数据,我们做了一次小规模微调:Qwen/Qwen3.5-4B 在 HuggingFaceH4/Multilingual-Thinking 数据集上,使用 TRL 的 SFTTrainer,以只读方式从其 Hub 仓库挂载模型,并将每个检查点写入 Hugging Face Bucket。同一份 SkyPilot YAML 在 AWS、GCP 和 Lambda 上运行,仅更改 --infra。SkyPilot 将每个任务调度到 GPU 空闲的位置,三者都读写同一个 bucket。
# qwen-sft.yaml. Launch anywhere: sky launch qwen-sft.yaml --infra aws|gcp|...
resources:
accelerators: H100:1 # or whatever the cloud has
file_mounts:
/base-model:
source: hf://Qwen/Qwen3.5-4B # read-only, lazy-mounted from the Hub
store: hf
mode: MOUNT
/checkpoints:
source: hf://buckets/my-org/qwen-sft # read-write Bucket
store: hf
mode: MOUNT
run: |
python train.py --model /base-model --output_dir /checkpoints
我们测量的内容:
- 模型在每朵云上都能免费加载。 惰性读取只拉取
from_pretrained所触及的部分,因此大约 30 秒即可开始训练(最高500 MB/s)。由于 Hugging Face 不收取出口流量费用,这次拉取没有任何成本;如果模型存放在 S3 上,每次从另一朵云上的 GPU 读取都会被计收出口流量费(AWS 上为 $0.09/GB)。 - 检查点直接流式写入 bucket,速度最高约 170 MB/s(每个权重 8.43 GB),并在 GPU 实例结束后依然持久保存。
各云平台检查点写入 bucket 的速度如下:
| 云平台 | GPU | 检查点写入 |
|---|---|---|
| AWS(us-east-2) | L40S | ~168 MB/s |
| GCP(us-central1) | L4 | ~123 MB/s |
| Lambda(us-west-3) | H100 | ~112 MB/s |
由 Xet 支持的存储:为检查点和模型变体提供去重
Hugging Face Buckets 构建于 Xet 之上,后者使用 内容定义分块将文件切分为约 64 KB 的块,并将每个唯一块只存储一次。由于分块边界跟随内容,一次编辑只会改变它所触及的块,其余部分会被识别为已存储。这在以下几个方面带来收益:
- 增量检查点和适配器检查点。当你冻结层、训练适配器,或在两次保存之间让大部分权重保持不变时,只有发生变化的块会被上传,而不是整个检查点。
- 共享同一基座的模型变体。同一基座模型的微调和量化版本之间高度重叠,因此共享的块在所有版本中只存储一次。
- 你追加写入的数据集。 诸如对话轨迹或推理输出之类的日志,会通过向大型 Parquet 文件追加行来增长。已有的行组保持字节级完全一致,因此只有新增的行会被传输:在 Hugging Face 的测试中,向一个 10 万行的表追加 1 万行,只移动了约 10 MB,而不是完整的约 106 MB。(如果你就地编辑或删除行,请使用
use_content_defined_chunking=True写入,以将改动保持在本地。) - 重新上传会跳过已存储的内容。 在我们的测试中,重新上传一个已存在于存储桶中的 8.43 GB blob 大约耗时 8 秒,而首次上传耗时 24 秒,因为只有分块哈希会被传输。同样的机制让仓库与存储桶之间的服务端
hf buckets cp通过引用进行复制,而不是重新上传字节。
你能节省多少取决于你的产物有多少重叠,但去重是自动的:你照常写入检查点,只有新的分块会离开本机。
pip install "skypilot[huggingface]"
hf auth login # or: export HF_TOKEN=<your-token>
为任意 SkyPilot 任务添加一个 hf:// 挂载并启动。MOUNT 需要一个带有 glibc 2.34+ 和 /dev/fuse 的基础镜像。
共同打造:Hugging Face 与 SkyPilot
最初的 store: hf 支持源于 Nikhil Jha 的贡献。Hugging Face 团队将其继续推进,并把 hf-mount 的 FUSE 修复上游化,使其能够在非特权容器中挂载——这也是许多 Kubernetes 集群的默认配置。SkyPilot 团队将其接入存储后端。整条链路都是开源的:SkyPilot、Hugging Face 的 hf-mount,以及 huggingface_hub 客户端。
资源
来源:Hugging Face:Blog(RSS) · huggingface.co