2. 版本申明¶
版本 | 修改内容 | 修改时间 |
---|---|---|
v1.0 | 初始化 | 8/15/2022 |
v1.1 | 格式调整 | 9/1/2022 |
v1.2 | 更新一些格式以及内容 | 4/17/2024 |
v1.3 | 更新一些格式以及内容 | 8/8/2024 |
v1.4 | 更新efsmi支持版本 | 11/8/2024 |
v1.5 | 更新一些内容 | 12/5/2024 |
v2.0 | gcushare调度器升级 | 2/27/2025 |
v2.1 | gcushare支持GCU资源隔离 | 3/10/2025 |
v2.2 | 调度插件兼容多版k8s | 3/26/2025 |
v2.3 | 支持多进程DRS调度 | 5/13/2025 |
3. 简介¶
3.1. 背景¶
Kubernetes基础设施使用GCU设备时,是不支持多个pod共享GCU的,这样可以实现更好的隔离,保证每个应用的GCU使用不受其它应用的影响,非常适合深度学习模型训练。但是对于想要提高集群中GCU利用率的用户来说,这样通常会造成GCU资源的浪费。比如:
作为集群管理员,在开发过程中,允许多个用户在同一个GCU上共享同一个模型开发环境,以增加集群的GCU使用率。
作为一名应用操作员,希望能够同时在同一个GCU上运行多个推理任务。
针对这些场景,就需要实现GCU设备的共享功能,以满足特定场景下的用户需求。
3.2. 查看版本号¶
1)执行./gcushare-device-plugin --version
,即可查看gcushare-device-plugin版本号;
2)执行./gcushare-scheduler-plugin --version
, 即可查看gcushare-scheduler-plugin版本号;
3.4. GCUShare功能概要¶
1)GCUShare支持调度级别的GCU设备共享(申请enflame.com/shared-gcu),也支持共享GCU的资源隔离(申请enflame.com/drs-gcu);
2)共享调度(申请enflame.com/shared-gcu或enflame.com/drs-gcu)和整卡调度(申请enflame.com/gcu)调度的方式不可以在集群内并存;
3)支持GCU掉卡场景以及卡片重新恢复时,集群内可用共享GCU资源的动态更新;
4)GCUShare依赖于enflame gcu driver和enflame gcu container toolkit,部署GCUShare组件前,必须按顺序部署好这两个依赖组件(gcu-operator2.0已支持GCUShare组件和依赖组件的一键部署);
5)GCUShare提供了inspect接口,用户可以访问该接口来查询集群所有共享GCU的使用情况,从而对整体资源的使用有一个初步的判断;
6)GCUShare支持用户在非DRS场景下自定义共享GCU设备的切片数(sliceCount),在DRS场景下当前只支持1切6;
7)通常组件日志都存储在容器中,一旦容器重启或组件卸载,很容易造成日志丢失。GCUShare组件的日志采用本地持久化存储方式,用户可通过日志自行定位问题;
8)GCUShare支持由用户指定部分节点共享GCU以及部分节点使用资源隔离。
4. 安装GCUShare组件¶
安装说明:
gcushare-scheduler-plugin组件依赖于gcushare-device-plugin组件,因此需要先安装gcushare-device-plugin组件;
环境提前安装好k8s >= v1.18,docker,helm3,enflame driver,enflame docker等组件。
本手册所使用测试环境安装了单节点k8s集群(version=1.32)。我们称该节点为节点1,下文同。
4.1. 制作gcushare组件镜像¶
镜像构建工具由"build-image.conf+build-image.sh" 组成,用户可以根据需要修改"build-image.conf"里的内容满足定制化需求, 然后执行 "build-image.sh" 生成容器镜像,基本过程如下:
1)镜像配置文件定制
"build-image.conf"内容如下,用户可以根据实际需要自行完成内容修改:
# Currently supports ubuntu, tlinux, openeuler
OS="ubuntu"
# Currently supports docker, ctr, podman, nerdctl
CLI_NAME="docker"
# The repository name
REPO_NAME="artifact.enflame.cn/enflame_docker_images/enflame"
# The image name
IMAGE_NAME="gcushare-device-plugin"
# The image tag
TAG="latest"
# The namespace used by nerdctl, ctr
NAMESPACE="k8s.io"
2)镜像构建
"build-image.sh" 使用说明如下:
# ./build-image.sh -h
Usage: ./build-image.sh [OPTIONS]...
Description: This script is used to build and save images.
Options:
--os Specify the operating system for the image. \
Currently supports "ubuntu, tlinux and openeuler", default: "ubuntu".
--cli Specify the CLI tool for building the image. \
Currently supports "docker, ctr, podman and nerdctl", default: "docker".
--repo Specify the repository for the image that will be built, \
default: "artifact.enflame.cn/enflame_docker_images/enflame".
--name Specify the name of the image that will be built, \
default: "gcushare-device-plugin".
--tag Specify the tag for the image that will be built, default: "latest".
--namespace Specify the namespace for the image that will be built by \
nerdctl and ctr, default: "k8s.io".
Examples:
./build-image.sh
./build-image.sh --cli podman --os ubuntu
./build-image.sh --cli nerdctl --os openeuler
./build-image.sh --cli ctr --os ubuntu
用户在完成"build-image.conf"里的内容定制化需求后,可以直接执行 "build-image.sh" 生成容器镜像:
# 默认 CLI: docker, OS: ubuntu
./build-image.sh
对于k8s version >=1.24,推荐采用 nerdctl 或 ctr构建:
./build-image.sh --cli nerdctl
或
./build-image.sh --cli ctr
4.2. 安装gcushare组件¶
安装gcushare-device-plugin¶
gcushare-device-plugin将注册两种资源:enflame.com/shared-gcu和enflame.com/drs-gcu;
修改组件启动时的sliceCount,将作用于enflame.com/shared-gcu资源;
gcushare-device-plugin组件安装时,支持用户指定要在哪些节点共享GCU设备。声明使用共享GCU资源的pod只会调度到这些节点上;
在gcushare-device-plugin安装包目录下,执行./deploy.sh一键安装gcushare-device-plugin组件。
gcushare-device-plugin_{VERSION} # ./deploy.sh
Cluster node name list:
[my-cluster-control-plane
my-cluster-control-plane
my-cluster-control-plane2]
Please input gcushare node name and separated by space(if all \
nodes use shared GCU, you can just press Enter):
......
deploy.sh主要做了两件事:
询问并请用户输入需要共享GCU设备的节点名称,并给这些节点自动打上"enflame.com/gcushare": "true"标签。只有打了该标签的节点才会部署gcushare-device-plugin组件;
如果节点安装有helm组件,则使用helm部署gcushare-device-plugin的release;否则使用kubectl直接安装yaml文件。
可以通过查看gcushare-device-plugin的pod信息,来确认gcushare-device-plugin是够运行正常:
gcushare-device-plugin_{VERSION} # kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system gcushare-device-plugin-n6c5w 1/1 Running 0 26h
......
再检查下节点的"enflame.com/gcu-count"字段和"enflame.com/shared-gcu"字段是否更新:
gcushare-device-plugin_{VERSION} # kubectl get node
NAME STATUS ROLES AGE VERSION
sse-lab-inspur-048 Ready control-plane,master 27h v1.20.0
gcushare-device-plugin_{VERSION} # kubectl describe node sse-lab-inspur-048
......
Capacity:
cpu: 80
enflame.com/drs-gcu: 12 # DRS资源,每张卡仅支持1切6
enflame.com/gcu-count: 2 # 当前节点有2张GCU卡
enflame.com/shared-gcu: 12 # 共享资源,每张卡默认支持1切6,可修改启动参数sliceCount进行控制
ephemeral-storage: 1345603940Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 394869612Ki
pods: 110
Allocatable:
cpu: 80
enflame.com/drs-gcu: 12
enflame.com/gcu-count: 2
enflame.com/shared-gcu: 12
ephemeral-storage: 1240108589051
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 394767212Ki
pods: 110
安装gcushare-scheduler-plugin组件¶
在gcushare-scheduler-plugin安装包目录下,执行./deploy.sh一键安装gcushare-scheduler-plugin组件:
gcushare-scheduler-plugin_{VERSION} # ./deploy.sh
deploy.sh主要做了一件事:
如果节点安装有helm组件,则使用helm部署gcushare-scheduler-plugin的release;否则使用kubectl直接安装yaml文件。
同样的,我们可以通过查询pod确定gcushare-scheduler-plugin运行正常:
gcushare-scheduler-plugin_{VERSION} # kubectl get pod -A
NAMESPACE NAME \
READY STATUS RESTARTS AGE
......
kube-system gcushare-device-plugin-n6c5w \
1/1 Running 0 26h
kube-system gcushare-scheduler-plugin-9b57bd745-rxd6r \
1/1 Running 0 26h
......
我们也可以通过简单的接口访问来测试下组件是否能正常提供服务:
# kubectl get svc -A|grep gcushare-scheduler-plugin
kube-system gcushare-scheduler-plugin ClusterIP \
10.96.1.37 <none> 32766/TCP 106s
# curl 10.96.1.37:32766/version
2.0.0
gcushare-scheduler-plugin组件使用service转发访问,端口为32766。访问上述URL,将会返回gcushare-scheduler-plugin版本号信息,说明组件正常运行。
5. 使用共享GCU¶
注意:
申请enflame.com/shared-gcu的pod下文称为gcushare pod;申请enflame.com/drs-gcu的pod下文称为drs pod;
申请共享GCU的pod必须明确指定调度器名称为gcushare-scheduler。
一个pod内或一个容器内不可以同时申请enflame.com/shared-gcu和enflame.com/drs-gcu。
enflame.com/shared-gcu: 1,表示申请1个共享GCU,实际单位取决于部署gcushare-device-plugin时的共享方式。如果sliceCount=6(默认共享方式),则1表示使用1/6张卡;如果sliceCount=4,则1表示使用1/4张卡。
5.1. inspect脚本¶
在部署pod申请共享GCU之前,可以先通过inspect脚本来查询节点共享GCU或DRS GCU的使用情况。
# ./inspect.sh -h
Usage: ./inspect.sh <param name> <param value>
Description: This script is used to display the usage of GCU devices.
Param:
--node Optional. Query the usage of the GCU device of the specified node, \
by default, all nodes are queried.
--drs Optional. Whether to query the usage of drs GCU, default 'false'
Example:
./inspect.sh --node node1 --drs true
5.3. 申请DRS GCU场景示例¶
使用方法:
apiVersion: v1
kind: Pod
metadata:
name: gcushare-pod-1
namespace: kube-system
spec:
schedulerName: gcushare-scheduler # 必须指定调度器名称
containers:
- name: pod-gcu-example
resources:
limits:
enflame.com/drs-gcu: 1 # 声明申请一个共享GCU,即1/6张卡
gcushare-scheduler-plugin提供了drs pod的示例yaml文件,目录:deployments/example/drs-pod.yaml。以下场景的测试文件均为基于该模板修改。
1)部署一个pod,申请1个drs GCU,pod可以正常运行
example # kubectl create -f drs-pod.yaml
pod/drs-pod-1 created
example # kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
......
kube-system drs-pod-1 1/1 Running 0 4s
通过efsmi命令可以查看gcushare为该pod创建的drs实例:
# efsmi
......
+-----------------------------------------------------------------------------+
| GCU instances: |
|----------------------------------+----------------------------+-------------|
| GCU Name ID | Mem (Usage/Total) | SIP |
+----------------------------------+----------------------------+-------------+
| 0 1g.7gb 1 | 0MiB / 7948MiB | 4 |
+----------------------------------+----------------------------+-------------+
也可以通过inspect.sh查看drs gcu的使用情况:
# ./inspect.sh --drs true --node my-cluster-control-plane
输出如下(为便于注释,这里将输出的json文本转换为了yaml格式):
name: my-cluster-control-plane
totalGCU: 12
usedGCU: 1
availableGCU: 11
devices:
'0':
totalGCU: 6
usedGCU: 0
availableGCU: 6
pods: []
'1':
virt: DRS
totalGCU: 6
usedGCU: 1
availableGCU: 5
pods:
- name: drs-pod-1
namespace: kube-system
uid: e1b83d13-0373-4b34-acd4-c34f02538ce0
creationTimestamp: '2025-05-14T07:00:34Z'
usedGCU: 1
phase: Running
containers:
pod-gcu-example:
allocated: true
request: 1 # pod申请1个DRS GCU
profileID: '0' # pod使用的profile ID
profileName: 1g.7gb # pod使用的profile name
instanceID: '1' # 为pod创建的instance ID
可以看到pod成功申请到了GCU1(注意这里的GCU1是efsmi所看到的GCU0)。
2)部署pod2,申请3个drs GCU,pod可以正常运行
example # kubectl create -f drs-pod-2.yaml
pod/drs-pod-2 created
example # kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
......
kube-system drs-pod-1 1/1 Running 0 20s
kube-system drs-pod-2 1/1 Running 0 4s
查看节点drs gcu的使用情况:
{
"name": "my-cluster-control-plane",
"totalGCU": 12,
"usedGCU": 4,
"availableGCU": 8,
"devices": {
"0": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"1": {
"virt": "DRS",
"totalGCU": 6,
"usedGCU": 4,
"availableGCU": 2,
"pods": [
{
"name": "drs-pod-1",
"namespace": "kube-system",
"uid": "e1b83d13-0373-4b34-acd4-c34f02538ce0",
"creationTimestamp": "2025-05-14T07:00:34Z",
"usedGCU": 1,
"phase": "Running",
"containers": {
"pod-gcu-example": {
"allocated": true,
"request": 1,
"profileID": "0",
"profileName": "1g.7gb",
"instanceID": "1"
}
}
},
{
"name": "drs-pod-2",
"namespace": "kube-system",
"uid": "1b4a1976-0850-496b-a9bc-47ff40a11d09",
"creationTimestamp": "2025-05-14T07:00:50Z",
"usedGCU": 3,
"phase": "Running",
"containers": {
"pod-gcu-example": {
"allocated": true,
"request": 3,
"profileID": "1",
"profileName": "3g.23gb",
"instanceID": "2"
}
}
}
]
}
}
}
可以看到pod2也成功申请到了GCU1。
3)部署pod3,申请2个drs GCU,pod无法调度
example # kubectl create -f drs-pod-2.yaml
pod/drs-pod-2 created
example # kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
......
kube-system drs-pod-1 1/1 Running 0 20s
kube-system drs-pod-2 1/1 Running 0 4s
kube-system drs-pod-3 0/1 Pending 0 1s
查看pod事件:
# kubectl describe pod drs-pod-3 -n kube-system
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 58s gcushare-scheduler running \
"GCUShareSchedulerPlugin" filter plugin: configmap: \
drs-pod-3.kube-system.db79de52.my-cluster-control-plane.configmap \
filter node: my-cluster-control-plane for pod(name: drs-pod-3,\
uuid: db79de52-b004-4796-b629-7d7e9d98e638) failed: configmap:\
drs-pod-3.kube-system.db79de52.my-cluster-control-plane.configmap\
pod exit container request: 2g, but the profile associated with it does not exist
可以看到,即使drs gcu资源充足,但不存在支持申请2/6的profile,pod无法调度(当前申请DRS时,仅支持1/6,3/6或6/6)。
5.4. 混合部署模式¶
gcushare支持既部署gcushare pod,又部署drs pod。即,先部署一个gcushare pod,再部署一个drs pod是允许的。
先部署1个gcushare pod申请1个shared gcu
# kubectl create -f example/gcushare-pod.yaml
pod/gcushare-pod-1 created
# kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system gcushare-pod-1 1/1 Running 0 2s
......
再部署1个drs pod申请3个shared gcu
# kubectl create -f example/drs-pod.yaml
pod/drs-pod-1 created
# kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system drs-pod-1 1/1 Running 0 2s
......
查看shared gcu的使用情况:
./inspect.sh --node my-cluster-control-plane
{
"name": "my-cluster-control-plane",
"totalGCU": 6,
"usedGCU": 1,
"availableGCU": 5,
"devices": {
"0": {
"virt": "Shared",
"totalGCU": 6,
"usedGCU": 1,
"availableGCU": 5,
"pods": [
{
"name": "gcushare-pod-1",
"namespace": "kube-system",
"uid": "57ac2683-be74-45fc-9d77-63e9bfe33c76",
"creationTimestamp": "2025-05-14T07:24:01Z",
"usedGCU": 1,
"phase": "Running",
"containers": {
"pod-gcu-example": {
"allocated": true
}
}
}
]
}
}
}
可以看到gcushare pod占用了GCU0,由于drs pod占用了GCU1,因此GCU1对gcushare pod已不再可用。
可以通过drs参数查看drs gcu的使用情况:
./inspect.sh --node my-cluster-control-plane --drs true
{
"name": "my-cluster-control-plane",
"totalGCU": 6,
"usedGCU": 3,
"availableGCU": 3,
"devices": {
"1": {
"virt": "DRS",
"totalGCU": 6,
"usedGCU": 3,
"availableGCU": 3,
"pods": [
{
"name": "drs-pod-1",
"namespace": "kube-system",
"uid": "1d79daf8-e77a-439b-8f3b-901d09d7b2a7",
"creationTimestamp": "2025-05-14T07:33:25Z",
"usedGCU": 3,
"phase": "Running",
"containers": {
"pod-gcu-example": {
"allocated": true,
"request": 3,
"profileID": "1",
"profileName": "3g.23gb",
"instanceID": "1"
}
}
}
]
}
}
}
同样,由于gcushare pod占用了GCU0,因此GCU0对drs pod来说也不再可用。
注意,未被任何pod占用的GCU对drs pod和gcushare pod来说都是可用的,遵循先到先得原则。
5.5. 修改GCU的共享切片数¶
GCUShare支持用户指定shared gcu的共享切片数。如果你想要将每个GCU切分成4份,那么在安装gcushare-device-plugin组件前,只需要修改gcushare-device-plugin-chart中values文件的sliceCount字段即可:
gcushare-device-plugin_{VERSION} # vim gcushare-device-plugin-chart/values.yaml
# Default values for gcushare-device-plugin-chart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
sliceCount: 4 # sliceCount表示将单个GCU设备共享为指定份数
...
修改后保存values文件,然后再部署gcushare-device-plugin组件即可生效。使用方式不变:
示例1:
enflame.com/shared-gcu: 1 # 表示申请1/4张卡。
示例2:
enflame.com/shared-gcu: 2 # 表示申请2/4张卡。
注意:
该功能仅对shared gcu生效,不支持显存隔离;
sliceCount是在部署组件前要确定好的,如果你已经部署成功了gcushare相关组件,那么在运行过程中,请不要再去修改sliceCount字段,否则可能会造成调度异常。
如果在运行过程中一定要修改sliceCount字段,必须先把正在使用shared GCU的pod全部清除,然后重新安装gcushare的两个组件。
5.6. 查看日志¶
通常组件的运行日志都存放在容器中,这样就容易带来一些问题,一是一旦容器重启或者组件被卸载,会造成日志的丢失;二是难以根据异常信息找到代码的调用栈。这些问题都将增大故障排查的复杂程度,而用户则完全无从下手进行排查,进而大大增大了开发人员的运维负担。而GCUShare提供了日志的本地存储功能,如果你的组件运行异常,或者使用GCUShare出现了问题,都可以通过查看日志,进行初步定位。
日志存放目录:/var/log/enflame/gcushare。
1)查看gcushare-scheduler-plugin组件日志:
/var/log/enflame/gcushare # vim gcushare-scheduler-plugin.log
2)查看gcushare-device-plugin组件日志:
/var/log/enflame/gcushare # vim gcushare-device-plugin.log
6. 组件卸载¶
通过release目录下delete.sh一键卸载gcushare组件。
6.1. gcushare-scheduler-plugin卸载¶
gcushare-scheduler-plugin_{VERSION} # ./delete.sh
6.2. gcushare-device-plugin卸载¶
gcushare-device-plugin_{VERSION} # ./delete.sh
组件卸载后,节点上的"enflame.com/gcushare": "true"标签将自动清除。
注意,如果仍然存在drs pod在运行,将遇到以下提示:
# ./delete.sh
Warning!!! The following pods are found to be using drs instance:
name: drs-pod-1, namespace: kube-system
If you delete these pods after the gcushare device plugin is uninstalled, \
the corresponding drs will not be automatically cleaned up.
It is recommended to delete these pods before uninstalling the gcushare device plugin,\
otherwise the remaining drs cannot be used by gcushare again until you manually \
delete these remaining drs.
Continue uninstalling gcushare device plugin?(y/yes, n/no)
此时,建议输入n/no退出卸载流程,然后先删除所有drs pod后重新卸载gcushare设备插件。因为gcushare设备插件负责drs instance的创建,删除等工作,如果先卸载了gcushare设备插件,然后卸载drs pod时,会残留drs instance无法自动清理,后续再部署drs pod时,会导致资源不足,除非你手动删除残留的drs资源。
7. 常见问题¶
1)按卡调度和共享调度为什么不可以共存?
整卡调度和共享调度采用的是两个完全没有关联的k8s设备插件。二者有自己的调度逻辑,而且无法感知到对方的调度记录,若同时存在,将造成调度混乱。
2)gcushare如何实现底层的显存分配?
用户部署pod申请drs gcu后,设备插件将向底层runtime传递相应的环境变量,实现底层的隔离。
3)组件卸载后,已经使用共享GCU的pod业务会受影响吗?
卸载gcushare调度插件没有影响;
卸载gcushare设备插件对gcushare pod没有影响;但会影响drs pod,因此卸载gcushare设备插件前请先删除所有drs pod。
4)gcushare组件重启后是否会影响后续的调度?
不会。gcushare调度pod时,可以实时获取到集群上申请共享GCU的pod信息,通常不会出现数据不一致的场景。
5)gcushare支持单个pod内多个容器申请共享GCU吗?
支持,但单个pod内所有申请共享GCU的容器的申请总和不得超过单张GCU卡的内存大小。
6)gcushare在运行过程中,如果掉了一张卡需要重启gcushare吗?或者如果卡片重新恢复了,需要重启gcushare吗?
这两种场景都不需要重启gcushare,gcushare可以自动适应掉卡以及卡片恢复等场景。
7)可以手动创建drs吗?
在使用gcushare的DRS功能时,请不要手动创建DRS,所有GCU设备均应交给gcushare自动化管理,否则可能造成调度混乱。
8. 已知问题¶
1)在k8s1.22版本中,使用gcushare部署多容器pod申请共享GCU时存在kubelet多调用一次Allocate接口的现象,导致pod容器创建失败。该问题经验证,确认属于k8s1.22版本存在的问题,在k8s其它版本中暂未发现。如果遇到该问题,建议升级k8s到1.24及以上版本。