☰
CubeStudio多机多集群部署指南:从扩容到资源组隔离实践
2026/10/22 16:48:39 网站建设 项目流程

前阵子有个朋友问我,CubeStudio 从单机扩到多机的时候,是不是把机器加到集群里就行。实际情况远没那么简单,如果你只是把一台新机器用 kubeadm 加进集群,任务调度倒是能跑了,但训练数据、GPU 资源上报、存储挂载、命名空间权限这些环节往往会连环踩坑。这篇文章我就把 CubeStudio 从单机走到多机、从多机走到多集群纳管、再按项目组拆分算力这条链路上,我自己实测过、踩过、最终沉淀下来的步骤和细节一次性讲清楚,重点覆盖多机部署、多集群接入、多资源组隔离这三块,适合已经搭好单机环境、正琢磨着怎么扩容,或者准备让不同团队共享一套平台的同学参考。

1. 扩机之前,先想清楚这三件事

1.1 单机部署到底卡在哪

先回头看单机部署的模式。CubeStudio 单机跑的时候,所有服务组件和计算任务都挤在一台机器上,训练任务、推理服务、文件存储、数据库全在一个资源池里抢 CPU 和内存。短期做开发验证没问题,但只要并发任务一多,问题就非常明显:显存不够、内存不足、磁盘 IO 成为瓶颈、任务排队时间越来越长。

我见过一个典型的例子,某算法团队一开始用一台 8 卡机器跑模型训练,表面上看显存总量挺大,但一次要同时喂给四个任务,每个任务都想申请 1 张卡以上,结果调度器反复报资源不足,任务全部卡在 Pending。这时候有人第一反应是“再买一台机器,装个同样环境,继续单独跑”,这其实并没有解决资源池化的问题,反而把环境割裂成两个孤岛,哪台机器有空闲、哪台机器满载,完全要靠人工去盯。

所以多机部署的核心不是“多装几套”,而是把多台物理机的 CPU、内存、GPU 汇总到一个统一的调度池里。这样才能做到任务提交后,调度器自动选择合适节点运行,而不是“这台满了换那台”。这是整个扩容过程中最关键的认知转变。

1.2 把“多机、多集群、多资源组”三个词拆开理解

这三个词经常被混在一起说,但实际解决的是完全不同的问题。

多机,指的是一个 Kubernetes 集群跨多台物理机。解决的是单台机器的物理资源上限问题,训练任务可以调度到任一台机器上,实现资源的统一调配。

多集群,指的是 CubeStudio 控制台同时管理多个独立的 Kubernetes 集群。这些集群可能是不同机房、不同网络环境,也可能是为了开发环境和生产环境硬隔离。解决的是“资源分散在不同地方,但需要统一入口管理”的问题。

多资源组,指的是在纳管好的资源之上,按项目组或业务线划分逻辑资源边界。比如 A 项目组最多能用 8 张卡,B 项目组最多能用 2 张卡,彼此之间的配额互不影响。解决的是“资源给谁用、用多少、谁能用”的问题。

用一个生活化的例子来理解:多机像把一个办公室从一间小会议室扩成一层楼,工位变多了;多集群像同时管理几栋办公楼,每栋楼独立,但由一个行政中心统一管;多资源组则像给每个部门划定办公区域和门禁权限,财务部进不了研发区,各部门也不能随意占别人的座位和会议室。

搞清楚这三层,后面动手才不会做无用功。很多人一上来就想直接配多集群,结果发现自己的场景只需要一个集群扩多机;也有团队网络环境确实跨机房,必须先解决多集群纳管,却花了大量时间纠结怎么在一套集群里做网络互通。

2. 单机扩多机:把一台机器变成一套调度池

2.1 加入节点前,所有机器的状态必须一致

如果你手上已经有一套单机部署的 CubeStudio,底层必然是 Kubernetes。要把机器扩进来,第一步不是执行 join 命令,而是先把新机器的“底子”打齐。底子不齐,后面无论怎么排查都会非常痛苦。

