快轉到主要內容

初探 Kubernetes DRA —— 以 NVIDIA DRA Driver GPU 為例

·2076 字·10 分鐘·
ChengHao Yang
作者
ChengHao Yang
SRE / CNCF Ambassador
目錄

前陣子 Dynamic Resource Allocation (DRA) 終於在 Kubernetes v1.35 進入 GA,相信不少人想要躍躍欲試,加上 NVIDIA 也把 dra-driver-nvidia-gpu 送進到 Kubernetes SIGs 當中,原有文件把 Beta 字眼拿掉,象徵技術和標準逐漸成熟。

這次我借了 CNTUG Infra Labs 目前所有的 NVIDIA GPU,來學習如何用 DRA 優雅的分配設備和資源。

友情工商:CNTUG Infra Labs
#

CNTUG Infra Labs 成立的宗旨是為了培育台灣軟體基礎設施領域的未來學生和工程師。目前已在 Equinix 東京機房成功建立完善的環境,得到了 CNTUG 社群多位成員的共同出資支持。此建置過程中運用了大量開源專案,例如 OpenStack、Ceph 和 Ansible 等。

由於基礎設施相關軟體具有較高的上手難度,並需要足夠的運算、儲存和網路資源,因此 CNTUG Infra Labs 計劃提供一個雲端平台供學生和社群成員測試及架設相關服務。我們期望這將吸引更多學生參與,並在軟體基礎設施領域不斷精進。同時,剩餘資源將提供給開源社群,供他們架設所需服務(如網站、Mattermost 和 Jitsi Meet 等),或用於工作坊活動。

目前已經有許多使用者利用此平台積極開源貢獻,詳情可以翻閱使用案例

接下來,回到我們的實驗環境介紹。

實驗環境介紹
#

這次會使用 Cluster API + OpenStack 建構的 Kubernetes cluster,篇幅關係此環境安裝過程會省略,想要詳細介紹可以看其他人 blog,或等我整理完步驟。

  • OS: Ubuntu 24.04
  • Kubernetes v1.35.3
  • Containerd 2.2.2
  • Node:
    • 1 Control Plane + etcd
    • 3 Workers
      • No GPU
      • T10 * 2
      • A5000 * 1
  • NVIDIA GPU Operator v26.3.1
  • NVIDIA DRA Driver GPU v25.12.0

kubectl get node 看完會有以下資訊:

NAME                                   STATUS   ROLES           AGE     VERSION
capi-dralabs-control-plane-xtcth       Ready    control-plane   8m7s    v1.35.3
capi-dralabs-md-0-p4xkh-rpfxc          Ready    <none>          6m55s   v1.35.3
capi-dralabs-md-gpua5000-jw4mx-d64jz   Ready    <none>          2m37s   v1.35.3
capi-dralabs-md-gput10-gzl84-f2m2d     Ready    <none>          6m49s   v1.35.3

安裝 NVIDIA GPU Operator
#

開始安裝 GPU Operator 前,要先幫有 GPU 的 Node 加上標籤,以我的環境來說就會像這樣:

kubectl label node capi-dralabs-md-gpua5000-jw4mx-d64jz nvidia.com/dra-kubelet-plugin=true
kubectl label node capi-dralabs-md-gput10-gzl84-f2m2d nvidia.com/dra-kubelet-plugin=true

加入 NVIDIA 的 Chart Repo:

helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

建立 values-gpu-operator.yaml 檔案,等等安裝會使用:

values-gpu-operator.yaml
1# version: v26.3.1
2devicePlugin:
3  enabled: false
4
5driver:
6  manager:
7    env:
8    - name: NODE_LABEL_FOR_GPU_POD_EVICTION
9      value: "nvidia.com/dra-kubelet-plugin"
註記

如果 Kubernetes 是使用其他發行版 (e.g. Rancher 或 K3s) ,可能會因為 Containerd 預設安裝位置不一樣,記得加入以下設定到 values-gpu-operator.yaml 中:

1toolkit:
2  env:
3  - name: CONTAINERD_SOCKET
4    value: /run/k3s/containerd/containerd.sock

