
前阵子有个朋友问我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 acceleratorgpu有一点要特别说明主机名必须是唯一的。如果两台机器都叫localhost或者都叫node-02kubelet 注册时会互相冲突表现就是节点一会儿 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 用。这样即使控制台被人侵入攻击面也限制在指定命名空间不会影响到整个集群。第三导入后给集群打上可识别的标签。比如envprod、envdev、locationbeijing这样后续创建任务时可以通过标签精确选择目标集群。验证是否可以正常管理目标集群可以先用 kubeconfig 文件执行一次命令kubectl --kubeconfigimport-kubeconfig get nodes如果这个命令能正常返回节点列表那导入到 CubeStudio 大概率没问题。3.3 多集群调度给任务指定“该去哪”多集群纳管以后最怕的就是任务乱跑。默认情况下如果你只有一个集群调度器会自己找地方。但好几个集群都注册在控制台里任务默认提交到哪个集群就变成一个明确需要配置的问题。在 CubeStudio 的设计里任务一般可以单独指定目标集群。如果没指定会走系统默认集群。实际上我会建议团队在创建任务模板时强制要求填写集群标签宁可多几步操作也不要让训练任务误发到生产集群去。也可以通过 Kubernetes 原生的污点Taint和容忍度Toleration来做硬隔离。比如给开发集群的节点加一个dedicateddev:NoSchedule的污点那么生产任务如果没有对应容忍度永远不会被调度到开发集群。反之亦然。这个方式的优点是即使有人通过原生 K8s 接口直接提交任务也一样被拦在节点层面。多集群环境最忌讳的就是“一套配置走天下”。每个集群的节点数量、GPU 型号、网络环境都不一样调度策略必须围绕集群标签和节点标签来设计这样后面排错也容易——看任务调度到了哪个集群、哪个节点基本一眼就能判断是否符合预期。4. 按项目组分算力从资源池到资源组的落地4.1 理解资源组命名空间、配额、权限的集合算力池化完成、多集群也接进来了资源总量已经很可观。但如果你直接把所有资源开放给团队成员马上就会遇到新的问题A 项目组跑了个大任务把 8 张卡全部占满B 项目组想跑个小实验却一直 Pending。这时候就必须引入资源组。CubeStudio 里的资源组本质上是一个逻辑边界它至少包含三层含义对应一组命名空间对应一组资源配额对应一组用户或项目组成员。可以用一个简单表格来对照维度对象作用物理资源集群 节点机器与 GPU 卡的归属逻辑隔离命名空间环境与资源边界配额限制ResourceQuota能使用多少资源访问控制Role / RoleBinding谁能使用这个空间4.2 创建资源组的步骤从链路设计到配额下发资源组不是凭空拍脑袋定的需要先把业务场景梳理清楚。我建议按“项目组规模 任务类型”两个维度去设计。规模决定配额大小任务类型决定资源口味——训练组需要大显存推理组需要低延迟方差很大。具体操作上可以分成四步。第一步确认项目组成员和负责人第二步确定该组能用哪些集群的哪些节点。注意这里可以通过节点标签做到比“整个集群”更细的粒度比如某项目组只能跑在打了project-agpu标签的节点上第三步填写资源配额第四步绑定成员并分配角色。配额配置最好在 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 kubelet90% 的问题都在这能看到。剩下几种隐蔽情况主机名重复、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 --kubeconfigimport-kubeconfig get nodes -o name kubectl --kubeconfigimport-kubeconfig get pods --all-namespaces -o name | head -5如果这两个命令都能正常执行说明权限够用、连接也通。若第二个命令报出 Forbidden就是权限范围问题只需要把 RBAC 规则从节点查看扩展到 Pod 查看即可。建议不要直接给 cluster-admin权限给到够用即可这既是对集群的保护也是排查问题时的清晰边界。6. 从单机到多集群我的最后几点体会整套操作做完以后回头总结经验最重要的一条是多机、多集群、多资源组不是必须同时完成的升级而是按实际需求逐步拉开的一条演进路径。我们经常会看到平台里塞满了漂亮的架构图但实际业务的资源用量根本撑不起那个复杂度。多集群带来的运维成本和管理复杂度是实打实的没有隔离需求就别急着上。我的建议是先做多机把共享存储和 GPU 上报跑顺让任务跨节点稳定运行至少一两周。然后在运行过程中观察是不是存在团队互相抢资源的情况是不是有开发环境与生产环境边界模糊带来的风险。确有需要再做多集群纳管和资源组划分。另外有个小技巧想分享给正在做扩容的朋友配置资源组时一定要先划好“配额草案”再动集群。哪怕第一版配额只是凭经验估的也比没有任何配额直接开跑好。因为资源组一旦落地使用后续调整涉及大量历史任务和成员习惯改动成本比你想的高得多。先把边界画出哪怕宽松一点之后收口都好办。CubeStudio 的这套多机、多集群、多资源组扩展路径我已经带着好几批同学验证过多次。如果你也是刚从单机往多机走建议把前面几个验证命令挨个跑一遍尤其是新节点加入后查看 GPU 上报和共享存储挂载状态。踩过一轮坑以后你会发现自己再也不是靠猜去规划算力而是有了一套可以复用的方法。