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 |
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设备共享,也支持共享GCU的资源隔离(默认开启);
2)共享调度(申请enflame.com/shared-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支持用户自定义共享GCU设备的切片数(sliceCount);
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
5. 使用共享GCU¶
使用共享GCU需要在容器内编排”enflame.com/shared-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/shared-gcu: 1 # 声明申请一个共享GCU,即1/6张卡
注意:
申请共享GCU的pod必须明确指定调度器名称为gcushare-scheduler。
“enflame.com/shared-gcu”: 1,表示申请1个共享GCU,实际单位取决于部署gcushare-device-plugin时的共享方式。如果sliceCount=6(默认共享方式),则1表示使用1/6张卡;如果sliceCount=4,则1表示使用1/4张卡。
5.1. 场景示例¶
gcushare-scheduler-plugin提供了pod的示例yaml文件,目录:deployments/example/gcushare-pod.yaml。以下场景的测试文件均为基于该模板修改。
1)部署一个pod,申请1个共享GCU,pod可以正常运行
example # kubectl create -f gcushare-pod-1.yaml
pod/gcushare-pod-1 created
example # kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
......
kube-system gcushare-pod-1 1/1 Running 0 4s
可以看到,pod运行正常。然后可以通过kubectl exec命令确认GCU设备是否被成功挂载到容器内:
example # kubectl exec -it gcushare-pod-1 -n kube-system -- ls /dev
core gcu3vid0 gcu3vid4 gcuctl pts stdin urandom
fd gcu3vid1 gcu3vid5 mqueue random stdout zero
full gcu3vid2 gcu3vid6 null shm termination-log
gcu3 gcu3vid3 gcu3vid7 ptmx stderr tty
可以看到,pod成功挂载了GCU3。
此外,gcushare-scheduler-plugin组件提供了inspect接口可以用来查询集群所有节点的GCU使用情况。使用示例:
inspect.sh {node-name}
其中,node-name为可选参数,为空将输出所有节点的gcu使用信息;否则输出指定节点的gcu使用信息。
gcushare-scheduler-plugin_{VERSION} # ./inspect.sh
输出信息如下所示(为便于注释,这里将输出的json文本转换为了yaml格式):
- name: my-cluster-worker # 节点名称
totalGCU: 48 # 当前节点共享GCU总数
usedGCU: 1 # 当前节点已经使用的共存GCU总数
availableGCU: 47 # 当前节点剩余可用的共存GCU总数
devices: # 每个GCU设备的具体使用信息
'0':
totalGCU: 6
usedGCU: 0
availableGCU: 6
pods: []
'1':
totalGCU: 6
usedGCU: 0
availableGCU: 6
pods: []
'2':
totalGCU: 6
usedGCU: 0
availableGCU: 6
pods: []
'3': # 当前GCU设备的使用情况
totalGCU: 6 # 当前GCU可以共享的总数
usedGCU: 1 # 当前GCU已经使用的数目
availableGCU: 5 # 当前GCU剩余可用的数目
pods: # 使用当前GCU的全部pod信息
- name: gcushare-pod-1
namespace: kube-system
uid: 9223275a-2d3f-4e1a-9207-c5576a919f5c
creationTimestamp: '2025-02-21T02:44:38Z'
usedGCU: 1
phase: Running
'4':
totalGCU: 6
usedGCU: 0
availableGCU: 6
pods: []
'5':
totalGCU: 6
usedGCU: 0
availableGCU: 6
pods: []
'6':
totalGCU: 6
usedGCU: 0
availableGCU: 6
pods: []
'7':
totalGCU: 6
usedGCU: 0
availableGCU: 6
pods: []
2)再部署一个pod,申请1个共享GCU,则该pod将优先使用节点1的GCU3卡。
example # kubectl create -f gcushare-pod-2.yaml
pod/gcushare-pod-2 created
example # kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
......
kube-system gcushare-pod-1 1/1 Running 0 7s
kube-system gcushare-pod-2 1/1 Running 0 5s
example # kubectl exec -it gcushare-pod-2 -n kube-system -- ls /dev
core gcu3vid0 gcu3vid4 gcuctl pts stdin urandom
fd gcu3vid1 gcu3vid5 mqueue random stdout zero
full gcu3vid2 gcu3vid6 null shm termination-log
gcu3 gcu3vid3 gcu3vid7 ptmx stderr tty
可以看到,pod2也成功被调度到了节点1,并绑定了GCU3卡。查看节点的GCU使用信息:
gcushare-scheduler-plugin_{VERSION} # ./inspect.sh my-cluster-worker
输出如下:
{
"name": "my-cluster-worker",
"totalGCU": 48,
"usedGCU": 2,
"availableGCU": 46,
"devices": {
"0": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"1": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"2": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"3": {
"totalGCU": 6,
"usedGCU": 2,
"availableGCU": 4,
"pods": [
{
"name": "gcushare-pod-1",
"namespace": "kube-system",
"uid": "9223275a-2d3f-4e1a-9207-c5576a919f5c",
"creationTimestamp": "2025-02-21T02:44:38Z",
"usedGCU": 1,
"phase": "Running"
},
{
"name": "gcushare-pod-2",
"namespace": "kube-system",
"uid": "cec755a6-a074-4b2a-822c-0a16b5479a20",
"creationTimestamp": "2025-02-21T09:37:01Z",
"usedGCU": 1,
"phase": "Running"
}
]
},
"4": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"5": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"6": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"7": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
}
}
}
注意:在满足pod申请资源的前提下,gcushare-scheduler-plugin将优先为pod分配剩余可使用共享GCU数目更少的GCU设备。
3)部署第3个pod,申请6个共享GCU,pod将占一张整卡。
example # kubectl create -f gcushare-pod-3.yaml
pod/gcushare-pod-3 created
example # kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
......
kube-system gcushare-pod-1 1/1 Running 0 9s
kube-system gcushare-pod-2 1/1 Running 0 7s
kube-system gcushare-pod-3 1/1 Running 0 5s
example # kubectl exec -it gcushare-pod-3 -n kube-system -- ls /dev
core gcu5vid0 gcu5vid4 gcuctl pts stdin urandom
fd gcu5vid1 gcu5vid5 mqueue random stdout zero
full gcu5vid2 gcu5vid6 null shm termination-log
gcu5 gcu5vid3 gcu5vid7 ptmx stderr tty
可以看到pod3使用了GCU5卡。查看节点GCU使用情况:
{
"name": "my-cluster-worker",
"totalGCU": 48,
"usedGCU": 8,
"availableGCU": 40,
"devices": {
"0": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"1": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"2": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"3": {
"totalGCU": 6,
"usedGCU": 2,
"availableGCU": 4,
"pods": [
{
"name": "gcushare-pod-1",
"namespace": "kube-system",
"uid": "9223275a-2d3f-4e1a-9207-c5576a919f5c",
"creationTimestamp": "2025-02-21T02:44:38Z",
"usedGCU": 1,
"phase": "Running"
},
{
"name": "gcushare-pod-2",
"namespace": "kube-system",
"uid": "cec755a6-a074-4b2a-822c-0a16b5479a20",
"creationTimestamp": "2025-02-21T09:37:01Z",
"usedGCU": 1,
"phase": "Running"
}
]
},
"4": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"5": {
"totalGCU": 6,
"usedGCU": 6,
"availableGCU": 0,
"pods": [
{
"name": "gcushare-pod-3",
"namespace": "kube-system",
"uid": "c6481294-373a-4c7e-89f1-7070e13986b6",
"creationTimestamp": "2025-02-21T09:58:07Z",
"usedGCU": 6,
"phase": "Running"
}
]
},
"6": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
},
"7": {
"totalGCU": 6,
"usedGCU": 0,
"availableGCU": 6,
"pods": []
}
}
}
4)部署pod4,使用8个共享GCU,此时pod将无法调度到节点,因为GCUShare限定了单个pod最大可申请的显存数为单张整卡。
example # kubectl create -f gcushare-pod-4.yaml
pod/gcushare-pod-4 created
example # kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
......
kube-system gcushare-pod-1 1/1 Running 0 11s
kube-system gcushare-pod-2 1/1 Running 0 9s
kube-system gcushare-pod-3 1/1 Running 0 7s
kube-system gcushare-pod-4 0/1 Pending 0 6s
查看pod事件,显示单个GCU卡显存不足。
example # kubectl describe pod gcushare-pod-4 -n kube-system
Name: gcushare-pod-4
Namespace: kube-system
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 15s gcushare-scheduler 0/1 nodes are available: \
1 pod(name: gcushare-pod-4, uuid: f8d4ee96-aa37-426d-89dd-4aecfd295e0d) request \
gcu: 8, but it is insufficient of node: my-cluster-worker. preemption: 0/1 \
nodes are available: 1 No preemption victims found for incoming pod.
5.2. 修改GCU的共享切片数¶
GCUShare支持用户指定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张卡。
注意:
1)sliceCount是在部署组件前要确定好的,如果你已经部署成功了gcushare相关组件,那么在运行过程中,请不要再去修改sliceCount字段,否则可能会造成调度异常。
2)如果在运行过程中一定要修改sliceCount字段,必须先把正在使用共享GCU的pod全部清除,然后重新安装gcushare的两个组件。
3)要尽量将共享切片数sliceCount设置的小一些,因为如果将GCU设备切分的“比较碎”的情况下,可能会产生一些资源碎片,导致GCU无法被充分利用。例如,想要使用1/2张卡,可以将sliceCount设置为2,然后设置enflame.com/shared-gcu: 1,而不是将sliceCount设置为4,pod设置enflame.com/shared-gcu: 2,等方案。
5.3. 关闭资源隔离¶
gcushare-device-plugin默认开启了共享GCU的资源隔离功能,但如果你只需要在调度层面共享GCU,而不关注资源隔离,那么在部署gcushare-device-plugin之前,可以将gcushare-device-plugin-chart中values文件的resourceIsolation设置为false即可:
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.
resourceIsolation: false # 关闭资源隔离功能
...
但上述方式会导致所有节点均启用或关闭资源隔离功能。因此,gcushare-device-plugin也支持通过给节点打标签的方式实现部分节点启用或关闭资源隔离功能。
示例1:节点my-cluster-worker关闭资源隔离功能
kubectl label node my-cluster-worker enflame.com/gcushare-resource-isolation=false --overwrite
示例2:节点my-cluster-worker启用资源隔离功能
kubectl label node my-cluster-worker enflame.com/gcushare-resource-isolation=true --overwrite
通过给节点打标签,可以实现部分节点启用资源隔离功能,而部分节点关闭资源隔离功能。
说明:
节点标签enflame.com/gcushare-resource-isolation的优先级高于values文件中resourceIsolation参数的优先级。
需要在部署gcushare-device-plugin之前给节点打标签。
5.4. 查看日志¶
通常组件的运行日志都存放在容器中,这样就容易带来一些问题,一是一旦容器重启或者组件被卸载,会造成日志的丢失;二是难以根据异常信息找到代码的调用栈。这些问题都将增大故障排查的复杂程度,而用户则完全无从下手进行排查,进而大大增大了开发人员的运维负担。而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”标签将自动清除。
7. 常见问题¶
1)按卡调度和共享调度为什么不可以共存?
整卡调度和共享调度采用的是两个完全没有关联的k8s设备插件。二者有自己的调度逻辑,而且无法感知到对方的调度记录,若同时存在,将造成调度混乱。
2)gcushare如何实现底层的显存分配?
用户部署gcushare-device-plugin开启资源隔离功能后,设备插件将向底层runtime传递相应的环境变量,实现底层的隔离。
3)组件卸载后,已经使用共享GCU的pod业务会受影响吗?
不会。如问题2,gcushare只负责pod调度以及设备分配,已经调度并分配过GCU设备的pod不再受GCUShare影响。
4)gcushare组件重启后是否会影响后续的调度?
不会。gcushare调度pod时,可以实时获取到集群上申请共享GCU的pod信息,通常不会出现数据不一致的场景。
5)gcushare支持单个pod内多个容器申请共享GCU吗?
支持,但单个pod内所有申请共享GCU的容器的申请总和不得超过单张GCU卡的内存大小。
6)gcushare当前是按GCU设备的比例进行共享的,对于不同GCU型号,设备的显存,sip等资源可能不同。如何操作可以为pod分配预期大小的显存等资源?
在gcushare的实现中,如果用户申请1/2卡,卡片的显存为40G,那么表示为pod分配20G显存;如果卡片显存为60G,那么表示为pod分配30G显存。但如果你只想使用20G显存,而不关注使用了卡片的多少分之一,那么你需要先了解当前卡片的显存大小。例如:如果显存为40G,你可以设置sliceCount=2,然后申请enflame.com/shared-gcu: 1;如果显存为60G,你可以设置sliceCount=3,然后同样申请enflame.com/shared-gcu: 1。gcushare的共享策略比较灵活,用户可以根据自己的需求进行合理配置。
7)gcushare在运行过程中,如果掉了一张卡需要重启gcushare吗?或者如果卡片重新恢复了,需要重启gcushare吗?
这两种场景都不需要重启gcushare,gcushare可以自动适应掉卡以及卡片恢复等场景。