安裝 NVIDIA GPU Operator:

helm upgrade --install gpu-operator nvidia/gpu-operator \
  --version=v26.3.1 \
  --create-namespace \
  --namespace gpu-operator -f values-gpu-operator.yaml

接下來就等待 GPU Operator 啟動完成,它會安裝好 NVIDIA GPU Driver 和修改 Container Runtime 設定,如果有需要個別調整設定需求,請詳閱 NVIDIA 官方文件

安裝 NVIDIA DRA Driver GPU
#

建立 values-nvidia-dra-driver-gpu.yaml 檔案,等等安裝會使用:

values-nvidia-dra-driver-gpu.yaml
 1# version: 25.12.0
 2nvidiaDriverRoot: /run/nvidia/driver
 3gpuResourcesEnabledOverride: true
 4image:
 5  pullPolicy: IfNotPresent
 6kubeletPlugin:
 7  nodeSelector:
 8    nvidia.com/dra-kubelet-plugin: "true"
 9resources:
10  gpus:
11    enabled: true
12  computeDomains:
13    enabled: false # No NVLink here
14# featureGates:
15#   TimeSlicingSettings: true

如果想要嘗試後面情境 IV 的 GPU Time Slicing,可以一併開啟 TimeSlicingSettings 這個 Feature Gate;如果暫時不需要也可以保留註解,之後再 helm upgrade 補上。

安裝 NVIDIA DRA Driver GPU:

helm upgrade -i nvidia-dra-driver-gpu nvidia/nvidia-dra-driver-gpu \
  --version="25.12.0" \
  --namespace nvidia-dra-driver-gpu \
  --create-namespace -f values-nvidia-dra-driver-gpu.yaml

kubectl get pod 確認一下安裝的 NVIDIA DRA Driver GPU 是否都上線:

kubectl get pod -n nvidia-dra-driver-gpu
NAME                                         READY   STATUS    RESTARTS   AGE
nvidia-dra-driver-gpu-kubelet-plugin-6skhp   1/1     Running   0          10m
nvidia-dra-driver-gpu-kubelet-plugin-jswk6   1/1     Running   0          10m

初探 DRA
#

DeviceClass
#

安裝完成後,就會看到 DeviceClassResourceSlice 已經由 NVIDIA DRA Driver GPU 設定,DeviceClass 顧名思義就是設備種類,打開就會看到裡面有一般 GPU、MIG 和 VFIO。(如果 ComputeDomains 沒關掉,也會看到 ComputeDomains 資訊。)

kubectl get deviceclass
DeviceClass 範例輸出
NAME                  AGE
gpu.nvidia.com        44m
mig.nvidia.com        44m
vfio.gpu.nvidia.com   44m

ResourceSlice
#

ResourceSlice 由各個節點上的 DRA driver 自動更新,記錄該 driver 在這個節點上管理的所有裝置。

同節點同 driver 的裝置會在同一個 Pool,當裝置數量過多(上限 128 個,有 taint 或使用 counter 會降為 64 個)無法塞進同個物件時,driver 會把 Pool 拆成多個 ResourceSlice

.spec.pool.generation.spec.pool.resourceSliceCount 讓 scheduler 判斷是不是拿到該節點完整的最新裝置列表。

kubectl get resourceslice
ResourceSlice 範例輸出
NAME                                                        NODE                                   DRIVER           POOL                                   AGE
capi-dralabs-md-gpua5000-jw4mx-d64jz-gpu.nvidia.com-w9fnv   capi-dralabs-md-gpua5000-jw4mx-d64jz   gpu.nvidia.com   capi-dralabs-md-gpua5000-jw4mx-d64jz   5m13s
capi-dralabs-md-gput10-gzl84-f2m2d-gpu.nvidia.com-dtgtc     capi-dralabs-md-gput10-gzl84-f2m2d     gpu.nvidia.com   capi-dralabs-md-gput10-gzl84-f2m2d     23m

可以用 -o yaml 展開完整的內容:

kubectl get resourceslices -o yaml

