背景
最近在负责一个数据存储平台的搭建,底层存储选型使用了 RustFS (一个兼容 S3 协议的分布式文件系统)。我们面临一个棘手的初始化问题:
手里有 50TB 的历史存量数据,静静地躺在服务器的硬盘目录里。
现在的挑战是: 如何把这 50TB 数据"灌"进 RustFS 的 Bucket 里?
- 方案 A (HTTP 上传): 走网页端或公网 API?50TB 传到猴年马月,且容易受网络波动影响。
- 方案 B (物理 Copy): 既然数据和服务都在同一台机器上,我能不能直接
cp到 RustFS 的数据目录,然后让它自己"扫描"一遍?
我本能地觉得 方案 B 才是正解,简单粗暴且高效。于是,我决定给开源社区提个 Issue。
一:向官方提 Feature Request
我满怀信心地去 GitHub 提了一个 Issue,希望能支持 "Sideloading" (侧载) 或 "Bulk Import" (批量导入) 功能。
我的理由很充分:
"We physically copy the data files directly into the underlying rustfs data directory... run a CLI command to scan... rustfs indexes these files."
(我们直接物理复制文件到底层目录,然后运行 CLI 命令扫描,建立索引。)
然而,官方 Maintainer 的回复给我的想法判了"死刑":
Official Reply:
"The best way to sync a local file system to object storage is by using the
mc mirrorcommand. This approach is highly efficient... Initial Upload performs full synchronization..."
翻译一下就是: 不支持物理扫描,也不会支持。请走标准协议,用 mc mirror 工具来同步。
二:为什么不能直接 Copy?(技术复盘)
冷静下来分析,我明白了官方的苦衷。这涉及到 对象存储 vs 文件系统 的核心架构差异:
- 元数据 (Metadata) 是关键: 对象存储(如 MinIO, RustFS)不只是把文件存进硬盘,还需要生成配套的元数据(ETag, VersionID, Content-Type 等)。
- 物理 Copy 的盲区: 如果我直接
cp video.mp4到底层磁盘,RustFS 的数据库里并没有这个文件的记录。对于系统来说,这个文件是"隐形"的,甚至可能被当做垃圾数据清理掉。 - 一致性: 强制要求走 S3 协议,是为了保证数据和元数据的一致性。
三:最终方案 ------ Localhost 回环加速
既然必须走 API,那就在 "网络路径" 上做文章。
虽然官方说要用客户端传,但谁规定客户端必须在千里之外?我直接在服务端本机装一个客户端,走 127.0.0.1 传输!
1:方案优势
- 0 网络延迟: 流量不经过交换机和路由器,全部在服务器内部的回环接口(Loopback)消化。
- 速度极快: 瓶颈只在于磁盘 I/O 和 CPU,基本能跑满硬盘读写速度。
- 协议合规: 走了标准的 S3 协议,RustFS 能正常生成元数据。
2:实施步骤
1. 安装 MinIO Client (mc)
虽然服务端是 RustFS,但 S3 协议是通用的,mc 是目前最好用的 S3 命令行工具。
bash
curl https://dl.min.io/client/mc/release/linux-amd64/mc \
--create-dirs -o $HOME/minio-binaries/mc
chmod +x $HOME/minio-binaries/mc
export PATH=$PATH:$HOME/minio-binaries/
2. 配置本地别名 (关键)
注意 API 地址填 127.0.0.1。
bash
# mc alias set <别名> <API地址> <AK> <SK>
mc alias set local_rustfs http://127.0.0.1:9000 my_access_key my_secret_key
3. 执行"镜像"同步
假设我的源数据在 /data/source_50tb,我想把它放到 Bucket 的同名目录下。
bash
# 核心命令
# --limit-upload: 稍微限制一下,避免把服务器卡死
# nohup: 放在后台跑,防止断连
nohup mc mirror /data/source_50tb local_rustfs/my-bucket/source_50tb > migration.log 2>&1 &
技巧:在目标路径末尾加上 /source_50tb,mc 会自动在 Bucket 里创建这个子目录层级,保持结构整洁。
四:致命的"空间陷阱" (避坑指南)
在执行之前,我意识到了一个极其严重的问题:磁盘空间够不够?
mc mirror(复制模式): 会保留源文件,传输峰值需 100TB 空间(50TB 源数据 + 50TB 目标数据)。rclone move(搬家模式): 边传边删,磁盘始终维持在 50TB 左右。
对于空间紧张的服务器,必须使用 rclone move。以下是具体实操:
4.1 快速配置 Rclone (连接本机)
我们不需要进入繁琐的交互式菜单,直接用一行命令生成配置。
关键点: endpoint 必须指向 127.0.0.1 以走回环网络。
bash
# 1. 安装 Rclone (如果还没装)
curl https://rclone.org/install.sh | sudo bash
# 2. 一键生成配置 (避免手填出错)
# 别名: local_rustfs
# 类型: s3
# 厂商: Minio (RustFS/Ceph 等兼容 S3 的都可以选这个)
rclone config create local_rustfs s3 \
provider Minio \
env_auth false \
access_key_id YOUR_ADMIN_ACCESS_KEY \
secret_access_key YOUR_ADMIN_SECRET_KEY \
endpoint http://127.0.0.1:9000
配置完成后,建议运行 rclone lsd local_rustfs: 测试一下能不能列出 Bucket。
4.2 执行"搬家"操作
配置通了之后,就可以执行 Move 操作了。记得加上 --transfers 提高并发,压榨本机 I/O 性能。
bash
# 核心命令:Move (上传成功即删除源文件)
# --transfers=32: 开启 32 线程并发 (视磁盘性能调整)
# --progress: 显示实时进度条
rclone move /data/source_50tb local_rustfs:my-bucket/source_50tb \
--transfers=32 \
--progress
⚠️ 高危操作提醒:
rclone move是不可逆 的(源文件会被删)。建议先在/data/source_50tb里找个只有几 MB 的子目录测试一下,确认文件成功出现在 RustFS 且本地被删除后,再跑全量。
五:进阶玩法 ------ 使用 --watch 实现"不停机迁移"
如果你的磁盘空间充裕,或者需要解决"旧系统还在持续产生数据"的问题,官方 Maintainer 提到的 mc mirror --watch 是一个非常高阶的建议。
场景痛点:
传输 50TB 数据可能需要好几天。在这几天里,如果你的旧业务系统还在往 /data/source_50tb 里写新文件,怎么办?等全量跑完,数据又不对齐了。
解决方案:
我们可以利用 --watch 参数,启动一个"实时监听"进程。它会监控文件系统的 INOTIFY 事件,一旦本地有新文件生成,毫秒级同步到 RustFS。
操作姿势:
建议采用 "全量 + 增量" 的组合拳:
- 先跑全量(追历史数据):
使用不带--watch的命令,先把存量数据搬完(或者用 Rclone 搬完历史数据)。 - 再跑增量(追实时数据):
全量结束后,挂起一个监听进程,保持两边数据一致,直到业务正式切换。
bash
# 持续监听模式
# 一旦 /data/source_50tb 有变动,立刻同步
mc mirror --watch /data/source_50tb local_rustfs/my-bucket/source_50tb
总结
面对大数据量的初始化迁移,这次折腾让我总结出几条铁律:
- 不要幻想物理 Copy: 除非你是文件系统开发者,否则绕过 S3 协议会导致元数据丢失。
- 善用 Localhost: 只要客户端和服务端在一起,S3 协议也可以很快。
- 算好磁盘账: 空间够大用
mc mirror(稳),空间不够用rclone move(险,但省空间)。 - 目录结构: 想要保持目录层级,记得在命令的目标路径上把文件夹名字加上。