至少需要确认三件事会一致:操作系统和内核版本、容器运行时版本、GPU 驱动和 CUDA 版本。有人可能觉得“都是 CentOS 7.9 就行”,但内核版本相差太多,可能会出现节点 kubelet 启动正常、却始终无法注册成功的情况。一般在生产环境我会建议所有节点用同一个模版或者同一份初始化脚本,避免后期维护成本失控。

GPU 驱动这块尤其重要。多机模式下,任务会动态调度到不同节点,如果 A 节点是 535 驱动、B 节点还停留在 470,那么同一个训练镜像在不同节点上跑出来的行为可能不一致,甚至直接报 CUDA 版本不匹配。解决方案是统一驱动版本,并让集群里的节点设备插件能够正确上报 GPU 数量。

容器运行时同理。Kubernetes 通过 CRI 与运行时通信,如果新老节点上的 containerd 版本差异较大,可能涉及 API 兼容性问题。建议要么完全统一版本,要么至少保证大版本一致。

2.2 节点加入的核心步骤:从 token 到标签

新节点环境准备好以后,加入集群的步骤其实不长,但每一步都有细节。

先生成加入命令。在主节点上执行:

kubeadm token create --print-join-command

这条命令会输出类似kubeadm join <apiserver-ip>:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx的内容。拿到命令后到新节点执行,然后等 kubelet 起来。

这里有个常见的坑:默认 token 有效期是 24 小时,如果集群闲置很久再扩容,建议重新生成 token,不要翻之前的 shell 历史记录。另外 token 有访问权限,千万不要外泄,否则任何人拿到都能接入集群。

新节点加入后,第一时间验证节点状态:

kubectl get nodes -o wide

如果节点状态不是 Ready,先看 kubelet 日志,一般能直接定位原因。接入了之后,给节点打上可辨识的标签,方便后面调度:

kubectl label node node-02 node-role.kubernetes.io/worker= kubectl label node node-02 accelerator=gpu

有一点要特别说明:主机名必须是唯一的。如果两台机器都叫localhost或者都叫node-02,kubelet 注册时会互相冲突,表现就是节点一会儿 Ready 一会儿 NotReady,非常隐蔽。

2.3 多机后必须处理的共享存储和 GPU 上报

单机模式下,数据放在本机磁盘路径也没事,因为任务只在这一个节点跑。一旦变成多机,任务调度的目标节点是动态的,本机路径就成了最大的隐患。比如任务先跑在 node-01,下次调度到 node-02,模型文件和训练数据在 node-02 上根本不存在,任务直接 404。

共享存储没有太多可选方案,最稳妥的是在 Kubernetes 集群里接入一个 CSI 存储插件,底层用 NFS 或对象存储,然后做成默认 StorageClass。只要在 CubeStudio 后端配置好存储类,任务提交时自动挂载共享数据卷,才真正实现“任务跑在哪台机器,数据都在同一个路径”。

GPU 上报方面,节点加入后要在集群中安装或确认 GPU 设备插件在运行。插件的作用是把每张物理 GPU 上报为可调度的资源,比如nvidia.com/gpu。没有插件的话,即使机器插了卡,调度器也看不见,任务照样 Pending。

我当时在第二台机器加入后,明明nvidia-smi能看到 4 张卡,但kubectl describe node里压根没有 GPU 资源,排查了半天,最后发现是新节点的标签和 GPU 插件选择器没匹配上,导致 DaemonSet 没有在该节点上运行。这类问题很典型,核心就是去查 DaemonSet 的调度状态。

多机做完以后,你会得到一个单集群多节点的资源池。这时候 CubeStudio 的任务调度天然会跨节点,但要注意给不同任务设置资源请求时,CPU 和内存一律别写 0 或者不写,否则调度器会把资源预估搞得很混乱。

3. 多集群纳管:把不同环境收进同一个控制台

3.1 什么时候需要多集群,而不是继续扩机器

一个集群多机模式用得好好的,为什么还要考虑多集群?我在实际项目中总结下来,两个理由最典型。

第一个是环境隔离。开发、测试、生产如果混在同一套集群,一个调试任务误删 namespace 都可能波及生产服务。这种事担不起风险,所以很多团队宁可拆成多套集群,各跑各的。

