共享GPU调度支持通过NVIDIA MPS(Multi-Process Service)作为底层GPU隔离模块,实现多个Pod共享同一张GPU卡,并确保各Pod之间的显存隔离。本文将为您介绍如何启用NVIDIA MPS隔离功能,并将其与共享GPU调度组件集成使用。
背景信息
基于MPI实现CPU核并行化,可以平衡CPU密集型任务之间的资源分配,确保多个计算任务能够同时进行,从而加速整体的计算过程。但当使用CUDA内核加速MPI进程时,每个MPI进程所分配到的工作量可能无法充分利用GPU,导致虽然每个MPI进程运行速度有所提高,但整体GPU的使用效率很低。当单个应用程序发送给GPU的任务量不足,GPU存在闲置资源时,推荐您使用NVIDIA MPS(Multi-Process Service),一种用于在NVIDIA GPU上运行多个CUDA应用程序的技术,适合用于多用户环境或需要同时运行多个小任务的场景,从而提升GPU利用率和应用程序的吞吐量。
MPS允许不同的应用程序在同一个GPU设备上并发执行,以提高集群GPU资源的利用率。MPS通过Client-Server架构来实现了这种功能,确保了二进制兼容性,即您无需对现有的CUDA应用程序进行重大改造。MPS的构成组件如下。
Control Daemon Process:负责启动和停止MPS Server,并协调客户端和MPS Server之间的连接,确保客户端能够顺利接入到MPS服务中进行GPU资源的请求和使用。
Client Runtime:集成于CUDA驱动程序库内部。开发者无需对CUDA应用程序代码进行重大改造即可使用MPS。当应用程序使用CUDA驱动进行GPU操作时,Client Runtime会自动处理与MPS Server的交互,从而使多个应用程序能够高效、安全地共享GPU。
Server Process:接收来自不同客户端的请求,通过高效的调度策略将请求运行在一个GPU设备上,从而实现了客户端之间的并发性。
注意事项
在NVIDIA MPS架构下,MPS Client(您提交的使用MPS功能的GPU应用)需要跟MPS Control Daemon保持交互。一旦MPS Control Daemon重启,这些MPS Client将错误退出。
在本示例中,MPS Control Daemon服务以容器化的方式运行,以DaemonSet的形式在每个GPU节点部署一个MPS Control Daemon Pod,关于MPS Control Daemon Pod说明如下。
MPS Control Daemon Pod不能随意删除或重启。删除MPS Control Daemon Pod将会导致节点的GPU应用不可用。您可以通过命令
kubectl get po -l app.aliyun.com/name=mps-control-daemon -A查询集群中MPS Control Daemon Pod状态。容器中运行MPS Control Daemon时,容器需要具有
privileged、hostIPC、hostPID的权限。这可能带来一些潜在风险,请谨慎评估后再决定是否使用该方案。MPS Control Daemon Pod使用
priorityClassName: system-node-critical来保证其优先级,以避免节点资源不足时MPS Control Daemon Pod被终止,继而导致业务程序无法使用。如果在部署MPS Control Daemon组件的过程中遇到节点资源不足的情况,MPS Control Daemon可能会抢占其他优先级较低的业务Pod,导致这些业务Pod被驱逐。请在部署组件前,确保节点CPU、内存等资源充足。
针对纳入K8s集群管理的GPU节点,为业务应用申请和使用GPU资源时,请关注以下注意事项。
请勿直接在节点上运行GPU应用程序。
请勿通过
docker、podman、nerdctl等工具命令创建容器并为容器申请GPU资源。例如,执行docker run --gpus all或docker run -e NVIDIA_VISIBLE_DEVICES=all并运行GPU程序。请勿在Pod YAML的
env中直接添加环境变量NVIDIA_VISIBLE_DEVICES=all或NVIDIA_VISIBLE_DEVICES=<GPU ID>等,通过容器的环境变量NVIDIA_VISIBLE_DEVICES直接为Pod申请GPU资源,并运行GPU程序。在Pod YAML中未设置环境变量
NVIDIA_VISIBLE_DEVICES,制作Pod所使用的镜像时,请勿将环境变量默认配置为NVIDIA_VISIBLE_DEVICES=all,并运行GPU程序。请勿在Pod的
securityContext中配置privileged: true,并运行GPU程序。
通过以上非标方式为业务应用申请的GPU资源,将存在如下安全隐患。
通过以上方式为业务应用申请的GPU资源,并未在调度器的设备资源账本中统计,有可能造成节点GPU资源的分配情况与调度器设备资源账本中记录的值不一致。调度器仍然会调度某些申请GPU资源的Pod到这个节点上,导致用户业务因为在同一张GPU卡上出现资源争抢(比如GPU显存申请)而运行失败的情况。
非标操作可能引发其他未知问题,例如NVIDIA社区的已知报错。
适用范围
已创建ACK托管集群Pro版,且版本为1.20及以上。若集群版本过低,请升级集群。
操作步骤
步骤一:安装MPS Control Daemon组件
登录容器服务管理控制台,在左侧导航栏选择。
进入应用市场界面,在搜索框输入ack-mps-control,然后单击搜索到的组件,进入其安装界面。
在ack-mps-control安装界面,单击一键部署,选择需要部署组件的集群,然后单击下一步。
在创建页面,选择对应Chart 版本,然后单击确定完成安装。
重要卸载和升级MPS Control Daemon组件ack-mps-control都会影响节点上已经运行的GPU应用,导致GPU应用错误退出。请在业务低峰期执行这些操作。
由于升级策略为
OnDelete,系统不会自动重启 Pod。升级结束后,请务必手动删除 ack-mps-control DaemonSet 下的旧 Pod 以完成更新。详细操作,请参见如何升级MPS Control Daemon组件?
步骤二:安装共享GPU组件
登录容器服务管理控制台,在左侧导航栏选择集群列表。
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择。
在云原生AI套件页面,单击一键部署。
在一键部署云原生AI套件页面,选中调度策略扩展(批量任务调度、GPU共享、GPU拓扑感知)。
在云原生AI套件页面最下方,单击部署云原生AI套件。
组件安装成功后,在云原生AI套件页面的组件列表中能看到已安装的共享GPU组件ack-ai-installer。
步骤三:开启GPU共享调度能力和显存隔离能力
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择。
在节点池页面,单击创建节点池。
在创建节点池页面,设置创建节点池的配置项,然后单击确认配置。
以下为重要配置项的说明,其余配置项的配置方法,请参见创建和管理节点池。
配置项
说明
期望节点数
设置节点池初始节点数量。
节点标签
单击节点标签的
,设置键为ack.node.gpu.schedule,值为mps。
步骤四:部署示例应用
使用如下YAML创建一个示例应用。
apiVersion: batch/v1 kind: Job metadata: name: mps-sample spec: parallelism: 1 template: metadata: labels: app: mps-sample spec: hostIPC: true # 需要设置的选项,否则Pod会启动失败。 hostPID: true # 不需要设置此选项,此处设置只是便于看到使用MPS的效果。 nodeSelector: kubernetes.io/hostname: <NODE_NAME> # 指定<NODE_NAME>为带ack.node.gpu.schedule=mps标签的GPU节点,例如:cn-shanghai.192.0.2.109。 containers: - name: mps-sample image: registry.cn-hangzhou.aliyuncs.com/ai-samples/gpushare-sample:tensorflow-1.5 command: - python - tensorflow-sample-code/tfjob/docker/mnist/main.py - --max_steps=100000 - --data_dir=tensorflow-sample-code/data resources: limits: aliyun.com/gpu-mem: 7 # 为该Pod申请7GiB显存。 workingDir: /root restartPolicy: Never重要当节点开启MPS能力后,在该节点上运行的GPU应用Pod需要配置
hostIPC: true,否则Pod会启动失败。等待Pod创建并处于Running状态,使用如下命令查看MPS是否使用。
kubectl exec -ti mps-sample-xxxxx -- nvidia-smi预期输出:
Tue May 27 05:32:12 2025 +---------------------------------------------------------------------------------------+ | NVIDIA-SMI 535.161.07 Driver Version: 535.161.07 CUDA Version: 12.2 | |-----------------------------------------+----------------------+----------------------+ | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+======================+======================| | 0 Tesla ****-****-**** On | 00000000:00:09.0 Off | 0 | | N/A 33C P0 55W / 300W | 345MiB / 16384MiB | 0% E. Process | | | | N/A | +-----------------------------------------+----------------------+----------------------+ +---------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=======================================================================================| | 0 N/A N/A 14732 C nvidia-cuda-mps-server 30MiB | | 0 N/A N/A 110312 M+C python 312MiB | +---------------------------------------------------------------------------------------+可以看到,
nvidia-smi命令输出中,mps-server已经启动,在宿主机上的进程号为14732,同时以MPS方式启动了一个进程号为110312的Python程序,说明MPS是正常运行的。
常见问题
如何升级MPS Control Daemon组件?
升级ack-mps-control v0.2.0版本依赖ack-ai-installer >= 1.13.1,推荐按以下顺序升级MPS Control Daemon组件。
在云原生AI套件页面的组件列表中,升级共享 GPU 调度组件 ack-ai-installer 的 Helm 版本。
在中选择
kube-system命名空间,升级 ack-mps-control 组件 Helm 版本。由于升级策略为
OnDelete,系统不会自动重启 Pod。升级结束后,请务必手动删除 ack-mps-control DaemonSet 下的旧 Pod 以完成更新。逐节点执行节点排水、设为不可调度并删除 ack-mps-control Pod。
将节点设为不可调度并执行节点排水。
删除该节点上的 ack-mps-control Pod。
确认新 Pod 正常运行。
删除并重启 ack-ai-installer Pod。
在完成 ack-mps-control 升级并确认相关 Pod 已更新后,手动删除 ack-ai-installer Pod,使其自动重建。
将节点恢复为可调度。
当目标节点上的 ack-mps-control Pod 和 ack-ai-installer 都确认正常后,将节点恢复为可调度状态。