把 AI/ML 工作负载搬进云原生基础设施,真正难的往往不是把容器跑起来,而是让数据持续、稳定、高吞吐地喂给计算资源。CNCF 关于云原生 AI 数据存储的白皮书聚焦的正是这个问题:AI/ML 是数据密集、强状态的工作负载,规模化部署时很容易被存储、网络和数据生命周期管理拖住。
AI 工作负载为什么不像普通微服务
传统无状态服务通常关心请求延迟、弹性扩缩容和故障恢复。AI/ML 工作负载还要额外处理几类“重数据”:
- 训练数据集:可能是 TB 到 PB 级对象、图片、文本、日志或特征数据。
- 模型检查点:训练过程中频繁写入,用于恢复、调参和版本管理。
- 中间产物:特征缓存、预处理结果、评估输出。
- 推理数据:在线请求、批处理输入、向量索引或模型权重。
这些数据不是容器重启后可以丢弃的临时状态。训练任务如果因为存储吞吐不足而让 GPU 空转,成本会直接放大;检查点写入如果不可靠,长时间训练失败后的恢复成本也会很高。
云原生存储需要同时满足三件事
在 Kubernetes 上跑 AI/ML,存储设计通常要同时回答三个问题。
第一,数据在哪里。对象存储适合大规模数据集和归档,块存储适合单节点高性能读写,文件存储适合多 Pod 共享访问。AI 平台经常需要组合使用,而不是押注一种介质。
第二,数据怎么靠近计算。训练任务经常以短生命周期 Pod 的形式调度,如果每次都从远端拉取完整数据集,启动时间和网络成本会失控。缓存、预取、数据分片和拓扑感知调度会变得重要。
第三,状态如何治理。模型、数据集和检查点都需要版本、权限、保留策略和可追溯性。云原生给了我们声明式 API 和自动化控制面,但也要求团队把存储策略写进平台能力,而不是靠人工约定。
可以这样实践:给训练任务声明持久卷和检查点目录
下面是一个可以改造的 Kubernetes 示例。它展示了一个训练 Job 如何挂载持久卷,把模型检查点写到稳定路径。你需要根据自己的集群替换 storageClassName、镜像地址和训练命令。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ai-checkpoints-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 200Gi
---
apiVersion: batch/v1
kind: Job
metadata:
name: image-training-job
spec:
backoffLimit: 2
template:
spec:
restartPolicy: Never
containers:
- name: trainer
image: ghcr.io/example/ml-trainer:latest
command:
- python
- train.py
- --data-dir=/mnt/dataset
- --checkpoint-dir=/mnt/checkpoints
resources:
limits:
nvidia.com/gpu: "1"
cpu: "8"
memory: 32Gi
requests:
cpu: "4"
memory: 16Gi
volumeMounts:
- name: checkpoints
mountPath: /mnt/checkpoints
- name: dataset
mountPath: /mnt/dataset
readOnly: true
volumes:
- name: checkpoints
persistentVolumeClaim:
claimName: ai-checkpoints-pvc
- name: dataset
persistentVolumeClaim:
claimName: ai-dataset-pvc
应用前可以先检查当前集群有哪些 StorageClass:
kubectl get storageclass
kubectl apply -f training-storage.yaml
kubectl get job image-training-job
kubectl logs job/image-training-job -f
这个例子不等于完整 AI 平台,但它抓住了一个关键原则:训练代码不应该把检查点写在容器本地文件系统里。Pod 被重调度、节点故障或任务重试时,本地状态很容易丢失。
可以这样实践:把数据准备阶段和训练阶段拆开
数据瓶颈经常不是训练代码本身造成的,而是所有任务同时从远端读取、解压、转换。可以把数据准备拆成单独 Job,先把预处理结果落到共享卷或对象存储,再让训练任务读取稳定格式。
apiVersion: batch/v1
kind: Job
metadata:
name: prepare-training-data
spec:
template:
spec:
restartPolicy: Never
containers:
- name: prepare
image: python:3.12-slim
command:
- sh
- -c
- |
python - <<'PY'
from pathlib import Path
out = Path('/mnt/prepared')
out.mkdir(parents=True, exist_ok=True)
(out / 'manifest.txt').write_text('sample-001.jpg\nsample-002.jpg\n')
print('prepared dataset manifest')
PY
volumeMounts:
- name: prepared-data
mountPath: /mnt/prepared
volumes:
- name: prepared-data
persistentVolumeClaim:
claimName: ai-prepared-data-pvc
实际环境里,这个阶段可以做格式转换、索引生成、样本清洗或特征计算。重点是让昂贵的数据准备动作可复用、可观测、可重试,而不是散落在每个训练容器的启动脚本里。
选型时别只看容量
AI/ML 存储选型很容易从“够不够大”开始,但生产环境更应该问这些问题:
- 吞吐是否能跟上 GPU 消耗数据的速度?
- 多个训练任务并发读取同一数据集时,延迟会不会抖动?
- 检查点写入失败时,任务是失败、重试,还是产生不完整文件?
- 数据集、模型和检查点有没有版本与保留策略?
- 存储权限是否和 Kubernetes 身份、命名空间、团队边界匹配?
- 跨区域、跨集群训练时,复制成本和一致性边界是否清楚?
落地建议
不要把 AI/ML 云原生化理解成“给训练脚本套一个容器”。更稳妥的路径是先识别数据流:数据集从哪里来,预处理在哪里发生,训练如何读取,检查点写到哪里,模型如何交付给推理服务。
小规模阶段可以用 PVC、对象存储和简单 Job 编排把路径跑通;规模变大后,再引入缓存、数据版本管理、拓扑调度和更细的权限治理。核心目标始终是一个:让计算资源等数据的时间越来越少,让状态恢复和数据追踪越来越确定。