共享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社区的已知报错。
前提条件
操作步骤
步骤一:安装MPS Control Daemon组件
登录容器服务管理控制台,在左侧导航栏选择 。
进入应用市场界面,在搜索框输入ack-mps-control,然后单击搜索到的组件,进入其安装界面。
在ack-mps-control安装界面,单击一键部署,选择需要部署组件的集群,然后单击下一步。
在创建页面,选择对应Chart版本,然后单击确定完成安装。
重要卸载和升级MPS Control Daemon组件ack-mps-control都会影响节点上已经运行的GPU应用,导致GPU应用错误退出。请在业务低峰期执行这些操作。
步骤二:安装共享GPU组件
登录容器服务管理控制台,在左侧导航栏选择集群。
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择 。
在云原生AI套件页面,单击一键部署。
在一键部署云原生AI套件页面,选中调度策略扩展(批量任务调度、GPU共享、GPU拓扑感知)。
在云原生AI套件页面最下方,单击部署云原生AI套件。
组件安装成功后,在云原生AI套件页面的组件列表中能看到已安装的共享GPU组件ack-ai-installer。
步骤三:开启GPU共享调度能力和显存隔离能力
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择 。
在节点池页面,单击创建节点池。
在创建节点池页面,设置创建节点池的配置项,然后单击确认配置。
以下为重要配置项的说明,其余配置项的配置方法,请参见创建节点池。
配置项
说明
期望节点数
设置节点池初始节点数量。如无需创建节点,可以填写为0。
节点标签
单击节点标签的,设置键为ack.node.gpu.schedule,值为mps。
重要每个GPU节点只有打上标签
ack.node.gpu.schedule=mps
后,GPU节点上才会部署MPS Control Daemon Pod,如果您的集群中部署了共享GPU调度组件,当节点打上标签ack.node.gpu.schedule=mps
后,该节点同时会开启共享GPU调度能力和MPS隔离能力。添加共享GPU调度标签后,请勿通过
kubectl label nodes
命令切换节点GPU调度属性标签值或使用控制台节点页面的标签管理功能更改节点标签,以避免引发潜在的问题,请参见使用kubectl label nodes或通过控制台节点标签管理功能切换标签值存在的问题。推荐您基于节点池划分GPU资源调度能力。
步骤四:安装共享GPU查询工具
下载kubectl-inspect-cgpu。
如果您使用的是Linux系统,您可以通过以下命令下载kubectl-inspect-cgpu。
wget http://aliacs-k8s-cn-beijing.oss-cn-beijing.aliyuncs.com/gpushare/kubectl-inspect-cgpu-linux -O /usr/local/bin/kubectl-inspect-cgpu
如果您使用的是macOS系统,您可以通过以下命令下载kubectl-inspect-cgpu。
wget http://aliacs-k8s-cn-beijing.oss-cn-beijing.aliyuncs.com/gpushare/kubectl-inspect-cgpu-darwin -O /usr/local/bin/kubectl-inspect-cgpu
执行以下命令,为kubectl-inspect-cgpu添加执行权限。
chmod +x /usr/local/bin/kubectl-inspect-cgpu
执行以下命令,查看集群GPU使用情况。
kubectl inspect cgpu
预期输出:
NAME IPADDRESS GPU0(Allocated/Total) GPU Memory(GiB) cn-shanghai.192.168.6.104 192.168.6.104 0/15 0/15 ---------------------------------------------------------------------- Allocated/Total GPU Memory In Cluster: 0/15 (0%)
步骤五:部署示例应用
使用如下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的效果。 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 Nov 12 11:09:35 2024 +---------------------------------------------------------------------------------------+ | 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 xxxxxxxxxxxxxx On | 00000000:00:07.0 Off | 0 | | N/A 37C P0 56W / 300W | 345MiB / 32768MiB | 0% E. Process | | | | N/A | +-----------------------------------------+----------------------+----------------------+ +---------------------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=======================================================================================| | 0 N/A N/A 197792 C nvidia-cuda-mps-server 30MiB | | 0 N/A N/A 387820 M+C python 312MiB | +---------------------------------------------------------------------------------------+
可以看到,
nvidia-smi
命令输出中,mps-server
已经启动,在宿主机上的进程号为197792,同时以MPS方式启动了一个进程号为387820的Python程序,说明MPS是正常运行的。