第 5 章
数据盘上架
1. 背景
1002 是我们实验室的一台 GPU 服务器,双卡 RTX 4000 Ada Generation,主要用来跑目标跟踪的训练和测试。之前数据都堆在系统盘上,根分区已经用了 76%。虽然有一块 1.8T 的移动固态硬盘临时挂在 /data 顶着,但那毕竟是借来的,不是长久之计。
现在,一块 9.1T 的企业级机械硬盘(HGST Ultrastar He10)已经装进机箱了,接下来要做的就是让系统认到它、格式化、把数据搬过去,然后把移动固态还回去。
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 595.71.05 Driver Version: 595.71.05 CUDA Version: 13.2 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
|=========================================+========================+======================|
| 0 NVIDIA RTX 4000 Ada Gene... Off | 00000000:01:00.0 Off | Off |
| 30% 32C P8 4W / 130W | 2MiB / 20475MiB | 0% Default |
+-----------------------------------------+------------------------+----------------------+
| 1 NVIDIA RTX 4000 Ada Gene... Off | 00000000:05:00.0 On | Off |
| 30% 32C P8 7W / 130W | 10MiB / 20475MiB | 0% Default |
+-----------------------------------------+------------------------+----------------------+
操作前的磁盘状态:
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv 913G 655G 212G 76% /
/dev/sdb1 1.8T 365G 1.4T 21% /data
根分区 76%,而 /data 是一块临时借来的移动固态。
2. 认盘
硬件装好了,系统能看到吗?先用 lsblk 看一下所有磁盘:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 9.1T 0 disk
sdb 8:16 0 1.8T 0 disk
└─sdb1 8:17 0 1.8T 0 part /data
sdc 8:32 1 57.7G 0 disk
└─sdc1 8:33 1 57.7G 0 part
nvme0n1 259:0 0 931.5G 0 disk
├─nvme0n1p1 259:1 0 1G 0 part /boot/efi
├─nvme0n1p2 259:2 0 2G 0 part /boot
└─nvme0n1p3 259:3 0 928.5G 0 part
└─ubuntu--vg-ubuntu--lv 252:0 0 928.5G 0 lvm /
可以看到一个 9.1T 的 sda,没有分区、没有挂载点 —— 就是我们刚装上去的新盘。但怎么确认它就是那块 HGST?用 hdparm 查看磁盘的硬件信息:
$ sudo hdparm -I /dev/sda | head -20
/dev/sda:
ATA device, with non-removable media
Model Number: HGST HUH721010ALE600
Serial Number: JEHP68BN
Firmware Revision: LHGAT38Q
Transport: Serial, ATA8-AST, SATA 1.0a, SATA II Extensions,
SATA Rev 2.5, SATA Rev 2.6, SATA Rev 3.0
型号 HGST HUH721010ALE600 —— 这是 HGST(西数旗下)Ultrastar He10,企业级氦气封装硬盘,可靠性比普通消费级盘好很多。SATA III 接口,理论带宽 6Gbps。
💡 另外注意
lsblk里还有一个 57.7G 的sdc,RM列是1(Removable),那是一个 U 盘,别选错设备。
还有一个小技巧:怎么区分机械硬盘(HDD)和固态硬盘(SSD)?
cat /sys/block/sda/queue/rotational
输出 1 表示旋转磁盘(HDD),0 表示固态(SSD)。我们的 sda 输出 1,确认是机械盘。
3. 磁盘使用的三层结构
在动手之前,先理解一下磁盘从"裸盘"到"能存文件"需要经过哪几步。可以把一块硬盘想象成一栋毛坯楼:
第一层:分区表(楼的平面图) 决定这栋楼怎么隔间 —— 哪里到哪里是一个房间。常见的分区表有两种:MBR(老图纸,最大只认 2TB)和 GPT(新图纸,支持超大的楼)。
第二层:文件系统 = 格式化(装修风格) 每个房间怎么收纳东西。ext4 是 Linux 标配,NTFS 是 Windows 标配,exFAT 适合移动存储跨平台使用。
第三层:挂载(给房间编门牌号)
把装修好的房间接入走廊 —— 比如 /data 就是门牌号,进了这个门就能访问对应磁盘上的文件。
分区表决定怎么切,文件系统决定怎么存,挂载决定从哪访问。三步缺一不可。
另外,分区是可选的 —— 技术上可以跳过分区直接格式化整块盘(就像 loft 不隔墙也能住),但有分区表更规范:别人接手一看 lsblk 就能看清结构,而且以后想切出一块做别的用途也方便。分区表本身才几 KB,在 9.1T 面前连灰尘都算不上。
4. 分区
因为这块盘是 9.1T,超过了 MBR 的 2TB 上限,必须用 GPT 分区表。用 parted 来操作:
sudo parted /dev/sda
进入 (parted) 交互界面后,依次输入:
mklabel gpt
mkpart primary ext4 0% 100%
print
quit
解释一下:
mklabel gpt—— 创建 GPT 分区表mkpart primary ext4 0% 100%—— 整块盘建一个分区,用百分比自动 4K 对齐print—— 打印分区表确认quit—— 退出
print 的输出:
Model: ATA HGST HUH721010AL (scsi)
Disk /dev/sda: 10.0TB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Number Start End Size File system Name Flags
1 1049kB 10.0TB 10.0TB ext4 primary
完成后 lsblk 就能看到 sda 下面多了一个 sda1 分区。
5. 格式化
分区切好了,现在给它"装修" —— 创建文件系统:
sudo mkfs.ext4 -m 0 /dev/sda1
参数说明:
-m 0—— 保留空间设为 0%。ext4 默认保留 5% 给 root 用户,在 9.1T 的盘上就是 455G,纯数据盘不需要这个,直接省下来。
格式化很快,十几秒就完成了。输出里有几个关键信息:
Creating filesystem with 2441608704 4k blocks and 305201152 inodes
3.05 亿个 inode —— 每个文件需要一个 inode,我们的数据集大约 2000 万个文件,完全够用。
踩坑:inode 不够
其实我第一次格式化时加了 -T largefile4 参数,以为数据集都是大文件。结果只分配了 238 万个 inode —— 连 2000 万个文件的零头都不够!
-T largefile4 的意思是"平均每 4MB 才分配一个 inode",适合存视频、大模型权重这种大文件。而我们的数据集是大量的小图片,用默认设置(每 16KB 一个 inode)才对。
⚠️ inode 数量在格式化后不能修改,选错了只能重新格式化。好在这时候盘上还没写数据,重来成本为零。
6. 数据迁移
现在新盘格式化好了,但旧的移动固态上还有 365G 数据要搬过来。策略是:先不动 /data,用临时挂载点完成数据迁移,最后再切换。这样万一复制出问题,数据还在旧盘上能兜底。
6.1 临时挂载
把新盘挂到一个临时位置:
sudo mkdir /data2
sudo mount /dev/sda1 /data2
用 df -h /data2 确认 9.1T 出现了。
6.2 rsync 复制
sudo rsync -ah --partial --info=progress2 /data/ /data2/
参数说明:
-a—— archive 模式,保留权限、时间戳、软链接等全部文件属性-h—— 人类可读的大小(GB/MB)--partial—— 支持断点续传,中断了再跑一次能接着传--info=progress2—— 只显示一行总进度(百分比 + 速度 + 剩余时间),不刷屏/data/—— ⚠️ 末尾的/很重要!表示复制目录里面的内容,不带斜杠会多嵌套一层
365G 数据,HDD 写入速度约 130-160 MB/s,大概跑了 37 分钟。
6.3 验证
复制完后再跑一次 rsync,它会自动扫描差异:
sudo rsync -ah --partial --info=progress2 /data/ /data2/
如果输出显示传输量为 0、几秒就跑完,说明数据完全一致。
0 0% 0.00kB/s 0:00:00 (xfr#0, to-chk=0/3884189)
0 差异,380 万个文件全部一致 ✅
踩坑:rsync 写到了根分区
操作过程中出了一个事故:我先卸载了 /data2,然后想卸载 /data 时失败了(有人在用),但 /data2 这个目录还在 —— 只是它不再指向 HDD 了,变成了根分区上的一个普通文件夹。这时候我习惯性地跑了一遍 rsync,结果数据写到了根分区上,差点把系统盘撑爆。
好在及时 Ctrl+C 了,只写了大约 9G。清理方法:
sudo rm -rf /data2/* # 清掉误写的数据
sudo mount /dev/sda1 /data2 # 重新挂回 HDD
📌 教训:操作挂载之前先用
df -h或mount | grep确认挂载状态。目录存在 ≠ 磁盘挂在上面。
7. 切换挂载
数据迁移验证完毕,现在把 HDD 从临时位置 /data2 切到正式位置 /data。
sudo umount /data2 # 卸载 HDD
sudo umount /data # 卸载移动固态
sudo mount /dev/sda1 /data # 把 HDD 挂到 /data
踩坑:target is busy
卸载 /data 时报了 target is busy —— 有人在占用这个目录。用 lsof 查看:
sudo lsof /data
发现一个同学的 bash 终端 cd 在 /data/renzhonghang 里。让他退出(或者 sudo kill 掉进程)就好。
另外,我自己的终端也 cd 在 /data 里,也会导致 busy。看命令提示符 yustia@1002:/data$ 就能发现 —— 先 cd ~ 回到家目录再操作。
切换完用 df -h 确认:
df -h /data
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 9.1T 368G 8.7T 4% /data
9.1T HDD 已经挂在 /data ✅
8. 持久化:fstab
现在的挂载是临时的 —— 重启后就没了。要让系统开机自动挂载,需要写入 /etc/fstab。
先拿到分区的 UUID:
sudo blkid /dev/sda1
/dev/sda1: UUID="8a77fcb2-9fcb-4b68-ad81-f4f675b8997d" ...
💡 为什么用 UUID 而不是
/dev/sda1?因为设备名不稳定 —— 插拔磁盘、加新盘、换 SATA 口都可能导致 sda 变成 sdb。UUID 是分区的唯一身份证,不会变。
编辑 /etc/fstab,把旧的移动固态 UUID 替换为新盘的:
UUID=8a77fcb2-9fcb-4b68-ad81-f4f675b8997d /data ext4 defaults,nofail 0 2
其中 nofail 表示:如果这个盘不在(比如坏了或者被拔了),系统照样能正常启动,不会卡在开机界面。
最后刷新 systemd 的缓存:
sudo systemctl daemon-reload
9. 旧盘善后
移动固态的数据已经全部迁移到 HDD 了,现在把它格式化还给原主。
因为这块盘要在 Windows 和 Linux 之间来回插,选 exFAT 格式:
| 文件系统 | Windows | Linux | 适合场景 |
|---|---|---|---|
| NTFS | ✅ 原生 | ⚠️ 靠 ntfs-3g 驱动 | 固定硬盘、Windows 系统盘 |
| exFAT | ✅ 原生 | ✅ 内核原生(5.4+) | 移动存储、跨平台 |
sudo apt install exfatprogs -y
sudo mkfs.exfat -L "Portable" /dev/sdb1
格式化完成后安全弹出:
sudo eject /dev/sdb
拔盘,收工!
10. 最终状态
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 9.1T 0 disk
└─sda1 8:1 0 9.1T 0 part /data
sdb 8:16 0 1.8T 0 disk
└─sdb1 8:17 0 1.8T 0 part
nvme0n1 259:0 0 931.5G 0 disk
├─nvme0n1p1 259:1 0 1G 0 part /boot/efi
├─nvme0n1p2 259:2 0 2G 0 part /boot
└─nvme0n1p3 259:3 0 928.5G 0 part
└─ubuntu--vg-ubuntu--lv 252:0 0 928.5G 0 lvm /
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv 913G 655G 212G 76% /
/dev/sda1 9.1T 368G 8.7T 4% /data
| 设备 | 容量 | 用途 | 挂载点 | 状态 |
|---|---|---|---|---|
| nvme0n1 | 931.5G | 系统盘(NVMe SSD) | / | ✅ |
| sda1 | 9.1T | 数据盘(HGST 企业级 HDD) | /data | ✅ 新挂载 |
| sdb | 1.8T | 移动固态(已格式化 exFAT) | — | 已拔除 |
留言区待配置
部署 Twikoo 后端后,设置环境变量 NEXT_PUBLIC_TWIKOO_ENV_ID