2.4 训练作业的 Operator 范式:从 Job 到训练 CRD


文档摘要

2.4 训练作业的 Operator 范式:从 Job 到训练 CRD 一个分布式训练不是「一个 Pod」,而是「一群互相绑定、同生共死的 Pod」——K8s 原生的 Job 不懂这种「合唱团」语义,于是有了 Operator:用一份声明式 CRD 把训练作业的复杂生命周期交给控制器自动驱动。 2.4.1 为什么普通 Job 不够用 K8s 原生的 Job 资源能管理「跑完即退出」的批处理任务,比如跑一个数据转换脚本。看起来训练也是「跑完退出」,似乎用 Job 就够了。但分布式训练有几个普通 Job 难以表达的特性: 多角色协作:一个分布式训练有 master、worker、parameter server 等多种角色,每个角色是一组 Pod,普通 Job 只能管一组同构 Pod。

2.4 训练作业的 Operator 范式:从 Job 到训练 CRD

一个分布式训练不是「一个 Pod」,而是「一群互相绑定、同生共死的 Pod」——K8s 原生的 Job 不懂这种「合唱团」语义,于是有了 Operator:用一份声明式 CRD 把训练作业的复杂生命周期交给控制器自动驱动。

2.4.1 为什么普通 Job 不够用

K8s 原生的 Job 资源能管理「跑完即退出」的批处理任务,比如跑一个数据转换脚本。看起来训练也是「跑完退出」,似乎用 Job 就够了。但分布式训练有几个普通 Job 难以表达的特性:

  1. 多角色协作:一个分布式训练有 master、worker、parameter server 等多种角色,每个角色是一组 Pod,普通 Job 只能管一组同构 Pod。
  2. 同生共死(Gang 语义):分布式训练要求所有 worker 同时启动、同时就绪,否则通信初始化会卡死。普通 Job 没有「全部调度成功或全部不调度」的语义。
  3. 复杂状态机:训练有 Created、Running、Restarting、Succeeded、Failed 等状态,还要处理检查点恢复、worker 失败重试、master 切换等复杂逻辑。
  4. 框架特有配置:PyTorch 要传 WORLD_SIZERANK;MPI 要配 hostfile;DeepSpeed 要注入启动脚本——每种框架有不同的启动约定。

下表对比普通 Job 与训练需求的差距:

训练需求 普通 Job 差距
多角色(master/worker) 不支持
Gang 调度(全部成功或全失败) 不支持
框架启动脚本注入 不支持
检查点恢复 手工
状态机与失败重试 简单重试

2.4.2 Operator 范式:把领域知识编码进控制器

Operator 模式 是 CoreOS 在 2016 年提出的 K8s 扩展范式,核心思想是:把人类运维专家的领域知识,编码成一个自定义控制器(Custom Controller),让它自动管理一类复杂应用的声明式 CRD(Custom Resource Definition)。

Operator 的两大组成:

  1. CRD(Custom Resource Definition):定义一类新资源,比如 PyTorchJobMPIJob
  2. Controller(控制器):一个常驻进程,持续 watch CRD 的实例变化,驱动实际状态向声明状态收敛。

Operator 的工作回路是经典的「Reconcile(调谐)」循环:控制器周期性地比较「用户声明的目标状态」与「集群里的实际状态」,发现有差距就采取行动消除差距。例如:

  • 用户声明 PyTorchJob 要 4 个 worker,但只有 3 个 Running → 控制器创建第 4 个。
  • 某个 worker Pod 挂了 → 控制器重新创建,并通知其他 worker 等待。
  • 所有 worker 完成 → 控制器把 PyTorchJob 状态置为 Succeeded

💡 判读:Operator 的本质是「领域知识自动化」。传统运维里靠人记忆的「分布式训练怎么启动、失败了怎么办」被编码进控制器,变成可版本化、可演进的代码。这也是云原生「声明式 + 自动化」哲学的极致体现。

