使用cGPU服务可以隔离GPU资源,实现多个容器共用一张GPU卡。该服务作为阿里云容器服务Kubernetes版ACK(Container Service for Kubernetes)的组件对外提供服务,应用于高性能计算能力的场景,例如机器学习、深度学习、科学计算等,方便您更高效地利用GPU资源,以加速计算任务。本文介绍如何通过安装并使用cGPU服务。
cGPU服务的隔离功能不支持以UVM的方式(即调用CUDA API cudaMallocManaged())申请显存,请您使用其他方式申请显存,例如调用cudaMalloc()等。更多信息,请参见NVIDIA官方文档。
前提条件
在进行本操作前,请确保GPU实例满足以下要求:
- GPU实例规格为gn7i、gn6i、gn6v、gn6e、gn5i、gn5、ebmgn7i、ebmgn6i、ebmgn7e、ebmgn6e、ebmgn7ex或sccgn7ex。 
- GPU实例操作系统为CentOS、Ubuntu或Alibaba Cloud Linux。 
- GPU实例已安装Tesla 418.87.01或更高版本的驱动。 
- GPU实例已安装Docker 19.03.5或更高版本。 
安装cGPU服务
无论您是企业认证用户还是个人实名认证用户,推荐您通过ACK的Docker运行时环境安装和使用cGPU服务。
安装1.5.7版本的cGPU组件,可能会导致cGPU内核驱动出现死锁现象(即并发执行的进程互相牵制),从而导致Linux Kernel Panic(即内核错误)问题,建议您安装1.5.8及以上版本的cGPU,或将低版本cGPU逐步升级到1.5.8及以上版本,避免在新业务上出现内核错误问题。
- 创建集群。 - 具体操作,请参见创建ACK托管集群。 
- 在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择。 
- 在基础能力区域,选中调度策略扩展(批量任务调度、GPU共享、GPU拓扑感知)。 
- 单击页面底部的部署云原生AI套件。 - 组件安装成功后,在云原生AI套件页面的组件列表中能看到已安装的共享GPU组件ack-ai-installer。 
在云原生AI套件页面,单击一键部署。
使用cGPU服务
本文以ecs.gn6i-c4g1.xlarge为例演示2个容器共用1张显卡。
运行cGPU服务
- 执行以下命令,创建容器并设置容器内可见的显存。 - 本示例中,设置 - ALIYUN_COM_GPU_MEM_CONTAINER和- ALIYUN_COM_GPU_MEM_DEV环境变量指定显卡的总显存和容器内可见的显存。例如创建2个容器:- gpu_test1:分配6 GiB显存。 - sudo docker run -d -t --gpus all --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 --name gpu_test1 -v /mnt:/mnt -e ALIYUN_COM_GPU_MEM_CONTAINER=6 -e ALIYUN_COM_GPU_MEM_DEV=15 nvcr.io/nvidia/tensorflow:19.10-py3
- gpu_test2:分配8 GiB显存。 - sudo docker run -d -t --gpus all --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 --name gpu_test2 -v /mnt:/mnt -e ALIYUN_COM_GPU_MEM_CONTAINER=8 -e ALIYUN_COM_GPU_MEM_DEV=15 nvcr.io/nvidia/tensorflow:19.10-py3
 说明- 该命令以使用TensorFlow镜像 - nvcr.io/nvidia/tensorflow:19.10-py3为例,请根据实际情况更换为您自己的容器镜像。使用TensorFlow镜像搭建TensorFlow深度学习框架的操作,请参见部署NGC环境构建深度学习开发环境。
