2. 数据存储层集成

机器学习工作负载对存储的需求从来不是单一的。数据摄取阶段需要高吞吐、低成本的对象存储;单机训练需要低延迟、高IOPS的块设备;多节点共享训练需要并发可写的文件系统;而大规模分布式训练又要求存储层能够提供数百GB/s的聚合带宽。AWS 提供了四类核心存储服务来覆盖这些场景:S3、EBS、EFS 和 FSx for Lustre。理解它们的定位差异,并掌握在 SageMaker、EC2 训练实例上的实际挂载方式,是构建高效 ML 数据平台的基础。

四类存储的本质差异

S3 是对象存储,数据以 key-value 形式存放,通过 HTTP API 访问,天然支持无限容量和 11 个 9 的持久性,是数据湖的事实标准。EBS 是块存储,挂载到单台 EC2 实例后表现为一块裸磁盘,提供稳定的 IOPS 和低延迟,但同一时刻只能挂载到一个实例(io2 多挂载等特例除外)。EFS 是托管的 NFS 文件系统,可被成百上千个 EC2 实例或容器同时挂载,适合共享代码、检查点和中小规模数据集。FSx for Lustre 是高性能并行文件系统,专为 HPC 和分布式训练设计,能够与 S3 建立数据仓库链接,实现按需加载和结果回写。

ML 存储选型:四类服务与工作负载匹配 S3 对象存储 无限容量 · 11个9持久性 HTTP API · 按量计费 数据湖 / 原始数据集 模型产物 / 日志归档 EBS 块存储 单实例挂载 · 低延迟 gp3/io2 · 可调IOPS 单机训练 / 本地缓存 临时工作目录 EFS 共享文件 NFSv4 · 多实例并发 弹性容量 · 按量计费 共享代码 / 检查点 中小规模共享数据 FSx for Lustre 并行文件系统 · 高吞吐 S3 数据仓库链接 分布式训练 / HPC 大规模数据集 挂载与访问方式 boto3 / s3fs / SDK mkfs + mount /dev/nvmeXn1 mount -t nfs4 ...:/ mount -t lustre ...@tcp:/fsx 数据摄取与预处理 Glue / EMR / Lambda 单机训练 EC2 / SageMaker 单节点 多节点共享 SageMaker 分布式 / ECS 大规模分布式训练 多机多卡 / HPC 集群 选型原则:按访问模式(顺序/随机)、并发需求(单/多实例)、延迟敏感度、成本模型综合判断 典型组合:S3 数据湖 + FSx for Lustre 加速训练 + EBS 本地缓存 + EFS 共享检查点

S3 数据湖的访问模式与优化

在 ML 工作负载中,S3 通常作为数据湖的底座。训练任务读取 S3 数据有两种主流方式:一是通过 boto3 或 SageMaker SDK 在 Python 中按需下载;二是通过 s3fs 或 Mountpoint for Amazon S3 将存储桶挂载为本地文件系统。前者适合小批量、随机访问的场景,后者适合需要 POSIX 接口的框架(如 PyTorch DataLoader 直接读取文件路径)。

需要注意的是,S3 的 GET 请求有延迟,单流吞吐受限于单连接带宽。当训练任务需要读取数万个小文件时,逐个 GET 会产生大量请求开销。此时应使用 S3 的批量操作或先通过 AWS DataSync、DistCp 将数据同步到 FSx for Lustre,再让训练任务从 Lustre 读取。另一个关键优化是合理设置 S3 前缀分区,避免请求热点。例如将 s3://bucket/dataset/year=2024/month=01/day=15/ 作为分区路径,使 GET 请求分散到不同分区上。

EBS 在单机训练中的角色

EBS 卷挂载到 EC2 后,表现为块设备。对于单机训练,EBS 通常承担三类职责:存放操作系统和训练框架、作为本地缓存加速数据读取、以及存放临时检查点。gp3 卷默认提供 3000 IOPS 和 125 MB/s 吞吐,可独立调整至 16000 IOPS 和 1000 MB/s,适合大多数训练场景。io2 Block Express 则提供更高的 IOPS 和更低的延迟,适合对随机读写敏感的小文件训练。

