做分布式训练的兄弟,大概率都有过这种经历:部门里五六个人同时提交训练任务,每个人都要好几块GPU。负责集群的你一打开看板,好家伙,大部分卡被一个任务占着,其他任务要么排队排队,要么起来了但一直报OOM。说白了就是资源争抢。咱们今天就聊聊怎么用Kubeflow把这堆破事理顺。
一、现实里的痛:训练任务挤成一锅粥
分布式训练跟普通程序不一样,它一个任务往往要同时起好几个进程。比如TensorFlow的PS架构,要1个参数服务器加多个Worker;PyTorch也会起多个Worker。这些进程在Kubernetes里就是一个个Pod。问题来了:这些Pod本来应该一起调度,但Kubernetes默认调度器只看单个Pod,根本不关心“这个作业是不是一个整体”。如果集群资源只够拉起其中几个Pod,那这几个Pod就会先跑起来,占着资源,剩下的Pod就一直Pending。这就相当于四个人约好坐同一辆车,结果三个人已经坐进去了,车却因为缺一个人开不走,坐在车里的人还把车门堵住,后面的人也没法上别的车。整个训练任务卡死,资源却被白白占用,别人的任务也进不来,越积越多。
Kubeflow正好是解决这类问题的平台。它跑在Kubernetes上,专门管机器学习训练、服务、超参调优这些活儿。要会用它,得先知道它到底靠什么干活。
二、Kubeflow到底是个啥
Kubeflow不是一个单独的大软件,它是一堆组件拼起来的。最核心的是Training Operator,它负责把你提交的训练任务转换成Kubernetes能认的“自定义资源”。你用TensorFlow写代码,就用TFJob;用PyTorch,用PyTorchJob。每个Job会管理一组Pod,确保它们按规则启动、运行、重启。
2.1 核心组件
- Training Operator:所有分布式训练任务的保姆。
- Katib:自动帮你调超参数。
- Pipelines:把整个训练流程串成流水线。
- Kube-batch / Volcano:这才是咱们今天的主角,管调度的。
2.2 分布式训练任务长啥样
用TFJob举个例子,一个标准的训练任务长这样:
# 技术栈:Shell (kubectl)
# 创建一个TFJob,包含4个Worker,每个Worker要1块GPU
kubectl apply -f - <<EOF
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
name: example-tfjob
namespace: default
spec:
tfReplicaSpecs:
Worker:
replicas: 4
restartPolicy: OnFailure
template:
spec:
containers:
- name: tensorflow
image: tensorflow/tensorflow:2.12.0-gpu
command: ["python", "/app/train.py"]
resources:
requests:
nvidia.com/gpu: 1
cpu: "2"
memory: 4Gi
limits:
nvidia.com/gpu: 1
cpu: "2"
memory: 4Gi
EOF
这个作业一共要4个Worker Pod,每个都独占一块GPU。如果集群只有3块空闲,这三个Pod会先运行,第四个就一直Pending,这就是我们说的死锁。下面的优化例子,都围绕怎么避免这种尴尬。
三、第一个优化办法:给每个团队圈好地
资源争抢最直接的原因是没有“边界”。我们可以用Kubernetes原生机制,给不同团队分配命名空间,再在命名空间里设资源上限,这样谁也不能超吃。
3.1 ResourceQuota 怎么用
ResourceQuota就像小区里的车位划区,每个团队只能停自己的车位,总共就那么多。比如金融组的训练集群,总共16核CPU、32G内存、8块GPU,我们就这么设:
# 技术栈:Shell (kubectl)
# 先建个属于金融团队的名词空间分账
kubectl create namespace finance-group
# 设总账本:整个命名空间最多用这么多资源
kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
name: finance-quota
namespace: finance-group
spec:
hard:
requests.cpu: 16
requests.memory: 32Gi
requests.nvidia.com/gpu: 8
limits.cpu: 20
limits.memory: 40Gi
EOF
这样金融组一共只能申请8块GPU,就算单个任务想申请100块,也会被拒绝。
3.2 LimitRange 防止“胖墩”任务
ResourceQuota管的是整个命名空间的总量,但挡不住一个任务在里面狮子大开口。LimitRange就是给每个Pod设“尺寸”限制,比如最小要多少、最大能吃多少。这能防止有人把16核CPU全部写在同一个Pod里,导致其他任务没法调度。
# 技术栈:Shell (kubectl)
# 给金融团队的命名空间里的每个Pod定下资源上下限
kubectl apply -f - <<EOF
apiVersion: v1
kind: LimitRange
metadata:
name: finance-pod-limit
namespace: finance-group
spec:
limits:
- max:
cpu: "4"
memory: 8Gi
min:
cpu: "200m"
memory: 256Mi
default:
cpu: "1"
memory: 1Gi
defaultRequest:
cpu: "500m"
memory: 512Mi
type: Container
EOF
这样任何一个Pod最大的CPU也只能要4核,内存最多8G,想一个人霸占所有资源?想都别想。
四、第二个优化办法:让整组任务一起吃上饭
光靠分地盘还有漏洞:就算每个团队有8块GPU,如果4个Worker都要1块,而集群只剩3块了,那还是会出现死锁。这时需要“Gang Scheduling”。这个词听起来高级,意思很简单:要么所有Pod一起调度成功,要么一个都别启动。
4.1 什么叫Gang Scheduling
把训练任务看成一顿饭,所有Pod是同桌吃饭的人。座位不够?那大家都别坐下,等人齐了再一起上菜。这样就不会有人先占座导致别人没办法。Kubernetes社区里最有名的两个调度器,一个是老牌的Kube-batch,一个是它的继任者Volcano。Kubeflow可以配置使用Volcano。
4.2 用Volcano调度器干活
要让Kubeflow任务走Volcano,先得装上Volcano。用Helm装最省事:
# 技术栈:Shell (kubectl)
# 用Helm把Volcano调度器装进集群
helm install volcano volcano/volcano --namespace volcano-system --create-namespace
装完之后,在TFJob的Pod模板里指定调度器名称:
# 技术栈:Shell (kubectl)
# 创建TFJob,并通过schedulerName参数让Volcano接管调度
kubectl apply -f - <<EOF
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
name: example-tfjob-gang
namespace: finance-group
spec:
tfReplicaSpecs:
Worker:
replicas: 4
restartPolicy: OnFailure
template:
spec:
schedulerName: volcano
containers:
- name: tensorflow
image: tensorflow/tensorflow:2.12.0-gpu
command: ["python", "/app/train.py"]
resources:
requests:
nvidia.com/gpu: 1
cpu: "2"
memory: 4Gi
limits:
nvidia.com/gpu: 1
cpu: "2"
memory: 4Gi
EOF
Volcano看到这个作业需要4个Worker,它会先把资源攒起来,如果凑不齐,这4个Pod谁也别想启动。表面上看起来就是大家一起挂Pending,但不会出现“部分运行、部分卡死”的情况。一旦有别的任务结束腾出位置,Volcano会把它们一次性全部放进去,训练立刻开始。
五、第三个优化办法:重要的任务先走
就算有配额,有Gang调度,不同任务的紧急程度还不一样。比如线上正在跑的用户推荐模型要立刻上线,跟一个实验性的小试验抢资源,那肯定重要的大爷优先。这个通过PriorityClass实现。
5.1 PriorityClass 怎么设
PriorityClass就像给任务发“VIP通行证”,数字越大,越优先。我们定义一个高优先级的类:
# 技术栈:Shell (kubectl)
# 定义高优先级类别,value越大越优先
kubectl apply -f - <<EOF
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority-training
value: 1000000
globalDefault: false
description: "线上重要训练任务"
EOF
然后在训练任务里引用这个优先级。比如金融团队的高价值模型训练:
# 技术栈:Shell (kubectl)
# 创建TFJob,并指定priorityClassName为高优先级
kubectl apply -f - <<EOF
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
name: highprio-fraud-model
namespace: finance-group
spec:
tfReplicaSpecs:
Worker:
replicas: 4
restartPolicy: OnFailure
template:
spec:
priorityClassName: high-priority-training
schedulerName: volcano
containers:
- name: tensorflow
image: tensorflow/tensorflow:2.12.0-gpu
command: ["python", "/app/train.py"]
resources:
requests:
nvidia.com/gpu: 1
cpu: "2"
memory: 4Gi
limits:
nvidia.com/gpu: 1
cpu: "2"
memory: 4Gi
EOF
加上这行之后,Kubernetes会尝试先把低优先级任务占用的资源腾出来,给高优先级任务用。低优先级任务会被抢占,也就是被干掉然后重新排队。这个能力是调度层面的,Kubeflow作为上层平台,只要修改任务清单就行,非常方便。
5.2 配合Kubeflow的调度器
Kubeflow的Training Operator本身并不做调度,它只负责管理任务的声明。到底谁先跑,是Kubernetes调度器说了算。所以你可以把Volcano、PriorityClass、ResourceQuota这些组合起来。比如高优先级的任务用Volcano凑齐Pod,同时PriorityClass又确保它插队。这样一套组合拳,基本把资源争抢的窟窿都堵上了。
六、实际应用场景分析
这套优化在几个场景特别明显。
第一个是多个团队共用GPU集群。比如车联网公司里,感知组每天跑目标检测训练,地图组跑高精地图模型。两边都对时间敏感,但没有人愿意等。通过命名空间+ResourceQuota,把物理资源切块,各自调度自己那份,互不干扰。
第二个是训练任务弹性提交。算法工程师白天把任务交上去,晚上想看结果,但集群上还有别的任务在跑。如果用了Gang Scheduling,白天提交的任务会在队列里“攒团”,等位置够了再一次性启动;而高优先级的夜间任务可以随时插队。
第三个是自动化调参和训练流水线。Katib会给一堆超参数组合生成多个训练作业,这些作业如果不加管理,很可能一涌而上抢资源。配合PriorityClass,可以把搜索阶段的任务优先级调低,把最终选定的模型训练优先级调高,保证出结果的速度。
七、技术优缺点和注意事项
7.1 优点
- 资源不再被“部分运行”的任务锁死,GPU利用率能明显提升。
- 不同团队之间有了明确边界,吵架变少。
- 重要任务能优先跑,业务连续性更有保障。
- Kubeflow本身就是云原生方案,这些调度优化都是Kubernetes标准能力,学一套通用性很强。
7.2 缺点
- 引入Volcano等多一层调度器,集群运维变复杂。不是“装上就完事”,还得监控它。
- PriorityClass如果设置得不合理,低优先级任务可能永远跑不上,要小心设计。
- 资源配额设得太死,反而浪费空闲资源。比如一个团队用不完,另一个团队想借却借不了。
7.3 注意事项
- 显卡资源要提前配好nvidia-device-plugin,否则永远调度不到GPU。
- ResourceQuota和LimitRange要匹配,不然可能相互冲突。比如LimitRange默认值设得比Quota大,会导致任何Pod都创建不了。
- 使用Volcano时,注意它和Kubernetes新版本的兼容性,不要只图新鲜强行升级。
- 优先级和抢占不是万能的,有时会带来Pod重启,训练任务需要支持断点续跑,不然被抢占的Pod一到,训练进度全没了。
八、总结
思路其实不复杂:第一把资源按团队划好边界,第二让作业整体调度而不是七零八落,第三给任务分三六九等。Kubeflow站在这三件套上,把分布式训练变成一个个可声明的资源,你只需要写好配置,剩下的交给平台。真正干活的还是Kubernetes的调度思想。只要吃透了这几点,再大的训练集群也能稳稳拿捏。
评论
围绕“基于Kubeflow的企业级分布式训练架构优化:解决大规模任务调度与资源争抢痛点”参与讨论