完整內容可以點擊下方展開,每個 ResourceSlice 會在 .metadata.ownerReferences 寫上節點、.spec.devices 紀錄裝置,每個裝置都會有 .attributes,包括但不限於架構、型號、裝置版本等。

由於本次實驗環境每個節點最多只有 2 張顯卡,遠低於單一 ResourceSlice 的 128 個上限,因此每個節點都只會看到一個 ResourceSlice

ResourceSlice 完整輸出結果
  1apiVersion: v1
  2items:
  3- apiVersion: resource.k8s.io/v1
  4  kind: ResourceSlice
  5  metadata:
  6    creationTimestamp: "2026-05-04T14:40:17Z"
  7    generateName: capi-dralabs-md-gpua5000-jw4mx-d64jz-gpu.nvidia.com-
  8    generation: 1
  9    name: capi-dralabs-md-gpua5000-jw4mx-d64jz-gpu.nvidia.com-w9fnv
 10    ownerReferences:
 11    - apiVersion: v1
 12      controller: true
 13      kind: Node
 14      name: capi-dralabs-md-gpua5000-jw4mx-d64jz
 15      uid: 83aafab6-7eb3-42d0-9faf-6118f78341ef
 16    resourceVersion: "11490"
 17    uid: d03fd27e-f6cb-4386-ae61-80aa84309e77
 18  spec:
 19    devices:
 20    - attributes:
 21        addressingMode:
 22          string: HMM
 23        architecture:
 24          string: Ampere
 25        brand:
 26          string: NvidiaRTX
 27        cudaComputeCapability:
 28          version: 8.6.0
 29        cudaDriverVersion:
 30          version: 13.0.0
 31        driverVersion:
 32          version: 580.126.20
 33        productName:
 34          string: NVIDIA RTX A5000
 35        resource.kubernetes.io/pciBusID:
 36          string: "0000:00:06.0"
 37        resource.kubernetes.io/pcieRoot:
 38          string: pci0000:00
 39        type:
 40          string: gpu
 41        uuid:
 42          string: GPU-e13ce856-7474-797f-d143-16e99b65c0c3
 43      capacity:
 44        memory:
 45          value: 23028Mi
 46      name: gpu-0
 47    driver: gpu.nvidia.com
 48    nodeName: capi-dralabs-md-gpua5000-jw4mx-d64jz
 49    pool:
 50      generation: 1
 51      name: capi-dralabs-md-gpua5000-jw4mx-d64jz
 52      resourceSliceCount: 1
 53- apiVersion: resource.k8s.io/v1
 54  kind: ResourceSlice
 55  metadata:
 56    creationTimestamp: "2026-05-04T14:21:53Z"
 57    generateName: capi-dralabs-md-gput10-gzl84-f2m2d-gpu.nvidia.com-
 58    generation: 1
 59    name: capi-dralabs-md-gput10-gzl84-f2m2d-gpu.nvidia.com-dtgtc
 60    ownerReferences:
 61    - apiVersion: v1
 62      controller: true
 63      kind: Node
 64      name: capi-dralabs-md-gput10-gzl84-f2m2d
 65      uid: d7ecdc93-1d6c-4868-8503-4251bcf8cf3b
 66    resourceVersion: "7408"
 67    uid: 66f32713-c547-4369-84de-97f86430d18d
 68  spec:
 69    devices:
 70    - attributes:
 71        addressingMode:
 72          string: HMM
 73        architecture:
 74          string: Turing
 75        brand:
 76          string: Nvidia
 77        cudaComputeCapability:
 78          version: 7.5.0
 79        cudaDriverVersion:
 80          version: 13.0.0
 81        driverVersion:
 82          version: 580.126.20
 83        productName:
 84          string: Tesla T10
 85        resource.kubernetes.io/pciBusID:
 86          string: "0000:00:06.0"
 87        resource.kubernetes.io/pcieRoot:
 88          string: pci0000:00
 89        type:
 90          string: gpu
 91        uuid:
 92          string: GPU-dae084a2-974c-00e2-6dec-4ba1999b8652
 93      capacity:
 94        memory:
 95          value: 16Gi
 96      name: gpu-0
 97    - attributes:
 98        addressingMode:
 99          string: HMM