第二个是跨机房或跨云资源。比如一部分训练在自建机房的物理机上,一部分推理在云上的托管集群里,网络不互通,不可能强行组成单一集群,但操作入口又不能分散在各个集群地址上,这时候就需要 CubeStudio 统一纳管。

多集群纳管不是让你把所有集群强行搅在一起,正好相反,它是把多个彼此独立的 K8s 集群做成“统一控制台”,任务还是跑在各自集群中,只是管理和调度上有了一个公共的入口。

3.2 外部集群导入:kubeconfig、RBAC、网络可达

CubeStudio 纳管集群的核心就是导入 kubeconfig。理解这个动作的本质,就相当于把它当做“控制台对目标集群接口的认证凭证”。

三件事必须同时满足。

第一,kubeconfig 里的 apiserver 地址必须从 CubeStudio 所在机器能访问到。我遇到过一种情况:在集群内部测试kubectl get nodes完全正常,但导入到 CubeStudio 后一直是连接失败,原因就是 CubeStudio 部署在另一套网络里,访问不到集群 apiserver 的内网地址。这种情况要么内网互通,要么把 kubeconfig 里的 server 地址改成可访问的地址。

第二,建议不要直接给 cluster-admin 权限。实际生产里,更稳妥的做法是创建一个只有基础查看和指定命名空间管理权限的 ServiceAccount,然后把对应的 kubeconfig 拿给 CubeStudio 用。这样即使控制台被人侵入,攻击面也限制在指定命名空间,不会影响到整个集群。

第三,导入后给集群打上可识别的标签。比如env=prod、env=dev、location=beijing,这样后续创建任务时可以通过标签精确选择目标集群。

验证是否可以正常管理目标集群,可以先用 kubeconfig 文件执行一次命令:

kubectl --kubeconfig=import-kubeconfig get nodes

如果这个命令能正常返回节点列表,那导入到 CubeStudio 大概率没问题。

3.3 多集群调度:给任务指定“该去哪”

多集群纳管以后,最怕的就是任务乱跑。默认情况下,如果你只有一个集群,调度器会自己找地方。但好几个集群都注册在控制台里,任务默认提交到哪个集群,就变成一个明确需要配置的问题。

在 CubeStudio 的设计里,任务一般可以单独指定目标集群。如果没指定,会走系统默认集群。实际上我会建议团队在创建任务模板时,强制要求填写集群标签,宁可多几步操作,也不要让训练任务误发到生产集群去。

也可以通过 Kubernetes 原生的污点(Taint)和容忍度(Toleration)来做硬隔离。比如给开发集群的节点加一个dedicated=dev:NoSchedule的污点,那么生产任务如果没有对应容忍度,永远不会被调度到开发集群。反之亦然。这个方式的优点是即使有人通过原生 K8s 接口直接提交任务,也一样被拦在节点层面。

多集群环境最忌讳的就是“一套配置走天下”。每个集群的节点数量、GPU 型号、网络环境都不一样,调度策略必须围绕集群标签和节点标签来设计,这样后面排错也容易——看任务调度到了哪个集群、哪个节点,基本一眼就能判断是否符合预期。

4. 按项目组分算力:从资源池到资源组的落地

4.1 理解资源组:命名空间、配额、权限的集合

算力池化完成、多集群也接进来了,资源总量已经很可观。但如果你直接把所有资源开放给团队成员,马上就会遇到新的问题:A 项目组跑了个大任务,把 8 张卡全部占满,B 项目组想跑个小实验却一直 Pending。

这时候就必须引入资源组。CubeStudio 里的资源组,本质上是一个逻辑边界,它至少包含三层含义:对应一组命名空间,对应一组资源配额,对应一组用户或项目组成员。

可以用一个简单表格来对照:

维度对象作用
物理资源集群 + 节点机器与 GPU 卡的归属
逻辑隔离命名空间环境与资源边界
配额限制ResourceQuota能使用多少资源
访问控制Role / RoleBinding谁能使用这个空间

4.2 创建资源组的步骤:从链路设计到配额下发