- 执行以下命令,查看容器的显存等GPU信息。 - sudo docker exec -i gpu_test1 nvidia-smi- 以gpu_test1为例,容器gpu_test1中可见的显存为6043 MiB,如下图所示:  
通过procfs节点查看cGPU服务
cGPU服务运行时会在/proc/cgpu_km下生成并自动管理多个procfs节点,您可以通过procfs节点查看和配置cGPU服务相关的信息。
- 执行以下命令,查看procfs节点信息。 - ls /proc/cgpu_km/- 执行结果如下所示:  
- 执行以下命令,查看GPU实例的显卡目录内容。 - 本示例中,以显卡0为例。 - ls /proc/cgpu_km/0- 执行结果如下所示:  
- 执行以下命令,查看容器对应的目录内容。 - 本示例中,以012b2edccd7a容器为例。 - ls /proc/cgpu_km/0/012b2edccd7a- 执行结果如下所示:  
- (可选)执行以下命令,配置cGPU服务。 - 了解procfs节点的用途后,您可以在GPU实例中执行命令进行切换调度策略、修改权重等操作,示例命令如下表所示。 - 命令 - 效果 - echo 2 > /proc/cgpu_km/0/policy - 将调度策略切换为权重抢占调度。 - cat /proc/cgpu_km/0/free_weight - 查看显卡上可用的权重。如果 - free_weight=0,新创建容器的权重值为0,该容器不能获取GPU算力,不能用于运行需要GPU算力的应用。- cat /proc/cgpu_km/0/$dockerid/weight - 查看指定容器的权重。 - echo 4 > /proc/cgpu_km/0/$dockerid/weight - 修改容器获取GPU算力的权重。 
通过cgpu-smi工具查看cGPU容器
您可以通过cgpu-smi工具查看cGPU容器的相关信息,包括容器ID、GPU利用率、算力限制、使用的显存以及分配显存的总量等信息。
cgpu-smi是cGPU的监控示例。部署k8s时,您可以参考或使用cgpu-smi的示例做二次开发集成。
cgpu-smi的监控展示信息如下所示:

升级或卸载cGPU服务
升级cGPU服务
升级cGPU服务支持冷升级和热升级两种方式。
- 冷升级 - Docker未使用cGPU服务的情况下,采用冷升级方式升级cGPU服务,操作步骤如下: - 执行以下命令,关闭所有运行中的容器。 - sudo docker stop $(docker ps -a | awk '{ print $1}' | tail -n +2)
- 执行以下命令,升级cGPU服务至最新版本。 - sudo sh upgrade.sh
 
- 热升级 - Docker使用cGPU服务的情况下,可以采用热升级方式升级cGPU内核驱动,但是对于升级的版本有一定限制。 如需任何协助,请联系阿里云售后技术团队。 
卸载cGPU服务
关于如何卸载节点上旧版本cGPU服务,具体操作,请参见通过命令升级节点cGPU版本。
cGPU服务使用示例
cGPU服务算力调度示例
cGPU服务加载cgpu_km的模块时,会按照容器最大数量(max_inst)为每张显卡设置时间片(X ms),用于为容器分配GPU算力,本示例中以Slice 1、Slice 2或Slice N表示。使用不同调度策略时的调度示例如下所示。
- 平均调度(policy=0) - 在创建容器时,为容器分配时间片。cGPU服务会从Slice 1时间片开始调度,提交任务到物理GPU,并执行一个时间片(X ms)的时间,然后切换到下一个时间片。每个容器获得的算力相同,都为 - 1/max_inst,如下所示。
- 抢占调度(policy=1) - 在创建容器时,为容器分配时间片。cGPU服务会从Slice 1开始调度,但如果没有使用某个容器,或者容器内没有进程打开GPU设备,则跳过调度,切换到下一个时间片。 - 示例如下: - 只创建一个容器Docker 1,获得Slice 1时间片,在Docker 1中运行2个TensorFlow进程,此时Docker 1最大获得整个物理GPU的算力。 
- 再创建一个容器Docker 2,获得Slice 2时间片。如果Docker 2内没有进程打开GPU设备,调度时会跳过Docker 2的时间片Slice 2。 
- 当Docker 2有进程打开GPU设备时,Slice 1和Slice 2都加入调度,Docker 1和Docker 2最大分别获得1/2物理GPU的算力,如下所示。 
 
- 权重抢占调度(policy=2) - 如果在创建容器时设置ALIYUN_COM_GPU_SCHD_WEIGHT大于1,则自动使用权重抢占调度。cGPU服务按照容器数量(max_inst)将物理GPU算力划分成max_inst份,但如果ALIYUN_COM_GPU_SCHD_WEIGHT大于1,cGPU服务会将多个时间片组合成一个更大的时间片分配给容器。 - 设置示例如下: - Docker 1:ALIYUN_COM_GPU_SCHD_WEIGHT=m 
- Docker 2:ALIYUN_COM_GPU_SCHD_WEIGHT=n 
 - 调度效果如下: - 如果只有Docker 1运行, Docker 1抢占整个物理GPU的算力。 
