cGPU服务可以隔离GPU资源,实现多个容器共用一张显卡。本章节介绍如何在GPU实例上安装和使用cGPU服务。
前提条件
安装cGPU服务前,请完成以下准备工作:
- 获取cGPU安装包。
GPU容器共享技术cGPU仅对通过了企业实名认证的用户开放,暂不支持个人实名认证用户使用。
- 如果您是企业认证用户,请提交工单获取cGPU安装包。
- 如果您是个人实名认证用户,请通过ACK安装和使用cGPU服务,具体操作,请参见通过ACK安装并使用cGPU服务。
- 确保GPU实例满足以下要求:
- GPU实例规格为gn7i、gn6i、gn6v、gn6e、gn5i、gn5、ebmgn7i、ebmgn6i、ebmgn7e、ebmgn6e。
- GPU实例操作系统为CentOS 7.6、CentOS 7.7、Ubuntu 16.04、Ubuntu 18.04或Alibaba Cloud Linux。
- GPU实例已安装418.87.01或更高版本的NVIDIA驱动。
- GPU实例已安装19.03.5或更高版本的Docker。
背景信息
cGPU服务的隔离功能不支持以UVM的方式申请显存,即调用CUDA API cudaMallocManaged(),更多信息,请参见NVIDIA官方文档。请使用其他方式申请显存,例如调用cudaMalloc()等。
安装cGPU服务
- 下载并解压cGPU安装包。
- 执行以下命令,查看安装包文件。说明 安装包版本不同时,安装包中包含的文件可能也不同。
cd cgpu
ls
执行结果如下所示:cgpu.conf cgpu-km.h cgpu-procfs.c conf.h install.sh os-interface.c upgrade.sh cgpu-container-wrapper cgpu_mig cgpu-smi conflict.h living_upgrade.sh README uuid.conf cgpu-km.c cgpu.o check_conflict conflict.sh Makefile uninstall.sh version.h
- 执行以下命令,安装cGPU服务。
sh install.sh
- 执行以下命令,验证安装结果。
lsmod | grep cgpu
执行结果显示为如下cGPU服务的状态信息,表示已成功安装cGPU服务。cgpu_procfs 28202 0 cgpu_km_1_5_0 53695 1
运行cGPU服务
环境变量名称 | 取值类型 | 说明 | 示例 |
---|---|---|---|
CGPU_DISABLE | Boolean | 是否禁用cGPU服务,取值范围:
| 当不设置环境变量CGPU_DISABLE 时,默认值为false。通过设置环境变量 |
ALIYUN_COM_GPU_MEM_DEV | Integer | 设置GPU实例上每张显卡的总显存大小,和实例规格有关。 说明 显存大小按GiB取整数。 | 一台GPU实例规格为ecs.gn6i-c4g1.xlarge,配备1张NVIDIA ® Tesla ® T4显卡。在GPU实例上执行nvidia-smi查看总显存大小为15109 MiB,取整数为15 GiB。 |
ALIYUN_COM_GPU_MEM_CONTAINER | Integer | 设置容器内可见的显存大小,和ALIYUN_COM_GPU_MEM_DEV结合使用。如果不指定本参数或指定为0,则不使用cGPU服务,使用默认的NVIDIA容器服务。 | 在一张总显存大小为15 GiB的显卡上,设置环境变量ALIYUN_COM_GPU_MEM_DEV=15 和ALIYUN_COM_GPU_MEM_CONTAINER=1 ,效果是为容器分配1 GiB的显存。 |
ALIYUN_COM_GPU_VISIBLE_DEVICES | Integer或uuid | 指定容器内可见的GPU显卡。 | 在一台有4张显卡的GPU实例上,执行nvidia-smi -L查看GPU显卡设备号和UUID。返回示例如下所示:
然后,设置以下环境变量:
|
ALIYUN_COM_GPU_SCHD_WEIGHT | Integer | 设置容器的算力权重,取值范围:1~max_inst。 | 无 |
ALIYUN_COM_GPU_HIGH_PRIO | Integer | 设置容器的高优先级,取值范围:
建议每张GPU卡设置一个高优先级,多个高优先级容器之间遵循相应的调度策略(policy)。
| 0 |
以ecs.gn6i-c4g1.xlarge为例演示2个容器共用1张显卡。
- 执行以下命令,创建容器并设置容器内可见的显存。
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
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深度学习框架的操作,请参见在GPU实例上部署NGC环境。本示例中,通过设置环境变量ALIYUN_COM_GPU_MEM_CONTAINER和ALIYUN_COM_GPU_MEM_DEV指定显卡的总显存和容器内可见的显存。命令执行结果是创建了2个容器:- gpu_test1:分配6 GiB显存。
- gpu_test2:分配8 GiB显存。
- 执行以下命令,查看显存等GPU信息。本示例中,以gpu_test1为例。
docker exec -i gpu_test1 nvidia-smi
容器gpu_test1中可见的显存为6043 MiB,如下图所示:
查看procfs节点
cGPU服务运行时会在/proc/cgpu_km下生成并自动管理多个procfs节点,您可以通过procfs节点查看和配置cGPU服务相关的信息。下面介绍各procfs节点的用途。
- 执行以下命令,查看节点信息。
ls /proc/cgpu_km/
执行结果如下所示:0 default_memsize inst_ctl upgrade version
节点信息说明如下表所示:节点 读写类型 说明 0 读写 cGPU服务会针对GPU实例中的每张显卡生成一个的目录,并使用数字作为目录名称,例如0、1、2。本示例中只有一张显卡,对应的目录ID为0。 default_memsize 读写 如果没有设置ALIYUN_COM_GPU_MEM_CONTAINER参数,默认为新创建的容器分配的显存大小。 inst_ctl 读写 控制节点。 upgrade 读写 控制cGPU服务的热升级。 使用方式请参考 living_upgrade.sh
文件。说明 关于如何查看living_upgrade.sh
文件,具体操作,请参见步骤2。version 只读 cGPU的版本。 - 执行以下命令查看显卡对应的目录内容。本示例中,以显卡0为例。
ls /proc/cgpu_km/0
执行结果如下所示:012b2edccd7a 0852a381c0cf free_weight major max_inst policy prio_ratio
目录内容说明如下表所示:节点 读写类型 说明 容器对应的目录 读写 cGPU服务会针对运行在GPU实例中的每个容器生成一个的目录,并使用容器ID作为目录名称。您可以执行docker ps查看已创建的容器。 free_weight 只读 用于查询和修改可用的权重。如果 free_weight=0
,新创建容器的权重值为0,该容器不能获取GPU算力,不能用于运行需要GPU算力的应用。major 只读 表示cGPU的major number,即不同的设备类型。 max_inst 读写 用于设置容器的最大数量,取值范围为1~25。 policy 读写 cGPU服务支持以下算力调度策略: - 0:平均调度。每个容器占用固定的时间片,时间片占比为
1/max_inst
。 - 1:抢占调度。每个容器占用尽量多的时间片,时间片占比为
1/当前容器数
。 - 2:权重抢占调度。当ALIYUN_COM_GPU_SCHD_WEIGHT的取值大于1时,自动使用权重抢占调度。
- 3:固定算力调度。用于固定算力的百分比。
- 4:算力弱调度。隔离性弱于抢占调度。
- 5:原生调度。即GPU驱动本身的调度方式。
您可以通过修改policy的值实时调整调度策略。更多调度策略说明,请参见cGPU服务算力调度示例。
prio_ratio 读写 在线和离线混合使用场景下,高优先级容器最大可以抢占的算力。取值范围:20~99。 - 0:平均调度。每个容器占用固定的时间片,时间片占比为
- 执行以下命令查看容器对应的目录内容。本示例中,以012b2edccd7a容器为例。
ls /proc/cgpu_km/0/012b2edccd7a
执行结果如下所示:highprio id meminfo memsize weight
目录内容说明如下表所示:节点 读写类型 说明 highprio 读写 用于设置容器的优先级,默认值为0。 当
ALIYUN_COM_GPU_HIGH_PRIO
参数取值为1时,表示容器最大可以抢占prio_ratio
参数的算力。说明 该功能用于在线和离线混合使用场景。更多信息,请参见安装cGPU服务中的README说明。id 只读 容器的ID。 memsize 读写 用于设置容器内的显存大小。cGPU服务会根据ALIYUN_COM_GPU_MEM_DEV参数自动设定此值。 meminfo 只读 包括容器内剩余显存容量、正在使用GPU的进程ID及其显存用量。输出如下所示: Free: 6730809344 PID: 19772 Mem: 200278016
weight 读写 用于设置容器获取显卡最大算力的权重,默认值为1。所有运行中的容器的权重之和必须小于等于max_inst。
命令 | 效果 |
---|---|
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服务
升级cGPU服务支持冷升级和热升级两种方式。
- 冷升级
Docker未使用cGPU服务的情况下,采用冷升级方式升级cGPU服务,操作步骤如下:
- 执行以下脚本,关闭所有运行中的容器。
docker stop (docker ps -a | awk '{ print $1}' | tail -n +2)
- 执行
upgrade.sh
脚本,升级cGPU服务至最新版本。sh upgrade.sh
- 执行以下脚本,关闭所有运行中的容器。
- 热升级
Docker使用cGPU服务的情况下,采用热升级方式升级cGPU服务。操作步骤如下:
执行living_upgrade.sh
脚本,升级cGPU服务至最新版本。sh living_upgrade.sh
卸载cGPU服务
- 关闭所有运行中的容器。
docker stop (docker ps -a | awk '{ print $1}' | tail -n +2)
- 执行
uninstall.sh
脚本,卸载cGPU服务。sh uninstall.sh
通过cgpu-smi监控容器
cgpu-smi的监控展示信息如下所示:

cGPU服务算力调度示例
- 平均调度(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驱动本身的调度方式。
- 测试项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
从上述实际测试结果可以看出,基于不同框架下的性能结果接近1:2,调度策略算法能够支持Ampere系列显卡。以下测试以其它系列显卡T4和P4为例,基于TensorFlow框架推理ResNet50模型,实测性能结果同样接近1:2。


综合以上不同场景,不同算力的GPU显卡测试数据,使用cGPU能够指定每个容器的算力大小,限制了多容器之间的争抢,从而保障高优先级的容器获得更多的算力。算力调度算法能够支持Ampere、Volta、Turing以及Pascal等多种NVIDIA GPU架构,可以为企业客户定制适合实际场景的调度策略。
cGPU服务多卡划分示例
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

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 |