这一节我们真正把:
python train.py
变成:
Kubernetes
→ Kubeflow Trainer
→ TrainJob
→ Pod
→ PyTorch
先跑 CPU,不碰 GPU。
目前官方安装文档给出的稳定 Trainer 版本是 v2.1.0,要求 Kubernetes 和 kubectl ≥ 1.31;官方也直接推荐没有集群时使用 Kind 或 Minikube。 Kubeflow培训师
1. 先确认本地 Kubernetes
上一节你已经知道需要 Docker、Kind、kubectl。
执行:
css
docker --version
kubectl version --client
kind version
如果还没创建 Kind 集群:
lua
kind create cluster
然后:
arduino
kubectl get nodes
目标是看到:
NAME STATUS
kind-control-plane Ready
这里实际上已经完成:
Mac
↓
Docker
↓
Kind Node
↓
Kubernetes
注意,此时还没有 Kubeflow。
2. 安装 Helm
当前官方 Trainer 安装方式之一就是 Helm。
Mac 上如果没有:
brew install helm
检查:
helm version
然后安装 Kubeflow Trainer:
arduino
export VERSION=v2.1.0
helm install kubeflow-trainer \
oci://ghcr.io/kubeflow/charts/kubeflow-trainer \
--namespace kubeflow-system \
--create-namespace \
--version ${VERSION#v} \
--wait
kubectl apply --server-side -k \
"https://github.com/kubeflow/trainer.git/manifests/overlays/runtimes?ref=v2.1.0"
最后这个:
lua
kubectl apply --server-side -k \
"https://github.com/kubeflow/trainer.git/manifests/overlays/runtimes?ref=v2.1.0"
很重要。
它除了安装 Trainer Controller,还安装默认的 ClusterTrainingRuntime,这样我们后面可以直接使用现成的 PyTorch Runtime。官方当前安装文档支持这种方式。 Kubeflow培训师
3. 检查安装结果
执行:
sql
kubectl get pods -n kubeflow-system
应该能看到类似:
jobset-controller-manager
kubeflow-trainer-controller-manager
状态最终应该是:
sql
Running
官方也是通过这两个 Controller Pod 来验证 Trainer 控制面的安装。 Kubeflow培训师
再执行:
arduino
kubectl get clustertrainingruntime
这里应该能看到默认 Runtime。
例如其中会有 Torch 相关 Runtime。
到这里,你的环境已经变成:
Mac
↓
Docker
↓
Kind
↓
Kubernetes
↓
Kubeflow Trainer Controller
↓
ClusterTrainingRuntime
但还没有真正的训练任务。
4. Runtime 到底是什么?
这里停一下,因为这是 Trainer v2 最值得训练平台工程师理解的设计。
你马上要创建:
TrainJob
但 TrainJob 不需要自己描述所有底层 Kubernetes 细节。
它引用:
ClusterTrainingRuntime
关系是:
markdown
Platform Admin
↓
ClusterTrainingRuntime
↓
规定训练任务怎么运行
AI Engineer
↓
TrainJob
↓
描述这一次训练什么
官方对 Runtime 的定位就是可复用的训练模板/蓝图,由平台管理员管理,TrainJob 引用它。 Kubeflow
这其实非常像企业训练平台的设计。
平台团队提前准备:
objectivec
PyTorch Runtime
DeepSpeed Runtime
MPI Runtime
...
用户只提交:
我要用 PyTorch Runtime
跑这个 Image
执行这个 Command
需要这些资源
5. 提交我们的第一个 TrainJob
我们先不用自己的 MNIST Image。
原因很简单:如果现在同时引入:
css
Dockerfile
Image Build
Kind load
MNIST Code
TrainJob
一次变量太多。
先用 Kubeflow 官方示例镜像,把 TrainJob 链路跑通。
官方 Trainer v2 的典型 TrainJob 是这样的结构:runtimeRef + trainer + image + command。 Kubeflow培训师
创建:
yaml
cat > mnist-trainjob.yaml <<'EOF'
apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainJob
metadata:
name: mnist-demo
spec:
runtimeRef:
name: torch-distributed
kind: ClusterTrainingRuntime
apiGroup: trainer.kubeflow.org
trainer:
numNodes: 1
image: docker.io/kubeflowkatib/pytorch-mnist:v1beta1-45c5727
command:
- python3
- /opt/pytorch-mnist/mnist.py
- --epochs=1
EOF
然后:
kubectl apply -f mnist-trainjob.yaml
如果创建成功:
bash
trainjob.trainer.kubeflow.org/mnist-demo created
不过这里提醒一下:Kubeflow Trainer 近期 API 正在持续演进,例如 v2.2 已引入 v2alpha1 和 breaking changes,因此如果你本机安装的不是这里使用的 v2.1.0,YAML 字段要跟对应版本文档走。 Kubeflow
6. 这一条 apply 背后发生了什么?
这是本节最关键的地方。
你执行:
kubectl apply -f mnist-trainjob.yaml
第一步,Kubernetes API Server 收到:
ini
kind = TrainJob
name = mnist-demo
但普通 Kubernetes 本身不知道:
TrainJob
是什么。
为什么现在它认识?
因为我们刚才安装 Trainer 时,同时安装了:
CRD
也就是:
CustomResourceDefinition
Trainer 给 Kubernetes 增加了新的资源类型:
TrainJob
TrainingRuntime
ClusterTrainingRuntime
官方 Helm Chart 默认会安装这些 CRD。 Kubeflow培训师
所以:
diff
原生 Kubernetes
Pod
Deployment
Job
Service
...
安装 Trainer 后
+
TrainJob
TrainingRuntime
ClusterTrainingRuntime
这就是 Kubernetes Operator 体系非常核心的思想。
7. Controller 开始工作
Trainer Controller 会监听:
TrainJob
发现:
mnist-demo
然后读取:
yaml
runtimeRef:
name: torch-distributed
相当于:
我要使用平台预先定义好的 Torch Runtime。
然后结合:
yaml
trainer:
numNodes: 1
image: ...
command: ...
生成真正的 Kubernetes 工作负载。
最终 Kubernetes 创建:
Pod
并运行这个 Image。
完整链路变成:
arduino
kubectl apply
↓
Kubernetes API
↓
TrainJob
↓
Kubeflow Trainer Controller
↓
读取 ClusterTrainingRuntime
↓
生成训练工作负载
↓
Kubernetes Scheduler
↓
Pod
↓
Container
↓
python3 mnist.py
↓
PyTorch
这条链是你作为训练平台工程师真正应该掌握的。
8. 看任务,而不是盯 YAML
执行:
arduino
kubectl get trainjob
然后:
arduino
kubectl get pods
你应该能看到 TrainJob 最终产生的 Pod。
可以持续观察:
arduino
kubectl get pods -w
Pod 状态可能经历:
sql
Pending
↓
ContainerCreating
↓
Running
↓
Completed
这其实就是训练平台页面里的:
创建中
排队中
运行中
成功
底层来源之一。
9. 看训练日志
找到对应 Pod 后:
xml
kubectl logs <pod-name>
你最终看到的还是熟悉的:
erlang
Train Epoch ...
Loss ...
Accuracy ...
这一刻非常关键。
因为我们绕了一大圈:
Kubeflow
Kubernetes
CRD
Controller
Runtime
TrainJob
Pod
Container
最终里面干的还是:
Dataset
↓
Forward
↓
Loss
↓
Backward
↓
Optimizer
也就是我们第一阶段学的东西。
10. 把 YAML 和训练平台页面一一对应
现在再看:
yaml
trainer:
numNodes: 1
相当于训练平台上的:
节点数量 = 1
arduino
image:
对应:
运行镜像
bash
command:
对应:
启动命令
makefile
runtimeRef:
可以对应:
训练框架 / Runtime 模板
以后页面上可能不会把:
ClusterTrainingRuntime
这个 Kubernetes 名字直接暴露给用户。
可能包装成:
PyTorch 2.x
DeepSpeed
LLM Training Runtime
但底层思路是一致的。
11. 为什么 Runtime 设计对平台很有价值?
假设没有 Runtime。
100 个用户,每次都得知道:
bash
JobSet
Pod Template
Torch distributed env
restart policy
network
launcher
...
这显然不适合训练平台。
有 Runtime:
swift
平台管理员
定义一次:
torch-distributed
↓
包含基础设施模板
普通用户
TrainJob A → torch-distributed
TrainJob B → torch-distributed
TrainJob C → torch-distributed
于是平台把:
基础设施复杂度
从算法/训练用户那里拿走了。
这就是 Trainer v2 的一个核心设计目标:把基础设施配置和具体训练任务定义分开。 Kubeflow
这和你以后理解企业内部训练平台会非常接近。
本节总结
今天我们第一次真正把:
python train.py
变成了:
TrainJob
↓
Runtime
↓
Trainer Controller
↓
Kubernetes workload
↓
Pod
↓
Container
↓
python train.py
↓
PyTorch
其中你需要真正记住四个东西:
TrainJob 表示"一次训练任务"。
ClusterTrainingRuntime / TrainingRuntime 表示"平台预先准备的训练运行模板"。
Trainer Controller 负责把 TrainJob + Runtime 转换成真正可运行的 Kubernetes 工作负载。
Pod / Container 才是最后真正执行 python train.py 的地方。
所以训练平台用户提交的:
训练任务
最终一定会逐渐落到非常具体的东西:
diff
程序
+
环境
+
资源
+
数据
+
启动命令