100        architecture:
101          string: Turing
102        brand:
103          string: Nvidia
104        cudaComputeCapability:
105          version: 7.5.0
106        cudaDriverVersion:
107          version: 13.0.0
108        driverVersion:
109          version: 580.126.20
110        productName:
111          string: Tesla T10
112        resource.kubernetes.io/pciBusID:
113          string: "0000:00:07.0"
114        resource.kubernetes.io/pcieRoot:
115          string: pci0000:00
116        type:
117          string: gpu
118        uuid:
119          string: GPU-d1bf2033-42f6-096c-b0c6-470575bc08df
120      capacity:
121        memory:
122          value: 16Gi
123      name: gpu-1
124    driver: gpu.nvidia.com
125    nodeName: capi-dralabs-md-gput10-gzl84-f2m2d
126    pool:
127      generation: 1
128      name: capi-dralabs-md-gput10-gzl84-f2m2d
129      resourceSliceCount: 1
130kind: List
131metadata:
132  resourceVersion: ""

有了這些資訊,Pod 該如何跟 Kubernetes 說我要哪些裝置呢?接下來就要來介紹 ResourceClaimResourceClaimTemplate

ResourceClaim & ResourceClaimTemplate
#

如果會希望很多個 Pod 對同一個裝置做使用,可以手動建立 ResourceClaim 管理,不論 Pod 新增或刪除,都不會被影響,完全獨立自主。

ResourceClaim 示意圖
ResourceClaim 示意圖

那假設想要每個 Pod 都有自己的裝置呢?ResourceClaimTemplate 可以先寫好 ResourceClaim 資源,Deployment 寫上 ResourceClaimTemplate 的名字,只要 Deployment 新增 Pod,就會自動產生對應的 ResourceClaim,反之刪除 Pod,就會把相對應的 ResourceClaim 刪除。

ResourceClaimTemplate 示意圖
ResourceClaimTemplate 示意圖

有沒有覺得上面這些概念哪裡似曾相識呢?DRA 概念就是借鑑 Storage 來達成,PersistentVolumeClaimPersistentVolumeClaimTemplate(後者僅存在於 StatefulSet 當中),DeviceClass 就有點像 StorageClass

DRA 練習
#

情境 I:2 個容器共用 1 張 GPU
#

ResourceClaim 宣告說要一張 NVIDIA GPU,接著 Pod 開兩個容器使用該顯卡。

lab01-rc.yaml
 1apiVersion: resource.k8s.io/v1
 2kind: ResourceClaim
 3metadata:
 4  name: must-nvidia-gpu
 5spec:
 6  devices:
 7    requests:
 8    - name: gpu
 9      exactly:
10        deviceClassName: gpu.nvidia.com
11        count: 1

接下來套用資源:

kubectl apply -f lab01-rc.yaml

get 取得 ResourceClaim 資訊:

kubectl get resourceclaim

就能看到目前狀態是 pending,因為還沒有 Pod 使用該資源。

NAME              STATE     AGE
must-nvidia-gpu   pending   10s

這裡寫好兩個容器的 Pod,並且使用剛剛建立的 ResourceClaim —— must-nvidia-gpu

lab01-pod.yaml
 1apiVersion: v1
 2kind: Pod
 3metadata:
 4  name: must-nvidia-gpu-pod
 5spec:
 6  restartPolicy: Never
 7  containers:
 8  - name: ctr0
 9    image: ubuntu:24.04
10    command: ["bash", "-c"]
11    args: ["nvidia-smi -L; trap 'exit 0' TERM; sleep 9999 & wait"]
12    resources:
13      claims:
14      - name: gpu
15  - name: ctr1
16    image: ubuntu:24.04
17    command: ["bash", "-c"]
18    args: ["nvidia-smi -L; trap 'exit 0' TERM; sleep 9999 & wait"]
19    resources:
20      claims:
21      - name: gpu
22  resourceClaims:
23  - name: gpu
24    resourceClaimName: must-nvidia-gpu