资源组不是凭空拍脑袋定的,需要先把业务场景梳理清楚。我建议按“项目组规模 + 任务类型”两个维度去设计。规模决定配额大小,任务类型决定资源口味——训练组需要大显存,推理组需要低延迟,方差很大。

具体操作上,可以分成四步。第一步,确认项目组成员和负责人;第二步,确定该组能用哪些集群的哪些节点。注意这里可以通过节点标签做到比“整个集群”更细的粒度,比如某项目组只能跑在打了project-a=gpu标签的节点上;第三步,填写资源配额;第四步,绑定成员并分配角色。

配额配置最好在 K8s 的 ResourceQuota 层面直接做,确保最终规则能真正约束到底层调度。比如:

apiVersion: v1 kind: ResourceQuota metadata: name: quota-project-a namespace: project-a-ns spec: hard: requests.cpu: "100" requests.memory: 200Gi requests.nvidia.com/gpu: "8" persistentvolumeclaims: "5"

这个示例表达的意思是:project-a 这个命名空间里,所有 Pod 申请的 CPU 总和不能超过 100 核、内存不能超过 200Gi、GPU 不能超过 8 张。配额一旦写死,任何超过限制的 Pod 都会创建失败。

然而只配 ResourceQuota 有一个漏洞。如果项目组成员提交 Pod 时没有写资源请求,K8s 可能默认按 0 来算。为了解决这个漏洞,还需要在每个命名空间上配一个 LimitRange,强制给每个 Pod 设置默认资源请求和限制。

4.3 配额之外的优先级:谁先拿到资源

配额管的是“最多能用多少”,但真实场景里还会遇到另一种冲突:同一个时间窗口下,A 项目组和 B 项目组都在申请剩余资源,谁的优先级高?

K8s 原生提供了 PriorityClass 机制。你可以在 CubeStudio 的任务配置里接入这一机制,把训练任务的优先级调高,比如:

apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: training-high-priority value: 1000000 globalDefault: false description: "生产训练任务"

配好以后,训练任务在队列中的权重会显著高于普通开发任务。即使集群资源已经满了,高优先级任务也有机会抢占低优先级 Pod 的资源,确保核心任务不会被测试任务堵住。

这里必须强调一个坑:配额和优先级是两个独立维度,不能混为一谈。配额决定“能不能提交”,优先级决定“满了以后谁先上”。只配优先级不配配额,团队之间还是会互相挤占;只配配额不配优先级,虽然不会超额,但关键任务的时效性得不到保障。我见过不少团队只盯着配额,结果训练任务和大规模数据预处理任务同时排队,训练任务被一堆跑批任务堵了好几个小时,这个问题用 PriorityClass 可以非常干净地解决掉。

另外,如果同一个项目组内部还要细分子团队,可以在资源组下面多建几个命名空间,分别配额。这种方式对后期账单分摊和资源审计非常有帮助。

5. 多机、多集群运行中常见的坑与排查方法

5.1 节点加入失败:不是所有报错都写在日志表面

多机部署最常遇到的坑就是新节点加入集群失败。我遇到过几次,表现都一样:执行 join 命令后卡住,或者显示成功但节点状态一直是 NotReady。

排查思路有固定顺序。第一步看 kubelet 服务状态,systemctl status kubelet,90% 的问题都在这能看到。剩下几种隐蔽情况:主机名重复、token 过期、证书 hash 不匹配、apiserver 地址不可达。

还有一次比较离奇,节点通过 join 命令加入了集群,但 kubelet 反复重启,后来发现是系统时间偏移超过集群容忍范围。K8s 的证书和通信对时间敏感,节点时间差太多,连 apiserver 的认证都会失败。所以在做节点初始化时,我一般会先把 NTP 服务配置好,再执行加入动作。

5.2 跨机训练任务异常卡死:优先级最高的检查点是存储

多机之后,如果是单机时代没跑过的任务,出现“卡死”“报错”“找不到文件”之类的问题,我的排查路径非常固定:第一步看 Pod 事件,第二步看日志,第三步检查数据挂载。

