【AIaaS 全栈架构师】第 30 篇:Kubeflow Training Operator——K8s 上的分布式训练调度
系列定位:AIaaS 全栈架构师教程,技术栈以 Go 为主。本篇聚焦 K8s 如何用 Operator 模式管理分布式训练任务,以及 Gang Scheduling 如何解决 all-or-nothing 难题。
本篇你将学到
- 为什么分布式训练任务不能用普通 Deployment/Job 管理
- Training Operator 的控制器架构与 CRD 设计
- PyTorchJob / TensorFlowJob CRD 的字段含义
- 分布式训练任务的提交、状态流转、日志收集
- Gang Scheduling 与 Volcano 的必要性
- PyTorchJob 与 Ray Train、Kueue 的对比
上一篇我们解决了 GPU 共享。但分布式训练任务还有一个独特需求:all-or-nothing。一个 16 卡训练任务必须同时拿到 16 张 GPU 才能启动,否则就会出现「3 个 worker 已经起来,13 个还在 Pending,前面 3 个空耗 GPU」的死锁。原生的 K8s Job/Deployment 完全没有这种语义。Kubeflow Training Operator 就是为这个场景而生的。
一、为什么需要专门的训练任务控制器
1.1 分布式训练的「整批」语义
先看一个 PyTorch DDP(DistributedDataParallel)训练任务的标准启动姿势。它有 N 个 worker 进程,每个进程:
- 初始化进程组(
torch.distributed.init_process_group),通过 rendezvous 互相发现。 - 等所有 rank 到齐,初始化 NCCL 通信。
- 进入训练循环,每个 step 做 AllReduce 同步梯度。
如果只有一个 worker 先启动,它会卡在 init_process_group,等其他 rank。这个等待不是无所谓的——它占着 GPU,可能等几十分钟甚至超时失败。问题严重性:
任务: 16 个 worker, 每个占 1 张 A100
集群空闲 GPU: 10
方案A: 用 K8s Job(副本=16)
- 调度器先后调度 10 个 worker 到有 GPU 的节点
- 剩下 6 个 Pending
- 已起 10 个 worker 卡在 init_process_group 等 6 个不会到的同伴
- 等待 30 分钟超时 → CrashLoopBackOff → 释放 GPU
- 期间 10 张 GPU 完全空转
方案B: 用 K8s Job(副本=16)+ 别的任务
- 任务 X 抢到 3 张 GPU(剩余 7)
- 任务 Y 抢到 3 张 GPU(剩余 4)
- 三个任务谁都不够 16 张,全部卡死 → 集群利用率 0%
这就是 分布式训练的死锁问题。解决之道是 Gang Scheduling(成组调度):要么所有 worker 一起调度,要么一个都不调度。
1.2 原生工作负载控制器的不足
| K8s 原生对象 | 训练场景下的不足 |
|---|---|
| Deployment | 不支持 Pod 间通信拓扑,副本滚动会破坏训练一致性 |
| Job | 缺少 Gang 语义,副本可被部分调度 |
| CronJob | 周期触发,但单次执行仍是 Job |
| StatefulSet | 有序但无 Gang,且不针对分布式训练任务语义 |
训练任务还有一堆特殊语义:worker/ps(parameter server)/chief/scheduler 不同角色、不同镜像、不同资源;要注入 WORLD_SIZE、RANK、MASTER_ADDR 等环境变量;要管理 rendezvous endpoint;要做 checkpoint 续训。这些都需要一个 领域专用控制器。
1.3 Operator 模式:声明式 + 控制循环
Kubeflow Training Operator 采用 K8s 的 Operator 模式:
- 用户声明一个 CR(Custom Resource),描述训练任务期望的样子。
- Operator(一个 Controller 进程)watch 这类 CR。
- Operator 根据期望状态创建/更新 Pod、Service、ConfigMap。
- 不断 reconcile,直到实际状态收敛到期望状态。
声明式 + 控制循环 = 用户只描述「我要什么」,Operator 自动算「怎么做到」。
二、Training Operator 架构
2.1 整体架构
Training Operator 本身是一个 Deployment,跑在 K8s 控制面。它内部为每种框架注册一个 Controller:PyTorchJob、TensorFlowJob、MPIJob、JAXJob、PaddlePaddleJob 等。所有 Controller 共享一套通用逻辑(Pod 创建、Service 注入、Status 上报),只是各框架的启动命令、环境变量注入策略不同。
2.2 通用 reconcile 流程
无论哪个框架,Controller 的核心 reconcile 逻辑都是:
关键设计点:
- headless Service:每个 worker Pod 对应一个 headless Service(同名的 DNS 记录),让其他 worker 可以通过
<pod-name>.<service-name>找到它。这是 PyTorchinit_process_group(backend=nccl)的MASTER_ADDR通常指向的地址。 - 环境变量注入:Operator 在创建 Pod 时自动计算并注入
WORLD_SIZE、RANK、MASTER_ADDR、MASTER_PORT,用户 Python 代码直接os.environ读取。 - 状态机管理:CR 的
.status.conditions字段记录 Created → Running → Succeeded/Failed 状态流转。 - 重启策略:单个 worker 失败时按
restartPolicy决定是否重启整个 Job 或仅重启失败 Pod。
2.3 Training Operator 安装
用 Helm 一行安装:
helm repo add kubeflow https://www.kubeflow.org/experimental/training-operator
helm repo update
helm install training-operator kubeflow/training-operator \
--namespace kubeflow \
--create-namespace \
--set webhook.port=8443
安装后检查 CRD 是否注册:
$ kubectl get crd | grep kubeflow.org
mpijobs.kubeflow.org
paddlepaddlejobs.kubeflow.org
pytorchjobs.kubeflow.org
tfjobs.kubeflow.org
xgboostjobs.kubeflow.org
三、PyTorchJob CRD 详解
3.1 最简 PyTorchJob
PyTorchJob 是 LLM 训练用得最多的 CRD。一个最小的 16 卡 DDP 训练任务:
apiVersion: kubeflow.org/v1
kind: PyTorchJob
metadata:
name: llama3-8b-finetune
namespace: training
spec:
pytorchReplicaSpecs:
Worker:
replicas: 4 # 4 个 worker 进程
restartPolicy: OnFailure # worker 失败时重启 Pod
template:
spec:
containers:
- name: pytorch
image: registry.internal/pytorch:2.3.0-cuda12.1
command:
- torchrun
- --nproc_per_node=4 # 每节点 4 卡(4 节点共 16 卡)
- --nnodes=4
- --rdzv_backend=c10d
- --rdzv_endpoint=llama3-8b-finetune-worker-0:29500
- train.py
- --model=meta-llama/Llama-3-8B
- --output=/checkpoints
resources:
limits:
nvidia.com/gpu: 4 # 每个 worker 4 张 GPU
requests:
nvidia.com/gpu: 4
volumeMounts:
- name: data
mountPath: /data
- name: ckpt
mountPath: /checkpoints
volumes:
- name: data
persistentVolumeClaim:
claimName: training-data
- name: ckpt
persistentVolumeClaim:
claimName: checkpoint-pvc
nodeSelector:
nvidia.com/gpu.product: A100-SXM4-40GB
tolerations:
- key: nvidia.com/gpu
operator: Exists
提交后:
kubectl apply -f llama3-pytorchjob.yaml
kubectl get pytorchjob -n training
NAME STATE AGE
llama3-8b-finetune Running 2m
3.2 字段含义详解
pytorchReplicaSpecs 是核心字段,支持两种角色:
| 角色 | 含义 | 典型用法 |
|---|---|---|
| Master | 0 号 worker,特殊角色 | 早期 PyTorch 用,现代 DDP 已弱化 |
| Worker | 所有 worker,等价角色 | 现代 DDP 只用 Worker |
现代 PyTorch 训练只用 Worker,所有 worker 完全对等(rank 0 是约定俗成的 master)。Master 角色为兼容旧代码保留。
每个 ReplicaSpec 包含:
- replicas:该角色副本数。
- restartPolicy:
Always/OnFailure/Never。生产推荐OnFailure,失败重试但成功完成不重启。 - template:PodTemplate,与普通 Pod spec 一致。
3.3 环境变量自动注入
Operator 不需要用户手动写 RANK、WORLD_SIZE。它会自动给每个 Pod 注入:
WORLD_SIZE = sum(all replicas)
RANK = 该 Pod 在所有 replica 中的序号
MASTER_ADDR = worker-0 的 headless Service DNS
MASTER_PORT = 29500 (默认)
容器内 Python 代码:
import os
import torch.distributed as dist
rank = int(os.environ["RANK"])
world_size = int(os.environ["WORLD_SIZE"])
master_addr = os.environ["MASTER_ADDR"]
dist.init_process_group(
backend="nccl",
init_method=f"tcp://{master_addr}:29500",
rank=rank,
world_size=world_size,
)
这种约定大幅减少了训练代码的样板。用户写训练代码时甚至不需要考虑「我在 K8s 里跑」,只要遵循 RANK/WORLD_SIZE/MASTER_ADDR 约定即可。
3.4 完整 CRD 结构
完整的 PyTorchJob CRD 字段(精简):
apiVersion: kubeflow.org/v1
kind: PyTorchJob
metadata:
name: <job-name>
spec:
# 主动清理策略:Job 完成后多久删除底层 Pod
runPolicy:
cleanPodPolicy: Running # Running / All / None
ttlSecondsAfterFinished: 86400 # 完成后保留 24 小时
activeDeadlineSeconds: 86400 # 最长运行 24 小时
backoffLimit: 6 # 最多重试 6 次
schedulingPolicy:
priorityClass: gpu-high
queue: training-queue # Kueue 队列
pytorchReplicaSpecs:
Worker:
replicas: 4
restartPolicy: OnFailure
template:
metadata:
labels:
team: llm-research
spec:
# ... PodSpec
status:
conditions: # 状态流转记录
- type: Created
status: "True"
lastTransitionTime: "..."
- type: Running
status: "True"
lastTransitionTime: "..."
replicaStatuses:
Worker:
active: 4
succeeded: 0
failed: 0
3.5 状态机与条件
Training Operator 通过 .status.conditions 记录 Job 的生命周期:
获取状态详情:
$ kubectl describe pytorchjob llama3-8b-finetune -n training
...
Status:
Conditions:
Last Transition Time: 2026-07-31T10:00:00Z
Reason: PyTorchJobRunning
Status: True
Type: Running
Replica Statuses:
Worker:
Active: 4
四、其他框架的 CRD
4.1 MPIJob
MPIJob 用于基于 MPI 的训练(典型如 Megatron-LM 的 Slurm 风格启动)。结构略复杂,多了一个 Launcher 角色:
apiVersion: kubeflow.org/v1
kind: MPIJob
metadata:
name: megatron-training
spec:
slotsPerWorker: 8 # 每个 worker 8 个 slot(GPU)
runPolicy:
cleanPodPolicy: Running
mpiReplicaSpecs:
Launcher: # 启动器,跑 mpirun
replicas: 1
template:
spec:
containers:
- name: mpi-launcher
image: registry.internal/megatron:latest
command: [/scripts/launch_mpirun.sh]
Worker: # 真正训练节点
replicas: 4
template:
spec:
containers:
- name: mpi-worker
image: registry.internal/megatron:latest
resources:
limits:
nvidia.com/gpu: 8
Launcher 负责调用 mpirun 通过 SSH 把任务派发到 Worker。Worker 节点通过 OpenSSH Server 接受 Launcher 调度。
4.2 TensorFlowJob
TensorFlowJob 主要为 parameter server 架构设计,有 Chief / Worker / PS 三种角色:
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
name: tf-resnet-training
spec:
tfReplicaSpecs:
Chief: # 主 worker,写 checkpoint
replicas: 1
template: ...
Worker: # 普通训练 worker
replicas: 8
template: ...
PS: # parameter server
replicas: 4
template: # 通常用 CPU
spec:
containers:
- name: tensorflow
image: tensorflow/tensorflow:2.15.0-gpu
resources:
limits:
cpu: 16
memory: 64Gi
随着 TensorFlow 对 CollectiveAllReduceStrategy(与 PyTorch DDP 类似)的成熟,PS 角色用得越来越少。但 TFJob 仍保留它以兼容旧代码。
五、Gang Scheduling:训练任务的命脉
5.1 死锁重现
回到开篇的死锁例子。即便用 PyTorchJob,默认调度器仍然是「先到先得」:
默认调度器看到 Pod 就尽量调度,没有「同 Job 的 Pod 一起」的概念。死锁在所难免。
5.2 Gang Scheduling 原理
Gang Scheduling 的核心思想:把一组 Pod 当作一个调度单元(PodGroup),要么全部调度成功,要么都不调度。K8s 上实现 Gang Scheduling 的主流方案是 Volcano(前身 kube-batch)。
Volcano 引入了一个新的 CRD PodGroup,并自己实现了一个调度器。PyTorchJob 创建 Pod 时同时创建一个 PodGroup,声明这些 Pod 必须成组调度:
5.3 启用 Volcano 调度
要让 PyTorchJob 用 Volcano 调度,配置:
apiVersion: kubeflow.org/v1
kind: PyTorchJob
metadata:
name: llama3-finetune-gang
spec:
schedulingPolicy:
schedulerName: volcano # 用 Volcano 调度器
minAvailable: 4 # 必须 4 个 worker 同时调度
pytorchReplicaSpecs:
Worker:
replicas: 4
template:
spec:
schedulerName: volcano # Pod 也指定
containers:
- name: pytorch
resources:
limits:
nvidia.com/gpu: 4
minAvailable 是 Gang Scheduling 的关键参数:
minAvailable: 4表示 4 个 Pod 都能调度时才动手。- 可以小于 replicas(如
minAvailable=3, replicas=4)允许部分冗余。
Volcano 还提供:
- Queue(队列):按租户/团队划分资源池,超配时按优先级排队。
- Preempt(抢占):高优 PodGroup 可抢占低优 PodGroup 的资源。
- Topology Constraint:约束 Pod 调度的拓扑分布(如一个 worker 必须在同一节点)。
5.4 Volcano Queue 与优先级
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: training-queue
spec:
weight: 3 # 该队列权重
reclaimable: true # 允许被抢占
capability:
cpu: 1000
memory: 4Ti
nvidia.com/gpu: 64 # 队列总配额
---
apiVersion: scheduling.volcano.sh/v1beta1
kind: PodGroup
metadata:
name: llama3-job-pg
spec:
minMember: 4 # 4 个 Pod 才能启动
queue: training-queue
priorityClassName: gpu-high
minResources:
nvidia.com/gpu: 16 # 至少需要 16 GPU
队列机制是规模化 AIaaS 平台的资源公平分配基础:
- 不同租户/团队有独立 Queue,按 weight 分集群资源。
- 单个 Queue 内多个 PodGroup 按 priority 排队。
- 跨 Queue 抢占保证高优 Queue 的 SLA。
5.5 Kueue:新一代批调度器
除 Volcano 外,K8s SIG 又推出了 Kueue,定位更轻量,专为批处理任务设计:
| 维度 | Volcano | Kueue |
|---|---|---|
| 定位 | 全功能批调度器 | 排队 + 准入控制器 |
| 是否替换默认调度器 | 是(自带调度器) | 否(用默认调度器) |
| Gang Scheduling | 原生支持 | 通过 COSI/集成 |
| 与 K8s 集成 | 重,自带 RBAC/CRD | 轻,更接近 K8s 原生 |
| 推荐场景 | 大规模多租户 | K8s 1.27+ 上的现代部署 |
Kueue 的核心是 ClusterQueue 和 LocalQueue 两个 CRD,配合默认调度器实现公平排队和配额借用,不需要替换调度器。Kubeflow Training Operator v1.7+ 原生支持 Kueue。
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: gpu-cluster-queue
spec:
namespaceSelector: {}
resourceGroups:
- coveredResources: ["nvidia.com/gpu", "cpu", "memory"]
flavors:
- name: gpu-a100
resources:
- name: nvidia.com/gpu
nominalQuota: 64
- name: cpu
nominalQuota: 2048
六、训练日志与状态监控
6.1 Pod 日志查看
每个 worker 都是一个 Pod,独立查日志:
# 列出所有 worker
kubectl get pods -n training -l job-name=llama3-8b-finetune
NAME READY STATUS RESTARTS
llama3-8b-finetune-worker-0 1/1 Running 0
llama3-8b-finetune-worker-1 1/1 Running 0
llama3-8b-finetune-worker-2 1/1 Running 0
llama3-8b-finetune-worker-3 1/1 Running 0
# 单个 worker 日志
kubectl logs -n training llama3-8b-finetune-worker-0 -f
# 同时看所有 worker(用 stern 之类的工具)
stern -n training llama3-8b-finetune-worker --tail 100
6.2 训练指标采集
训练任务的 metrics 不在 Prometheus 默认抓取范围,需要业务侧主动暴露:
- PyTorch Lightning / HuggingFace Trainer:内置 TensorBoard / W&B / MLflow 集成,写到 PVC 或外部服务。
- 自定义 metrics exporter:训练脚本里
prometheus_client.start_http_server(8000),由 Prometheus 抓取。
典型的训练指标暴露片段:
from prometheus_client import Gauge, start_http_server
# 定义指标
loss_gauge = Gauge('training_loss', 'Current training loss', ['step'])
lr_gauge = Gauge('training_learning_rate', 'Current learning rate')
gpu_mem_gauge = Gauge('training_gpu_memory_allocated_bytes', 'GPU memory allocated')
# 启动 metrics server
start_http_server(8000)
# 训练循环中
for step, batch in enumerate(dataloader):
loss = train_step(batch)
loss_gauge.labels(step=step).set(loss.item())
lr_gauge.set(get_lr())
gpu_mem_gauge.set(torch.cuda.memory_allocated())
Prometheus 抓取配置(针对训练 Pod):
- job_name: 'training-jobs'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_job-name]
regex: .+
action: keep
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
metrics_path: /metrics
6.3 GPU 利用率监控
每个 worker Pod 的 GPU 利用率通过 DCGM Exporter 采集:
# 按 Pod 维度查 GPU 利用率
DCGM_FI_DEV_GPU_UTIL{pod=~"llama3-8b-finetune-worker.*"}
# 按 Job 聚合
avg(DCGM_FI_DEV_GPU_UTIL{pod=~"llama3.*"}) by (pod)
# 整个集群的 GPU 利用率分布
histogram_quantile(0.95, sum(rate(DCGM_FI_DEV_GPU_UTIL_bucket[5m])) by (le))
如果训练任务 GPU 利用率长期低于 70%,通常是数据 pipeline 没跟上或通信瓶颈,需要排查 DataLoader num_workers / NVLink 拓扑。
6.4 Checkpoint 管理
分布式训练必须支持 checkpoint 续训。一个生产可用的 checkpoint 策略:
# 在训练脚本中(伪代码)
def save_checkpoint(step, model, optimizer, epoch):
ckpt_path = f"/checkpoints/step-{step}.pt"
# DDP 下只有 rank 0 写
if dist.get_rank() == 0:
torch.save({
'step': step,
'epoch': epoch,
'model_state': model.state_dict(),
'optimizer_state': optimizer.state_dict(),
'rng_state': torch.get_rng_state(),
}, ckpt_path)
dist.barrier() # 所有 rank 同步
存储建议:
- checkpoint 写到 PVC(ReadWriteMany,多 Pod 共享)。
- 长期归档上传到对象存储(S3/OSS),减小 PVC 压力。
- 保留最近 3-5 个 checkpoint + 每隔 N step 一个长期 checkpoint。
七、生产落地:从单任务到批调度
7.1 任务生命周期管理
7.2 多租户任务队列
生产 AIaaS 平台通常按租户划分 Queue,按 SLA 配置优先级:
# 租户 A: 高级用户,优先级高,配额大
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: tenant-a-premium
spec:
cohort: gpu-cohort
resourceGroups:
- flavors:
- name: a100-flavor
resources:
- name: nvidia.com/gpu
nominalQuota: 32 # 保证配额
borrowingLimit: 16 # 可借 16
---
# 租户 B: 免费用户,优先级低,配额小
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: tenant-b-free
spec:
cohort: gpu-cohort
resourceGroups:
- flavors:
- name: a100-flavor
resources:
- name: nvidia.com/gpu
nominalQuota: 4 # 保证 4 GPU
borrowingLimit: 0 # 不可借
cohort 让多个 Queue 在同一资源池里互相借调。租户 A 高峰期可以借走租户 B 没用的 GPU,反之不行(borrowingLimit: 0)。
7.3 训练任务 CI/CD
把训练任务纳入 CI/CD:
这里 Argo Workflows 充当训练流水线的总编排,PyTorchJob 是其中一步。这是模块六模型流水线的内容。
7.4 常见故障与排查
| 现象 | 原因 | 排查 |
|---|---|---|
| PyTorchJob 长期 Created 不 Running | Pod Pending,资源不够 | 看 Pod events,是否 FailedScheduling |
| Worker 起来但 init_process_group 卡住 | 部分 worker 没到,DNS 没解析 | kubectl exec 进 Pod 试 ping worker-0 |
| 训练几十步后 NCCL timeout | NVLink/InfiniBand 故障,或跨 NUMA | 看 dmesg,nvidia-smi |
| Worker OOM | batch size 太大或显存配额不够 | 减小 batch,开启 ZeRO |
| Worker 突然退出码 137 | OOM Killer | 看 dmesg,调内存 limit |
| Worker 退出码 143 | 收到 SIGTERM,被驱逐 | 看 events,是否被优先级抢占 |
| Volcano 不 Gang Scheduling | schedulerName 没设对 | 看 Pod.schedulerName 是否为 volcano |
八、与其他方案的对比
8.1 Training Operator vs Ray Train
| 维度 | Training Operator | Ray Train |
|---|---|---|
| 定位 | K8s 原生训练 CRD | 通用分布式计算框架 |
| 调度 | K8s 调度器 + Volcano | Ray 自有调度器 |
| 适合 | 标准 PyTorch/TF 分布式 | 异构任务、超参搜索 |
| 弹性 | 有限(需 PyTorch Elastic) | 原生支持 worker 增减 |
| 学习成本 | 低(K8s 语义) | 中(Ray 概念多) |
| 多语言 | 主要 Python | Python 原生,Java/C++ 部分 |
详见下一篇(Ray 与 KubeRay)。
8.2 Training Operator vs 自研 Operator
部分大厂选择自研 Operator,主要差异:
- 自研优势:可以深度定制启动模板、状态机、资源模型。
- Training Operator 优势:成熟、社区活跃、与 Kubeflow 生态集成。
- 折中:基于 Training Operator 上层做 webhook 扩展(注入企业特有逻辑),如审计、配额、计费 hook。
对绝大多数公司,直接用 Training Operator + Volcano/Kueue 是最优解。
本篇小结
| 主题 | 核心结论 |
|---|---|
| 训练任务的特殊性 | all-or-nothing 语义、worker 间通信、checkpoint 续训 |
| Operator 模式 | 声明式 CR + 控制循环,描述「我要什么」而非「怎么做」 |
| PyTorchJob | 用 Worker 角色定义对等 worker,Operator 自动注入 RANK/WORLD_SIZE/MASTER_ADDR |
| 环境变量约定 | 用户代码读 RANK/WORLD_SIZE/MASTER_ADDR,与 K8s 解耦 |
| Gang Scheduling | 一组 Pod 要么全调度要么全不调度,避免死锁 |
| Volcano | 实现完整 Gang Scheduling + Queue + 抢占,重型多租户首选 |
| Kueue | K8s 原生排队系统,轻量,新项目推荐 |
| 日志/监控 | Pod 级独立日志,prometheus_client 暴露训练指标 |
| 任务队列 | ClusterQueue + LocalQueue,按租户/SLA 划分配额 |
| 选型 | 优先 Training Operator + Kueue,自研仅在大规模定制需求 |
核心心智模型:训练任务 = 一组必须同时启动的 Pod + 一个能识别这个语义的调度器。Operator 解决「怎么定义」,Gang Scheduling 解决「怎么调度」。
下篇预告
Training Operator 适合「标准」分布式训练,但有些场景需要更灵活的分布式计算——比如超参搜索(每个 worker 跑不同超参)、强化学习(多 Agent 并行)、训练 + 推理混合。Ray 是这类场景的事实标准。下一篇我们讲 Ray 与 KubeRay,看 Ray 的 Head/Worker 架构、Ray Train 与 Ray Serve,以及 KubeRay 如何在 K8s 上管理 Ray 集群。
如果本篇内容对你有帮助,欢迎点赞收藏!有任何疑问,欢迎在评论区交流。

870

被折叠的 条评论
为什么被折叠?