2.4.3 Kubeflow Training Operator:训练 CRD 全家桶

Kubeflow Training Operator(前身是 tf-operatorpytorch-operator 的合并项目)是 K8s 上承载训练作业的事实标准。它提供了一系列针对不同框架的 CRD:

CRD 对应框架 典型场景
PyTorchJob PyTorch DDP PyTorch 分布式数据并行训练
MPIJob MPI / Horovod Horovod、跨框架 AllReduce
DeepSpeedJob DeepSpeed 大模型 ZeRO 训练
TFJob TensorFlow TF 分布式训练
XGBoostJob XGBoost 分布式树模型
MXNetJob MXNet MXNet 分布式训练

每个 CRD 都遵循相似的 schema:声明 master/worker 的 replica 数、每个 replica 的 template(镜像、资源、命令)。一个 PyTorchJob 的伪声明:

apiVersion: kubeflow.org/v1 kind: PyTorchJob metadata: name: resnet50-train spec: pytorchReplicaSpecs: Master: replicas: 1 template: spec: containers: - name: pytorch image: my-trainer:v1 resources: limits: nvidia.com/gpu: 1 Worker: replicas: 7 template: spec: containers: - name: pytorch image: my-trainer:v1 resources: limits: nvidia.com/gpu: 1

这份声明表达了「我要跑一个 1 master + 7 worker 的 PyTorchJob,每个 Pod 1 张 GPU」。Training Operator 会自动:

  • 创建 1 个 master Pod + 7 个 worker Pod。
  • 注入 WORLD_SIZE=8RANK=0..7MASTER_ADDR 等环境变量。
  • 等待所有 Pod 就绪后再让训练开始(Gang 启动)。
  • 监控 Pod 健康,失败时按策略重试或清理。
  • 训练完成后把 CR 状态置为 Succeeded

2.4.4 三种主流训练 CRD 的辨析

PyTorchJobMPIJobDeepSpeedJob 是最常用的三种,它们的关系与适用场景需要辨析清楚。

PyTorchJob:PyTorch 原生分布式

  • 底层协议:PyTorch 的 DDP(DistributedDataParallel)与 torchrun 启动器。
  • 角色模型:Master(rank 0)+ Workers(rank 1..N)。
  • 典型场景:PyTorch 写的训练脚本,用 DDP 做数据并行。
  • 优点:与 PyTorch 生态原生融合,配置最简单。
  • 限制:主要面向数据并行,复杂的张量并行/流水线并行需要额外协调。

MPIJob:Horovod 与跨框架 AllReduce

  • 底层协议:MPI(Message Passing Interface)+ SSH 或 OpenMPI。
  • 角色模型:Launcher(启动器)+ Workers。
  • 典型场景:Horovod 训练(TensorFlow/Keras/PyTorch 通用),或需要 MPI 语义的 HPC 任务。
  • 优点:Horovod 一套 API 通吃多框架;MPI 是 HPC 标准协议。
  • 限制:Launcher 模式略复杂,新项目逐渐转向 PyTorchJob 或 DeepSpeedJob。

DeepSpeedJob:大模型 ZeRO 训练

  • 底层协议:DeepSpeed 启动脚本 deepspeed_launcher
  • 角色模型:Master + Workers(多机多卡)。
  • 典型场景:用 DeepSpeed 的 ZeRO-1/2/3 训练大模型(详见第 5 章)。
  • 优点:原生支持 ZeRO 显存切分、混合精度、检查点,大模型训练首选。
  • 限制:绑定 DeepSpeed 框架,灵活性略低。
选型维度 PyTorchJob MPIJob DeepSpeedJob
训练框架 PyTorch 任意(Horovod) DeepSpeed + PyTorch
并行策略 数据并行 数据并行 数据并行 + ZeRO
大模型友好 一般 一般
配置复杂度
2024 后趋势 主流 收缩 大模型标配