举个例子,某个训练脚本里写死了/data/model_weights,单机时没问题。多机以后任务调度到 node-02,这个路径根本不存在,Pod 事件会出现FailedMount或者容器内 IO error。解决方案就是前面说的,所有数据目录统一放到共享存储上,代码里杜绝本机路径。

还有一个容易忽略的问题:共享存储本身也可能是性能瓶颈。以前单机读本地磁盘可能 500MB/s,换成网络存储以后如果只有 50MB/s,训练任务整体变慢,不是卡死但让你误判是网络问题。如果你发现跨机训练明显比单机慢,先测一下共享存储的吞吐,别急着调集群参数。

5.3 资源组配额没生效:检查两个最容易忽视的环节

资源配额配了,任务还是超额提交,这是我在项目里被问得最多的一个问题。通常原因有二。

第一,Pod 没设置资源请求。如果项目成员提交任务时不写resources.requests,那么 ResourceQuota 对requests.cpu的统计可能为空,配额形同虚设。解决办法就是配合使用 LimitRange,它会给未显式设置请求的 Pod 自动填充默认值。

第二,GPU 资源名没有对上。如果你在集群里安装了某个 GPU 插件,资源名可能是nvidia.com/gpu,也可能是amd.com/gpu或者其他型号的自定义资源名。ResourceQuota 里写的资源名如果和实际调度用的资源名不一致,配额自然不会生效。这个排查起来比较隐蔽,只能去kubectl describe node看节点上报的资源名,再回头核对配额里的名字。

5.4 多集群纳管失败:连接断开与权限不足的判断

导入集群报错时,先把报错信息区分成两类:一类是“连接超时”“拒绝连接”,另一类是“Forbidden”“Unauthorized”。

连接类问题的原因基本都是网络不通或者证书信任不对。先确认 CubeStudio 能访问 apiserver 地址,再确认 kubeconfig 里的 CA 证书没被替换。权限类问题则往 RBAC 方向查,比如你创建 ServiceAccount 时只给了get权限,但 CubeStudio 需要list权限来展示资源列表,就会报 Forbidden。

为了避免权限问题反复折腾,我建议在导入前用一个最小验证集:

kubectl --kubeconfig=import-kubeconfig get nodes -o name kubectl --kubeconfig=import-kubeconfig get pods --all-namespaces -o name | head -5

如果这两个命令都能正常执行,说明权限够用、连接也通。若第二个命令报出 Forbidden,就是权限范围问题,只需要把 RBAC 规则从节点查看扩展到 Pod 查看即可。建议不要直接给 cluster-admin,权限给到够用即可,这既是对集群的保护,也是排查问题时的清晰边界。

6. 从单机到多集群,我的最后几点体会

整套操作做完以后,回头总结经验,最重要的一条是:多机、多集群、多资源组不是必须同时完成的升级,而是按实际需求逐步拉开的一条演进路径。我们经常会看到平台里塞满了漂亮的架构图,但实际业务的资源用量根本撑不起那个复杂度。多集群带来的运维成本和管理复杂度是实打实的,没有隔离需求就别急着上。

我的建议是先做多机,把共享存储和 GPU 上报跑顺,让任务跨节点稳定运行至少一两周。然后在运行过程中观察:是不是存在团队互相抢资源的情况,是不是有开发环境与生产环境边界模糊带来的风险。确有需要,再做多集群纳管和资源组划分。

另外有个小技巧想分享给正在做扩容的朋友:配置资源组时一定要先划好“配额草案”,再动集群。哪怕第一版配额只是凭经验估的,也比没有任何配额直接开跑好。因为资源组一旦落地使用,后续调整涉及大量历史任务和成员习惯,改动成本比你想的高得多。先把边界画出,哪怕宽松一点,之后收口都好办。

CubeStudio 的这套多机、多集群、多资源组扩展路径,我已经带着好几批同学验证过多次。如果你也是刚从单机往多机走,建议把前面几个验证命令挨个跑一遍,尤其是新节点加入后查看 GPU 上报和共享存储挂载状态。踩过一轮坑以后,你会发现自己再也不是靠猜去规划算力,而是有了一套可以复用的方法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询