AutoDL 上跑实验,最让人肉疼的往往不是模型训不出来,而是数据搬运那段时间 GPU 仍然在按秒计费。我现在的固定做法是:数据集、预训练权重和模型 checkpoint 全部走一遍 OSS,AutoDL 节点随开随用、用完释放,数据不会跟着实例一起消失,卡时也不会耗在等待传输上。这篇文章把我在 AutoDL 上配置 OSS 的全流程整理成一套可直接复制粘贴的命令清单——从 Bucket 创建、RAM 授权、ossutil 安装,到训练代码里直传 OSS 都会覆盖。正在用 AutoDL 训练、又受不了反复手动传文件的人,可以直接照着抄。
1. 卡时账单是最大的成本黑洞:为什么数据要绕道 OSS
1.1 等数据下载时 GPU 还在计费,这笔账有多亏
很多人刚用 AutoDL 的习惯是:开机,打开终端,wget 数据集,等下载完再开始训练。一套公开数据集少则几 GB,多则几百 GB,下载时间动辄 20 分钟到 2 小时。这期间卡时费一分不少地扣,如果你开的是多卡实例,浪费的其实是好几倍的卡时成本。更糟的是,你还会习惯性地开着 JupyterLab 页面干等,注意力全耗在进度条上。
我自己踩过最狠的一次,是跑一个多模态实验要把混在一起的视频抽帧重建,凑出来一个 180GB 左右的数据集。第一次傻乎乎地在 4090 实例上直接传,传输加校验花了快三个小时,按当时的价格算,光搬运就烧掉了大几十块的卡时费,而且显存、内存全部白挂。后来我把流程改成"OSS 中转 + 无卡模式搬运"之后,同样三个小时的传输,成本几乎可以忽略。这个转变不是省了一点半点,而是从"每分钟都在烧钱"变成"放着不动也不心疼"。
所以核心思路就一句话:不要让 GPU 实例参与任何和"等待"有关的操作。上传、下载、解压、打包这些 IO 密集任务,要么放到无卡模式去做,要么放到 OSS 端用工具脚本完成。GPU 实例只干两件事——拉数据和训练,其余时间直接关机。
1.2 OSS 在 AutoDL 训练流中的位置与成本模型
OSS(Object Storage Service)是阿里云的对象存储服务,没有传统文件系统的目录层级,通过 bucket + key 来组织数据。你可以把它想象成一个放在远端的取件柜:AutoDL 实例是一台临时租用的工程车,OSS 是仓库,你需要什么就从仓库取,不需要把整座仓库搬到车上。这样做的直接好处有三个:数据持久化、实例解耦、成本分离。AutoDL 实例释放后数据盘不保证保留,但 OSS 上的对象只要不手动删,就一直躺在那里。
在实际工作流里,我推荐的角色分工很固定:
- 数据集、预训练权重:从各种渠道下载后先做校验、去重、打包,统一上传到 OSS;
- 训练中间产物、checkpoint:每轮训练结束或定时同步到 OSS,防止实例中途释放导致成果丢失;
- 最终结果:整理完后上传 OSS,再从本地下载到个人电脑。
成本上,OSS 的费用由三块组成:存储费、流量费和请求费。AutoDL 实例默认走公网访问 OSS,所以从 OSS 下载数据会产生下行流量费。很多人只盯着卡时价格,月底看账单才发现流量费比预想高出一截。做个粗略参考,标准存储大概每 GB 每月 0.12 元左右,中国大陆地域的公网下行流量大概每 GB 0.5 元,具体以控制台定价为准。我的建议是 bucket 选在离 AutoDL 机房最近的地域,并且只把真正常用的对象放标准存储,很冷的数据可以降级到低频访问存储,但要注意低频访问有最短计费周期,存几天就删反而更贵。
| 费用项 | 触发条件 | 价格量级参考 |
|---|---|---|
| 存储费 | 对象在 OSS 上占用的空间 | 标准存储约 0.12 元/GB/月 |
| 下行流量费 | 从 OSS 公网下载数据 | 中国大陆约 0.5 元/GB |
| 请求费 | 每次 GET/PUT 等 API 调用 | 万次几分钱级别 |
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 账号侧一次性准备:Bucket、RAM 与 AccessKey 安全基线
2.1 创建 Bucket:地区、存储类型和读写权限怎么选
打开阿里云 OSS 控制台,点创建 Bucket。地区选择是最容易手滑的一步——很多教程默认截图的华东,但你的 AutoDL 实例如果在华北或华南,上传速度会差很远。我建议先确认 AutoDL 实例所在地域,bucket 地域和它保持一致。地域选错了也不是不能用,只是跨地域访问延迟更高,流量费还可能增加,属于完全没必要的开销。
Bucket 名称要求全局唯一,小写字母、数字和短横线。我习惯用项目名-环境-区域的命名方式,比如 coco-mix-bj,后面写 RAM 策略时也容易按 bucket 前缀做权限隔离。存储类型默认选标准存储,这对训练数据集没问题。读写权限直接选"私有",不要图方便用公共读。训练数据里经常有还没发布的结果,公共读等于把数据裸奔在公网,这个风险没必要承担。
创建完之后,先不要急着做任何配置,先去控制台里确认一下 bucket 所在的 Region,Region 代码后面配 endpoint 时要用。比如华北二(北京)对应 oss-cn-beijing,华东一(杭州)对应 oss-cn-hangzhou,华南一(深圳)对应 oss-cn-shenzhen。记错这个,后面所有命令都会报地域不匹配。
2.2 RAM 子账号与最小权限策略 JSON
AccessKey 千万不要用阿里云主账号的,一旦泄露等于把整个账号的管理权限都交出去。正确做法是在 RAM 控制台创建一个子账号,专门给 OSS 用,然后只授 OSS 相关权限。
最省事的办法是直接给子账号勾上 AliyunOSSFullAccess 系统策略,第一版可以先这么干,先跑通再收紧。但更稳妥的是用最小权限策略。下面这个 JSON 既能满足 ossutil ls,又能覆盖上传、下载、删除,Resource 部分改一下 bucket 名就能直接用:
json复制{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"oss:ListBuckets",
"oss:GetBucketInfo",
"oss:GetBucketLocation",
"oss:ListObjects",
"oss:GetObject",
"oss:PutObject",
"oss:DeleteObject"
],
"Resource": [
"acs:oss:*:*:my-bucket",
"acs:oss:*:*:my-bucket/*"
]
}
]
}
注意 ListBuckets 是账号级操作,如果你只操作固定一个 bucket,可以去掉。但 ListObjects、GetBucketLocation 这两个权限最好保留,我遇到很多次自建策略漏配 oss:GetBucketLocation,导致 ossutil cp 在初始化阶段就报错,看起来权限没问题,实际卡在一个很隐蔽的地方。
2.3 AccessKey 的存放与轮换习惯
AccessKey ID 和 AccessKey Secret 在创建子账号时只完整显示一次,建议当时就存进密码管理器。之后的使用原则是:不写死在代码里,尽量用环境变量或独立配置文件传入;不同项目尽量不用同一个 AccessKey,防止某台实例被入侵后全部账号遭殃。
我在 AutoDL 上执行 ossutil config 后会立刻 chmod 600 ~/.ossutilconfig,避免同实例其他用户读到。跑训练代码时,AccessKey 通过环境变量注入,代码里只读 os.environ。一旦发现 AccessKey 可能泄露,马上去 RAM 控制台禁用或删除重新生成,别等一下午再处理,攻击者可不会等你。
3. 三行命令装好 ossutil,非交互式配置一次跑通
3.1 下载前先看 CPU 架构:装错二进制是第一个坑
AutoDL 实例绝大多数是 x86_64 架构,偶尔会遇到 aarch64 的 CPU 实例。装 ossutil 前先看一眼架构:
bash复制uname -m
输出 x86_64 就用 amd64 的包,输出 aarch64 就去官网找对应 arm64 版本。硬装错版本的结果通常是 Exec format error,看着像命令不存在,其实是二进制和 CPU 不匹配。
x86_64 下下载安装命令如下,三行搞定:
bash复制wget https://gosspublic.alicdn.com/ossutil/1.7.19/ossutil-v1.7.19-linux-amd64.zip
unzip -o ossutil-v1.7.19-linux-amd64.zip
cp ossutil-v1.7.19-linux-amd64/ossutil64 /usr/local/bin/ossutil && chmod +x /usr/local/bin/ossutil
装完执行 ossutil --version,有版本号输出就说明路径没问题。放到 /usr/local/bin 是因为 AutoDL 用户一般有 root 权限,SSH 终端、JupyterLab 终端和训练脚本里都能直接调用 ossutil,不用每次折腾环境变量。
3.2 非交互式 config 命令与配置文件权限
ossutil 第一次使用通常会建议跑交互式 ossutil config,但复制即用的场景里交互式反而啰嗦。直接非交互式一行搞定:
bash复制ossutil config -e oss-cn-beijing.aliyuncs.com -i LTAIxxx -k your_secret --language=CH
-e 是 endpoint,-i 是 AccessKey ID,-k 是 AccessKey Secret。执行后会在当前用户家目录生成 ~/.ossutilconfig,默认权限可能偏宽松,立刻收紧:
bash复制chmod 600 ~/.ossutilconfig
有个细节容易被忽略:如果你在 AutoDL 上切换了 conda 环境,或者通过 JupyterLab 的终端而不是 SSH 登录,Shell 的 $HOME 路径可能不一致。统一用 ~ 确认配置文件位置就对了。实在找不到,cat ~/.ossutilconfig 看一眼里面有没有 [Credentials] 段和 endpoint 字段。
3.3 用 list 命令验证整条链路
配置完先别急着传大文件,先跑一个轻量命令确认网络、权限、账号三个环节都通:
bash复制ossutil ls oss://my-bucket/
能看到 bucket 里的对象列表,说明链路是通的。如果报 AccessDenied,优先查 RAM 策略是否漏了 oss:ListObjects;如果报连接超时,多半是 endpoint 配错地域或用了内网地址,切回公网 endpoint 再试。这一步 30 秒不到,能省掉后面排查大文件传输失败的大把时间。
4. 复制即用命令集:上传下载、增量同步与训练内直读 OSS
4.1 cp 命令的精简用法与目录级传输
AutoDL 的数据盘默认挂载在 /root/autodl-tmp,所有大文件尽量放这里,系统盘空间通常很紧张。上传时我习惯先进入数据盘目录再执行,避免路径拼错:
bash复制cd /root/autodl-tmp
ossutil cp -r dataset/ oss://my-bucket/dataset/
这个命令会把 dataset 目录下的所有内容上传到 oss://my-bucket/dataset/ 下,并在 OSS 端自动生成对应的 key,不需要手动建目录。下载则反过来:
bash复制ossutil cp -r oss://my-bucket/dataset/ /root/autodl-tmp/dataset/
注意斜杠的位置。源路径结尾的斜杠和目的地结尾的斜杠都影响对象在 bucket 下的 key 前缀,粗心写错可能让文件层级变得很怪。我写命令时习惯源路径和目标路径都带结尾斜杠,保持对称,基本不会出幺蛾子。
大目录第一次上传如果中断,可以加 --update 续传,它会跳过两端已经一致的文件:
bash复制ossutil cp -r --update /root/autodl-tmp/dataset/ oss://my-bucket/dataset/
4.2 sync 增量同步:参数含义与 --delete 的杀伤力
每天训练完要同步 checkpoint 到 OSS,cp 每次都全量覆盖不够聪明,更舒服的是 sync。它默认就是增量同步,只上传新增或时间更新的文件:
bash复制ossutil sync /root/autodl-tmp/experiment/ oss://my-bucket/experiment/
加 --delete 可以把远端存在但本地已删除的文件也同步删掉,适合做严格镜像:
bash复制ossutil sync /root/autodl-tmp/experiment/ oss://my-bucket/experiment/ --delete
但 --delete 是我最谨慎用的参数。本地路径写错,比如目录不存在或内容为空,同步会把 OSS 上对应前缀下的文件全部删光,恢复成本极高。我每次执行带 --delete 的命令前,都会先不带 --delete 跑一遍,或用 ossutil ls 确认远端文件和本地确实一一对应,再上真正的删除参数。
还有个 --size-only,只比较文件大小、不比较最后修改时间。如果数据集特别大且你确定不会出现同名但内容不同的场景,用 --size-only 能省去大量 stat 请求,判断速度快不少。但日常同步我还是用默认的时间和大小双条件判断,稳妥优先。
4.3 大文件与小文件:分片、并发和先打包的策略
OSS 本质是 HTTP 对象接口,单文件传大的时候,分片上传和并发分片能显著提升吞吐。ossutil 对超过 100MB 的大文件默认走分片上传,但默认并发可能不够。我实际用下来,对 10GB 左右的模型文件,加并发参数能快一截:
bash复制ossutil cp -r --jobs 10 --parallel 8 --part-size 100MB /root/autodl-tmp/dataset/ oss://my-bucket/dataset/
参数含义可以记成一张表:
| 参数 | 含义 | 我的推荐值 |
|---|---|---|
| --jobs | 同时处理的文件数量 | 10 |
| --parallel | 单个文件内部并行分片数 | 8 |
| --part-size | 每个分片的大小 | 100MB |
参数不是越大越好。我试过把 --jobs 调到 40,结果 OSS 请求直接吃满本地网络,训练里其他下载任务全卡住。对常见的公网连接,--jobs 10 --parallel 8 已经够用,再往上就是玄学调参。
如果遇到几十万个 5KB 的小图片小文本,别直接传目录,先打包再传更靠谱。几千个文件会让 HTTP 请求数爆炸,传输时间和费用都不可控:
bash复制cd /root/autodl-tmp && tar -czvf dataset.tar.gz dataset
ossutil cp dataset.tar.gz oss://my-bucket/dataset.tar.gz
到了目标实例再解包,速度往往比直接传目录快很多,而且目标端只需要下载一个文件,还能走单文件分片并发,资源利用率完全不一样。
4.4 Python SDK 极简封装:每轮训练后自动备份 checkpoint
除了命令行,训练代码里直接读写 OSS 也很常见。安装 SDK 一行命令:
bash复制pip install oss2
然后写一个极简工具模块,把当前实验的 checkpoint 传到 OSS:
python复制import os
import oss2
ENDPOINT = os.environ.get("OSS_ENDPOINT", "oss-cn-beijing.aliyuncs.com")
BUCKET_NAME = os.environ["OSS_BUCKET"]
auth = oss2.Auth(os.environ["OSS_ACCESS_KEY_ID"], os.environ["OSS_ACCESS_KEY_SECRET"])
bucket = oss2.Bucket(auth, f"https://{ENDPOINT}", BUCKET_NAME)
def upload_checkpoint(local_path, remote_key):
bucket.put_object_from_file(remote_key, local_path)
def download_checkpoint(remote_key, local_path):
bucket.get_object_to_file(remote_key, local_path)
训练循环里可以配合一个进度回调,随时看传输状态:
python复制def progress_callback(consumed_bytes, total_bytes):
if total_bytes:
print(f"\r{consumed_bytes / total_bytes:.1%}", end="")
bucket.put_object_from_file(
"ckpt/epoch_10.pth",
"/root/autodl-tmp/ckpt/epoch_10.pth",
progress_callback=progress_callback,
)
再给一个实战建议:如果你不想让上传拖慢训练,可以把上传丢进线程池异步执行,比如 ThreadPoolExecutor(max_workers=2),训练迭代完直接提交上传任务,下一轮继续跑。但要注意,如果同一份 checkpoint 一边被 torch.save 写入、一边被 oss2 读取上传,可能传到一半文件被覆盖,得到一个损坏的对象。我的做法是每轮保存成带时间戳的新文件名,或在本地先保存到临时路径再上传,确保远端拿到的永远是完整快照。
5. AutoDL 实测踩坑记录:Endpoint 错配、权限 403 和效率细节
5.1 内网 Endpoint 一定会超时吗:我验证过的结论
阿里云文档里经常会看到 oss-cn-beijing-internal.aliyuncs.com 这类内网 endpoint,它只在阿里云 VPC 内网环境才可访问。AutoDL 的实例并不在你的阿里云 VPC 里,所以从 AutoDL 的默认网络访问这个地址,绝大多数情况就是连接超时。我刚开始也想省流量费,试过内网 endpoint,结果干等 5 分钟,连日志都看不出来到底卡在哪。
我的结论很直接:AutoDL 上统一用公网 endpoint oss-cn-beijing.aliyuncs.com,别去折腾内网。除非你的 AutoDL 实例明确提供了专线或内网通道,否则就是浪费时间。公网 endpoint 会产生从 OSS 到 AutoDL 的下行流量费,这个费用真实存在,但和卡时费比还是便宜太多。
5.2 最小权限策略漏了 ListObjects,ossutil ls 直接 403
我一开始给 RAM 子账号配的策略只写了 oss:GetObject、oss:PutObject、oss:DeleteObject 三个对象级权限,上传下载本来都能正常跑。结果某天想用 ossutil ls 看看 bucket 里有什么,直接报 AccessDenied,排查半天才发现 ls 需要 oss:ListObjects 这个 bucket 级权限,而策略里根本没有。
这个报错说明:最小权限策略要按实际命令集来设计,不能只按"我想操作对象"来设计。既然要和 ossutil 配合使用,oss:ListObjects、oss:GetBucketLocation、oss:GetBucketInfo 这几个 bucket 级读权限几乎绕不开。干脆一步到位把前面 2.2 节的 JSON 用上,省得后面权限不够再回来改。
5.3 无卡模式才是数据搬运的正确打开方式
AutoDL 控制台有一个无卡模式开机选项,会在不分配 GPU 的情况下启动实例。这时候你依然能 SSH 登录,能跑 CPU 任务、能联网、能操作数据盘,但不会产生 GPU 卡时费。我的数据搬运流程现在已经固定成四步:
text复制本地或原先云盘 → 上传到 OSS
AutoDL 新建实例 → 无卡模式开机 → 从 OSS 拉取数据和代码 → 关机
AutoDL 开 GPU 模式 → 直接训练 → 每轮或定时同步 checkpoint 到 OSS
有些大文件从本地传到 OSS 这一步很慢,我干脆在 AutoDL 上用 wget 先把数据拉到数据盘,再在无卡模式下做去重、打包,最后传输到 OSS。整个过程不花一分卡费。很多人的误区是把"下载数据集"和"上传 OSS"这两件事全放在 GPU 模式的实例里干,浪费的卡时远比想象中多。
5.4 实例释放后配置丢光:把初始化写成脚本存底
AutoDL 实例释放或重建后,数据盘内容不保证保留,系统盘上的自定义配置也可能会被清空,~/.ossutilconfig 就是最典型的丢失项。我第一次遇到时,新开实例发现 ossutil 命令不存在,才意识到初始化动作应该脚本化。
我现在的做法是维护一个 init_oss.sh,每次新开实例直接跑一遍:
bash复制#!/bin/bash
set -e
export ACCESS_ID="LTAIxxx"
export ACCESS_SECRET="your_secret"
wget https://gosspublic.alicdn.com/ossutil/1.7.19/ossutil-v1.7.19-linux-amd64.zip
unzip -o ossutil-v1.7.19-linux-amd64.zip
cp ossutil-v1.7.19-linux-amd64/ossutil64 /usr/local/bin/ossutil
chmod +x /usr/local/bin/ossutil
ossutil config -e oss-cn-beijing.aliyuncs.com -i "$ACCESS_ID" -k "$ACCESS_SECRET" --language=CH
chmod 600 ~/.ossutilconfig
这个脚本我不会放进公开仓库,因为里面带 AccessKey,属于敏感文件。放到私有 Gist 或密码管理器里,每次新开实例拉下来执行一遍,新实例从零到能传数据,五分钟内搞定,再也不用在控制台里点来点去。
我自己现在几乎把所有和 AutoDL 相关的数据交接都归到了这套流程里:数据先进 OSS,训练结果定时回流 OSS,实例本身只是随时可以丢弃的计算资源。刚开始会觉得多一道传输步骤很麻烦,坚持几周后你会发现,真正省下来的不只是卡时费,还有每次找数据、等数据、担心数据丢失的精力。上面的命令基本覆盖了日常 90% 的使用场景,剩下的边角需求,等真遇到了再查文档也不迟。