接著就可以開新的 Pod:

kubectl apply -f lab01-pod.yaml

重新看 ResourceClaim 資訊:

kubectl get resourceclaim

就能看到狀態更改為 allocatedreserved,因為已經有 Pod 使用該資源。

NAME              STATE                AGE
must-nvidia-gpu   allocated,reserved   16s

接下來就可以用 logs 印出內容:

kubectl logs pod must-nvidia-gpu-pod --all-containers --prefix
[pod/must-nvidia-gpu-pod/ctr0] GPU 0: Tesla T10 (UUID: GPU-dae084a2-974c-00e2-6dec-4ba1999b8652)
[pod/must-nvidia-gpu-pod/ctr1] GPU 0: Tesla T10 (UUID: GPU-dae084a2-974c-00e2-6dec-4ba1999b8652)

實際不一定是 T10,也有可能是 A5000。

接下來把 Pod 刪除掉:

kubectl delete -f lab01-pod.yaml

重新看 ResourceClaim 資訊:

kubectl get resourceclaim

狀態重新回到 pending,因為已經沒有 Pod 使用該資源。

NAME              STATE     AGE
must-nvidia-gpu   pending   3m39s

刪除 ResourceClaim

kubectl delete -f lab01-rc.yaml

但上面範例只寫了一張 GPU,不過根本不知道用哪張 GPU。

這情境其實跟原本的 Device Plugin 沒什麼兩樣,對吧?但後面情境才是 DRA 的優勢!

情境 II:ResourceClaimTemplate 優先使用 A5000 套用 Deployment
#

今天 RD 跑來跟我說,他想要有推論模型可以優先使用 A5000,但知道機器沒有很多,希望 scale up 的時候可以用 T10。

ResourceClaim 資源除了 exactly 以外,也可以用 firstAvailable 排優先順序,回到 ResourceSlice 的完整輸出結果,想要指定顯卡名稱,可以用 .attributes.productName

就會像以下的寫法:

lab02.yaml
 1apiVersion: resource.k8s.io/v1
 2kind: ResourceClaimTemplate
 3metadata:
 4  name: first-a5000
 5spec:
 6  spec:
 7    devices:
 8      requests:
 9      - name: gpu
10        firstAvailable:
11        - name: a5000
12          deviceClassName: gpu.nvidia.com
13          selectors:
14          - cel:
15              expression: device.attributes["gpu.nvidia.com"].productName == "NVIDIA RTX A5000"
16        - name: fallback-t10
17          deviceClassName: gpu.nvidia.com
18          selectors:
19          - cel:
20              expression: device.attributes["gpu.nvidia.com"].productName == "Tesla T10"
21---
22apiVersion: apps/v1
23kind: Deployment
24metadata:
25  name: first-a5000-deploy
26  labels:
27    app: first-a5000-deploy
28spec:
29  replicas: 1
30  selector:
31    matchLabels:
32      app: first-a5000-deploy
33  template:
34    metadata:
35      labels:
36        app: first-a5000-deploy
37    spec:
38      containers:
39      - name: ctr0
40        image: ubuntu:24.04
41        command: ["bash", "-c"]
42        args: ["nvidia-smi -L; trap 'exit 0' TERM; sleep 9999 & wait"]
43        resources:
44          claims:
45          - name: gpu
46      resourceClaims:
47      - name: gpu
48        resourceClaimTemplateName: first-a5000

把上面內容存為 lab02.yaml,套用進去:

kubectl apply -f lab02.yaml

確認 PodRunning,並從 nvidia-smi -L 的輸出確認分到的是 A5000:

kubectl get pod
kubectl logs deployments/first-a5000-deploy --all-pods
NAME                                 READY   STATUS    RESTARTS   AGE
first-a5000-deploy-8c6cf4568-2lsv9   1/1     Running   0          9s

[pod/first-a5000-deploy-8c6cf4568-2lsv9/ctr0] GPU 0: NVIDIA RTX A5000 (UUID: GPU-e13ce856-7474-797f-d143-16e99b65c0c3)