💡 选型经验:新项目用 PyTorch 做数据并行 → PyTorchJob;要训百亿以上大模型 → DeepSpeedJob;老项目用 Horovod → MPIJob。三者的底层都是把训练脚本跑在 K8s 编排的 Pod 群上,区别在于启动脚本与角色约定。

2.4.5 Operator 与调度器的分工

一个常见的混淆是:Operator 和调度器都管 Pod,它们怎么分工

  • 调度器(Scheduler) 管「Pod 应该去哪个节点」——基于资源、亲和、污点打分。
  • Operator(Controller) 管「该创建/删除哪些 Pod、它们的状态如何」——基于 CRD 声明做 Reconcile。

二者协作:Operator 创建 Pod → 调度器决定 Pod 落到哪台节点 → Pod 运行 → Operator 监控 Pod 状态。Operator 不直接决定节点,调度器不直接创建 Pod。

但分布式训练有个特殊诉求——Gang Scheduling(成组调度):「我要 8 个 worker,要么 8 个全部调度成功,要么一个都不调度」。原生调度器是逐个调度 Pod 的,可能出现「调度了 5 个,剩 3 个资源不够,前 5 个干等」的死锁。这时需要 Volcano、Kueue 等批处理调度器配合,做 Gang 调度。第 3 章会详细讲。

2.4.6 Operator 范式的更广意义

Operator 不只为训练服务。云原生 AI 生态里很多组件都是 Operator:

  • KServe Controller:管理 InferenceService CRD,自动部署推理服务(第 6 章)。
  • Kueue Controller:管理 WorkloadClusterQueue,做作业排队(第 3 章)。
  • Ray Operator:管理 RayClusterRayJob,部署 Ray 集群(第 5 章)。
  • Cert-Manager、Prometheus Operator 等:基础设施也用 Operator 模式。

掌握 Operator 范式,等于掌握了 K8s 上承载一切复杂应用的标准方法论。这也是为什么本章把 Operator 作为「从 K8s 基础设施走向 AI 工作负载」的过渡——后续章节你会反复看到它的身影。

2.4.7 第 2 章小结与第 3 章预告

到这里,第 2 章的四节构成了一条完整的「K8s 承载 AI」地基链:

  1. AI 负载特性与挑战(2.1):知道 AI 要什么。
  2. GPU 资源建模(2.2):让 K8s 认识、调度、注入 GPU。
  3. 异构资源管理(2.3):用节点池+标签+污点管好多型号集群。
  4. Operator 范式(2.4):用 CRD 承载训练作业的复杂生命周期。

但这套地基仍是「独占模式」——一张 GPU 给一个 Pod。第 3 章将在这套地基上叠加:GPU 共享(MIG/MPS)、拓扑感知调度、批处理调度器、弹性训练,把 GPU 利用率从 30% 推向 80%+。

本节小结

  • 普通 Job 不懂分布式训练的「多角色、Gang 启动、复杂状态机」语义,需要 Operator 范式。
  • Operator = CRD + Controller,核心是 Reconcile 回路:把领域知识编码成持续调谐的控制器。
  • Kubeflow Training Operator 提供了 PyTorchJob、MPIJob、DeepSpeedJob 等训练 CRD,是 K8s 上训练作业的事实标准。
  • PyTorchJob 适合 PyTorch 数据并行、DeepSpeedJob 适合大模型 ZeRO 训练、MPIJob 适合 Horovod,三者底层都是 Pod 群编排。
  • Operator 管「创建哪些 Pod、状态如何」,调度器管「Pod 去哪个节点」,二者协作;Gang 调度需批处理调度器配合。
  • Operator 范式是 K8s 承载一切复杂应用的标准方法论,后续章节(KServe、Kueue、Ray)都会反复出现。

第 2 章完结。下一章《第 3 章 共享 GPU、拓扑感知与高级调度策略》将在这套地基上深入 GPU 共享与拓扑优化。


发布者: 作者: 灏天文库 转发
评论区 (0)
U