- 如果Docker 1和Docker 2同时运行,Docker 1和Docker 2获得的理论算力比例是m:n。和抢占调度不同的是,即使Docker 2中没有GPU进程也会占用n个时间片的时间。 说明- m:n设置为2:1和8:4时的运行表现存在差别。在1秒内切换时间片的次数,前者是后者的4倍。 
 - 权重抢占调度限制了容器使用GPU算力的理论最大值。但对算力很强的显卡(例如NVIDIA V100显卡),如果显存使用较少,在一个时间片内即可完成计算任务。此时如果m:n值设置为8:4,则剩余时间片内GPU算力会闲置,限制基本失效。 
- 固定算力调度(policy=3) - 您可以通过指定ALIYUN_COM_GPU_SCHD_WEIGHT和max_inst的占比,固定算力的百分比。 
- 算力弱调度(policy=4) - 在创建容器时,为容器分配时间片,隔离性弱于抢占调度。更多信息,请参见抢占调度(policy=1)。 
- 原生调度(policy=5) - 只用来做显存的隔离。原生调度表示NVIDIA GPU驱动本身的调度方式。 
算力调度策略支持阿里云所有的异构GPU实例,以及GPU实例所配备的NVIDIA显卡,其型号包含Tesla P4、Tesla P100、Tesla T4、Tesla V100、Tesla A10。以下测试项使用2个容器共享一台单卡A10的GPU实例,并将2个容器的算力比设置为1:2,将显存均分,每个容器的显存为12 G。
以下性能测试结果数据为实验室数据,仅供参考。
- 测试项1: 在基于TensorFlow框架训练的ResNet50模型、精度为FP16的场景下,测试不同batch_size下的性能数据比较。结果如下所示: - 框架 - 模型 - batch_size - 精度 - images/sec(容器1) - images/sec(容器2) - TensorFlow - ResNet50 - 16 - FP16 - 151 - 307 - TensorFlow - ResNet50 - 32 - FP16 - 204 - 418 - TensorFlow - ResNet50 - 64 - FP16 - 247 - 503 - TensorFlow - ResNet50 - 128 - FP16 - 257 - 516  
- 测试项2:在基于TensorRT框架训练的ResNet50模型、精度为FP16的场景下,测试不同batch_size下的性能数据比较。结果如下所示: - 框架 - 模型 - batch_size - 精度 - images/sec(容器1) - images/sec(容器2) - TensorRT - ResNet50 - 1 - FP16 - 568.05 - 1132.08 - TensorRT - ResNet50 - 2 - FP16 - 940.36 - 1884.12 - TensorRT - ResNet50 - 4 - FP16 - 1304.03 - 2571.91 - TensorRT - ResNet50 - 8 - FP16 - 1586.87 - 3055.66 - TensorRT - ResNet50 - 16 - FP16 - 1783.91 - 3381.72 - TensorRT - ResNet50 - 32 - FP16 - 1989.28 - 3695.88 - TensorRT - ResNet50 - 64 - FP16 - 2105.81 - 3889.35 - TensorRT - ResNet50 - 128 - FP16 - 2205.25 - 3901.94  
cGPU服务多卡划分示例
本示例以设置4卡为例,设置0卡为3 G、1卡为4 G、2卡为5 G、3卡为6 G。多卡划分示例代码如下所示:
docker run -d -t --runtime=nvidia  --name gpu_test0123 --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 -v /mnt:/mnt -e ALIYUN_COM_GPU_MEM_CONTAINER=3,4,5,6 -e ALIYUN_COM_GPU_MEM_DEV=23 -e NVIDIA_VISIBLE_DEVICES=0,1,2,3 nvcr.io/nvidia/tensorflow:21.03-tf1-py3
docker exec -i gpu_test0123   nvidia-smi执行结果显示如下,您可以看到4卡显存详情。
多卡设置显存参数(即ALIYUN_COM_GPU_MEM_CONTAINER参数)说明如下所示:
| 参数取值 | 说明 | 
| ALIYUN_COM_GPU_MEM_CONTAINER=3 | 表示4卡的显存都被设置为3 G。 | 
| ALIYUN_COM_GPU_MEM_CONTAINER=3,1 | 表示4卡的显存依次被设置为3 G、1 G、1 G、1 G。 | 
| ALIYUN_COM_GPU_MEM_CONTAINER=3,4,5,6 | 表示4卡的显存依次被设置为3 G、4 G、5 G、6 G。 | 
| ALIYUN_COM_GPU_MEM_CONTAINER未设置 | 表示禁用cGPU服务。 | 
| ALIYUN_COM_GPU_MEM_CONTAINER=0 | |
| ALIYUN_COM_GPU_MEM_CONTAINER=1,0,0 |