接著 scale up 到 2 個副本,看新的 Pod 會分到哪張:

kubectl scale deployment first-a5000-deploy --replicas 2
kubectl logs deployments/first-a5000-deploy --all-pods
[pod/first-a5000-deploy-8c6cf4568-2lsv9/ctr0] GPU 0: NVIDIA RTX A5000 (UUID: GPU-e13ce856-7474-797f-d143-16e99b65c0c3)
[pod/first-a5000-deploy-8c6cf4568-865jj/ctr0] GPU 0: Tesla T10 (UUID: GPU-dae084a2-974c-00e2-6dec-4ba1999b8652)

第一個 Pod 已經佔用唯一一張 A5000,第二個 Pod 因此 fallback 到 T10 —— 這正是 firstAvailable 在第一順位不可用時的預期行為。

並且可以看到相對應的 ResourceClaim

kubectl get resourceclaim
NAME                                           STATE                AGE
first-a5000-deploy-8c6cf4568-2lsv9-gpu-bdz9j   allocated,reserved   4m29s
first-a5000-deploy-8c6cf4568-865jj-gpu-mqfcx   allocated,reserved   3m29s
如果把 A5000 的 Pod 下令刪除,重建的 Pod 會回到 A5000 嗎?

如果以上面的設定檔來說,不會回到 A5000。Deployment 預設的 strategy.typeRollingUpdate,舊的 Pod 進到 Terminating 的時候,ResourceClaim 並沒有被釋放。
Deployment controller 會立刻建立新的 Pod,也會依照 ResourceClaimTemplate 建立新的 ResourceClaim,新的 ResourceClaim 看到 A5000 依然被舊的 Pod 佔用,會 fallback 到 T10。

接下來,就可以把資源清除掉:

kubectl delete -f lab02.yaml

情境 III:需要 20 GiB 以上記憶體的 GPU
#

今天有個 LLM 模型要部署,RD 跟我說需要使用 20 GiB 以上記憶體的單張 GPU,但還在測試中所以不用算力要求,有空閒的 GPU 就可以了。

除了 .attributes 外,還有 .capacity.memory 可以使用,但該如何寫比較規則呢?可以看看第 15 行:

lab03.yaml
 1apiVersion: resource.k8s.io/v1
 2kind: ResourceClaimTemplate
 3metadata:
 4  name: gt20g
 5spec:
 6  spec:
 7    devices:
 8      requests:
 9      - name: gpu
10        firstAvailable:
11        - name: gt20g
12          deviceClassName: gpu.nvidia.com
13          selectors:
14          - cel:
15              expression: device.capacity["gpu.nvidia.com"].memory.isGreaterThan(quantity("20Gi"))
16---
17apiVersion: apps/v1
18kind: Deployment
19metadata:
20  name: gt20g-deploy
21  labels:
22    app: gt20g-deploy
23spec:
24  replicas: 1
25  selector:
26    matchLabels:
27      app: gt20g-deploy
28  template:
29    metadata:
30      labels:
31        app: gt20g-deploy
32    spec:
33      containers:
34      - name: ctr0
35        image: ubuntu:24.04
36        command: ["bash", "-c"]
37        args: ["nvidia-smi -L; trap 'exit 0' TERM; sleep 9999 & wait"]
38        resources:
39          claims:
40          - name: gpu
41      resourceClaims:
42      - name: gpu
43        resourceClaimTemplateName: gt20g

利用 CEL 的 isGreaterThan(quantity("20Gi")) 表示需要大於 20 GiB。

接下來就可以套用進去:

kubectl apply -f lab03.yaml

確認一下 Pod 是否在 Running,確認顯卡是不是 A5000。

kubectl get pod
kubectl logs deployments/gt20g-deploy --all-pods
NAME                           READY   STATUS    RESTARTS   AGE
gt20g-deploy-5ff576476-hdz8f   1/1     Running   0          5m16s