挂载 EBS 的标准流程是:创建卷并附加到实例后,使用 lsblk 确认设备名(如 /dev/nvme1n1),然后格式化并挂载。以下是一个典型操作序列:

# 确认新卷设备名
lsblk

# 格式化为 ext4(仅首次使用)
sudo mkfs -t ext4 /dev/nvme1n1

# 创建挂载点并挂载
sudo mkdir /mnt/data
sudo mount /dev/nvme1n1 /mnt/data

# 验证挂载与可用空间
df -h /mnt/data

如果希望实例重启后自动挂载,需要将设备 UUID 写入 /etc/fstab。使用 blkid 获取 UUID 后添加一行:UUID=xxxx /mnt/data ext4 defaults,nofail 0 2。其中 nofail 选项确保卷不可用时实例仍能正常启动。

EFS 的共享挂载实践

EFS 适合多实例共享同一份数据的场景,例如多个 SageMaker 训练作业共享代码仓库、多个推理容器共享模型文件。EFS 的挂载需要先创建挂载目标(Mount Target),每个可用区一个,然后通过 NFSv4 协议挂载。挂载命令格式为:

sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 \
  fs-0123456789abcdef0.efs.us-east-1.amazonaws.com:/ /mnt/efs

其中 rsize 和 wsize 设置为 1MB 可以提升大文件传输效率,hard 确保 NFS 请求在超时后持续重试而不是返回错误。EFS 的性能模式分为通用型和 Max I/O,前者延迟更低,后者吞吐更高但延迟略高。对于训练场景,如果主要是顺序读取大文件,Max I/O 更合适;如果涉及大量元数据操作(如遍历目录),通用型更优。

FSx for Lustre 与 S3 的联动

FSx for Lustre 的核心价值在于两点:一是提供亚毫秒级延迟和数百 GB/s 的聚合带宽,二是支持与 S3 建立数据仓库链接。创建文件系统时指定 S3 桶路径,Lustre 会自动将 S3 对象的元数据导入,训练任务首次访问某个文件时,Lustre 从 S3 按需加载数据到文件系统;训练结束后,可以通过 hsm_archive 命令将结果回写到 S3。

挂载 Lustre 文件系统的命令如下:

sudo mount -t lustre -o noatime,flock \
  fs-0123456789abcdef0.fsx.us-east-1.amazonaws.com@tcp:/fsx /mnt/fsx

其中 flock 选项启用文件锁,对于 PyTorch DistributedDataParallel 等需要文件锁的框架是必需的。挂载后,训练脚本可以直接将 /mnt/fsx 作为数据目录,框架会以本地文件系统的方式访问数据,而底层由 Lustre 负责从 S3 拉取。

选型决策与组合策略

实际生产环境中,四类存储往往组合使用。一个典型的分布式训练流水线可能是:原始数据存放在 S3 数据湖中,通过 DataSync 或 Lustre 链接同步到 FSx for Lustre;训练实例挂载 Lustre 作为主数据源,同时挂载 EBS 作为本地缓存和临时目录;检查点和最终模型写入 EFS 或直接回写 S3。这种组合兼顾了 S3 的低成本持久化、Lustre 的高吞吐、EBS 的低延迟和 EFS 的共享便利。

选型时优先回答三个问题:数据是否需要被多个实例同时访问?访问模式是顺序大文件还是随机小文件?训练任务对延迟和吞吐的敏感度如何?答案会直接指向 S3、EBS、EFS 或 FSx for Lustre 中的一种或多种组合。
EBS 卷在同一时刻只能挂载到一个 EC2 实例(io2 多挂载除外),不要尝试将同一块 EBS 卷同时挂载到多个训练节点,否则会导致文件系统损坏。多节点共享必须使用 EFS 或 FSx for Lustre。