[pod/gt20g-deploy-5ff576476-hdz8f/ctr0] GPU 0: NVIDIA RTX A5000 (UUID: GPU-e13ce856-7474-797f-d143-16e99b65c0c3)

接著,試試看 scale up 後會發生什麼事:

kubectl scale deployment gt20g-deploy --replicas 2

增長了之後,確認一下 Pod 有沒有新加入:

kubectl get pod
NAME                           READY   STATUS    RESTARTS   AGE
gt20g-deploy-5ff576476-hdz8f   1/1     Running   0          8m9s
gt20g-deploy-5ff576476-vjss8   0/1     Pending   0          26s

gt20g-deploy-5ff576476-vjss8 這個 Poddescribe

kubectl describe pod gt20g-deploy-5ff576476-vjss8
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  98s   default-scheduler  0/4 nodes are available: 1 node(s) had untolerated taint(s), 3 cannot allocate all claims. still not schedulable, preemption: 0/4 nodes are available: 4 Preemption is not helpful for scheduling.

因為現在 Cluster 沒有其他可用的 20 GiB 以上記憶體的 GPU,T10 只有 16 GiB,因此新加入的 Pod 就會被卡在 Pending

接下來把資源清除:

kubectl delete -f lab03.yaml

如果要更複雜的判斷方式,可以去詳讀 CEL 在 Kubernetes 的使用方式

情境 IV:GPU Time Slicing in DRA
#

警告

截至 2026/5/10 發文當下,NVIDIA 官方文件和 NVIDIA DRA Driver GPU 的 Wiki 文件並沒有針對 Time Slicing 撰寫任何教學。
設定內容是參照 demo/specs/quickstart/v1/gpu-test5.yaml 的內容改編,筆者也去讀了部分程式碼,Feature Gate 部分參考國外文章資料撰寫。
設定方式可能會隨著未來更新有所不同,特此注意!

今天 RD 又跑過來跟我說:「我知道 DRA 很棒,能更有效分配資源,但有沒有辦法還是可以回去使用 Time Slicing 模式?」

想要回去原本的模式?當然是……,沒問題的!

只要在 .spec.devices.config 寫上我要設定的裝置,共享策略調整為 TimeSlicing。內容範例如下:

lab04.yaml
 1apiVersion: resource.k8s.io/v1
 2kind: ResourceClaim
 3metadata:
 4  name: time-slicing-manual
 5spec:
 6  devices:
 7    requests:
 8    - name: ts-gpu
 9      exactly:
10        deviceClassName: gpu.nvidia.com
11    config:
12    - requests: ["ts-gpu"]
13      opaque:
14        driver: gpu.nvidia.com
15        parameters:
16          apiVersion: resource.nvidia.com/v1beta1
17          kind: GpuConfig
18          sharing:
19            strategy: TimeSlicing
20            timeSlicingConfig:
21              interval: Long
22---
23apiVersion: apps/v1
24kind: Deployment
25metadata:
26  name: ts-gpu-deployment
27spec:
28  replicas: 4
29  selector:
30    matchLabels:
31      app: ts-gpu
32  template:
33    metadata:
34      labels:
35        app: ts-gpu
36    spec:
37      containers:
38      - name: ctr
39        image: nvcr.io/nvidia/k8s/cuda-sample:nbody-cuda11.6.0-ubuntu18.04
40        command: ["bash", "-c"]
41        args: ["trap 'exit 0' TERM; /tmp/sample --benchmark --numbodies=4226000 & wait"]
42        resources:
43          claims:
44          - name: gpu
45      resourceClaims:
46      - name: gpu
47        resourceClaimName: time-slicing-manual

將上面內容儲存為 lab04.yaml,就可以套用進去:

kubectl apply -f lab04.yaml

確認一下 Pod 是否都有正常運作:

kubectl get pod
NAME                                 READY   STATUS    RESTARTS   AGE
ts-gpu-deployment-549c945798-6t2dx   1/1     Running   0          4s
ts-gpu-deployment-549c945798-tlgp4   1/1     Running   0          4s
ts-gpu-deployment-549c945798-x2gbv   1/1     Running   0          4s
ts-gpu-deployment-549c945798-xbv22   1/1     Running   0          4s

因為 4 個 Pod 共用同一個 ResourceClaim,用 kubectl get resourceclaim 會只看到一筆,這本身就說明它們在共享:

kubectl get resourceclaim
NAME                  STATE                AGE
time-slicing-manual   allocated,reserved   30s

如果再用 describe 進去看,會看到 Reserved For 列出全部 4 個 Pod:

kubectl describe resourceclaim time-slicing-manual
Status:
  Allocation:
    Devices:
      Config:
        Opaque:
          Driver:  gpu.nvidia.com
          Parameters:
            API Version:  resource.nvidia.com/v1beta1
            Kind:         GpuConfig
            Sharing:
              Strategy:  TimeSlicing
              Time Slicing Config:
                Interval:  Long
        Requests:
          ts-gpu
        Source:  FromClaim
      Results:
        Device:   gpu-0
        Driver:   gpu.nvidia.com
        Pool:     capi-dralabs-md-gpua5000-jw4mx-d64jz
        Request:  ts-gpu
    Node Selector:
      Node Selector Terms:
        Match Fields:
          Key:       metadata.name
          Operator:  In
          Values:
            capi-dralabs-md-gpua5000-jw4mx-d64jz
  Reserved For:
    Name:      ts-gpu-deployment-549c945798-x2gbv
    Resource:  pods
    UID:       be21eecf-2d58-4891-9cac-ac3674f4ff09
    Name:      ts-gpu-deployment-549c945798-6t2dx
    Resource:  pods
    UID:       37d504a0-966e-4263-9fcd-b713d73c0e77
    Name:      ts-gpu-deployment-549c945798-xbv22
    Resource:  pods
    UID:       2b5ec297-9889-4cb9-8079-b626a854b292
    Name:      ts-gpu-deployment-549c945798-tlgp4
    Resource:  pods
    UID:       ee836856-8c6b-4161-9582-4bc4ff63a606

這樣就能在 DRA 下啟用 Time Slicing 行為,效果類似舊有 Device Plugin 的 Time Slicing 模式,不需要刻意寫要切幾等份,只需要設定 timeSlicingConfig 與循環週期。

接下來就可以把資源清除:

kubectl delete -f lab04.yaml

總結
#

相比 Device Plugin,現在的 DRA 能以更清晰的方式使用,讓開發者和叢集管理員更精準分配裝置。不需要再把同一個裝置放到同一個節點,或者寫複雜的規則放在 nodeSelectorAffinity 等。

K8s v1.36 以後還有裝置健康狀態回報,不再讓 Pod 只有顯示 Error,能知道 Error 原因是裝置問題還是應用程式問題。

過去,K8s 叢集如果發現所需的 CPU、Memory 不夠,可以透過 Cluster Autoscaler 開啟新機器,如果 GPU 不夠,未來說不定也可以透過 Cluster Autoscaler 產生所需的 GPU 節點,更有效地去分配資源。

後記
#

前年為了一些測試需求買了 A5000,就把這張顯卡放到 Infra Labs,但覺得很可惜沒有更多的實驗測試,再加上自己太忙,一直放在機房裡面吃電和吃灰塵。

dra-driver-nvidia-gpu 進到 Kubernetes SIGs 的時候,想到裡面還有提供兩張 T10 顯卡,於是就促成這次的 Lab。

同時,我也看到了許多 AI Gateway 的出現,還有 Gateway API 的延伸套件 —— Gateway API Inference Extension,有機會再來寫文章介紹!

相關文章

Kubernetes Release Team 裡看 Signal:Release Signal 的日常與成長

Kubernetes v1.35 終於 release 了,不過你有想過大型專案 Kubernetes 每一個版本背後,都有人在盯著「訊號」是否開始失真嗎? Release Signal 在做什麼?每天到底在看什麼? 這篇文章記錄我從 Shadow 走進 Kubernetes Release Team,成為 sub-team lead 的心路歷程,實際參與版本節奏、code freeze、RC 前後的真實日常。