Kubernetes 解体新書Part 4 インターフェース

GPU をコンテナに渡す仕組み

Pod に GPU を使わせるための仕組みを見ます。個数で要求する方式と、種類や容量などの条件で選ぶ方式を比べます。

  • L1 Web ターミナル
  • 更新 2026年10月6日

機械学習のアプリに GPU を 1 枚使わせたい。 Kubernetes は、どのノードに空きがあるかを調べ、選んだ GPU をコンテナから使えるようにする必要があります。 さらに、GPU なら何でもよいのではなく、種類やメモリ容量を指定したい場合もあります。

この章では、デバイスの個数を要求する Device Plugin と、属性などの条件で選ぶ DRA を比べます。 実習環境には GPU がないため、実機への割り当てではなく、API とデバイス情報の置き場所を確認します。

GOAL この章のゴール

  • GPU の一覧が報告され、Pod に割り当てられる流れが分かる
  • 個数で要求する方式と、属性の条件で選ぶ方式を区別できる
  • API でデバイスの情報を確認する場所が分かる

Device Plugin

ノードに GPU があっても、kubelet がその種類や状態をすべて自力で調べられるわけではありません。 そこで、GPU を扱うプログラムが「このノードには何枚あり、どれが使えるか」を kubelet に知らせます。 この役を担うのが Device Plugin です。

通常は、ベンダーが提供するプラグインを対象の各ノードで動かします。 プラグインと kubelet は、同じノード上のプログラム同士をつなぐ UNIX socket を使い、gRPC という方式で情報をやり取りします。

Device Plugin の流れ: 登録、ListAndWatch、Allocatedevice plugin は /var/lib/kubelet/device-plugins/ に自分の socket を置き、kubelet の Registration サービスに Register する。kubelet は ListAndWatch でデバイス一覧を受け取り、Node の capacity に nvidia.com/gpu: 4 のように載せる。Pod がデバイスを要求すると kubelet は Allocate を呼び、返ってきたデバイスノードやマウントを CRI の CreateContainer に渡す。Nodekubeletdevice managernvidia-device-pluginDaemonSet1. Register (kubelet.sock)2. ListAndWatchGPU × 4/dev/nvidia0 …Node capacitynvidia.com/gpu: 43. 報告Pod speclimits: nvidia.com/gpu: 14. 要求5. Allocate → デバイスノード / env / mountcontainerd (CRI)6. CreateContainer に devices と mounts (または CDI 名) を載せる
図 1plugin が kubelet に登録し、デバイス一覧を流し続け、Pod が要求したときに Allocate で渡す。渡された内容は CRI の CreateContainer にそのまま載る。

流れは 3 段です。 まず plugin が /var/lib/kubelet/device-plugins/ に自分の socket を置いて gRPC を提供し始め、同じディレクトリの kubelet.sock で kubelet が提供する Registration サービスに「nvidia.com/gpu というリソース名で、この socket に居る」と登録します。 次に kubelet が plugin の ListAndWatch を呼び、デバイスの ID と健全性の一覧をストリームで受け取り続けます。 kubelet はその数を Node の status.capacity に nvidia.com/gpu: 4 のように載せ、scheduler はこの数を見て Pod を置きます。 最後に、Pod が limits: nvidia.com/gpu: 1 を要求してノードに乗ると、kubelet は plugin の Allocate を呼びます。 Allocate の応答には、コンテナからデバイスを使うために必要なデバイスノード、環境変数、マウント、注釈、そして CDI のデバイス名が入っていて、kubelet はそれを CRI の CreateContainer に載せます。 kubelet は最初に GetDevicePluginOptions を呼んで、plugin が任意の機能 (割り当ての助言をする GetPreferredAllocation、コンテナ起動前に初期化をする PreStartContainer) を実装しているかを確かめます。

Node の capacity に載る nvidia.com/gpu のような名前は 拡張リソース (extended resource) と呼ばれ、scheduler にとっては、ノードごとに残っている個数を表す整数でしかありません。 kubernetes.io は拡張リソースの制約として、整数でしか扱えないこと、オーバーコミットできないこと、デバイスをコンテナ間で共有できないことを挙げています。 これが Device Plugin の強みであり、限界でもあります。 整数なので scheduler の変更無しに済みますが、「メモリ 40GiB 以上の GPU」のような属性を条件にした要求は表現できません。 どの GPU を渡すかは kubelet (と plugin) がノードの中で決め、scheduler はノード選びにその情報を使えないのです。

DRA

DRA は、この限界を解くために作られました。 kubernetes.io は DRA の利点として、CEL (式の言語) による細かい絞り込み、複数のコンテナや Pod でのデバイス共有、デバイス設定をノード単位でなくワークロード単位で持てること、DeviceClass による種類の一元管理を挙げ、Device Plugin には「コンテナごとの個数指定しかできず、共有も式による絞り込みもできない」と対比しています。

Device Plugin と DRA の違い: 数で頼むか、条件で頼むかDevice Plugin は nvidia.com/gpu: 2 のように整数で要求し、kubelet のデバイス一覧から選ぶ。どの GPU かは選べない。DRA は ResourceClaim で属性 (メモリ、モデル、NVLink) を条件に書き、scheduler がクラスタ全体の ResourceSlice から選ぶ。同じデバイスを複数 Pod で共有することもできる。Device Plugin (1.26 GA)resources: limits: nvidia.com/gpu: 2整数で数を頼む。属性は選べない選ぶのは kubelet (ノード内)共有や分割は driver 独自の拡張で拡張リソース (extended resource) の仕組みDRA (1.34 GA)requests:- deviceClassName: gpu.nvidia.com selectors: memory >= 40Gi条件 (CEL) で頼む。属性を選べる選ぶのは scheduler (クラスタ全体)Claim を複数 Pod で共有できるresource.k8s.io/v1 の API
図 2左が整数で頼む Device Plugin、右が条件で頼む DRA。選ぶ主体が kubelet から scheduler に移る。

DRA では、デバイスは属性付きのオブジェクトとして apiserver に公開されます。 Pod の作者は「どのクラスのデバイスを、どんな条件で、何個」と書き、scheduler がクラスタ全体の在庫と突き合わせて選びます。 kubernetes.io はこれを、StorageClass から PVC で容量を claim する動的ボリューム確保と似た体験だと説明しています。

DRA の 4 つのリソース: ResourceSlice、DeviceClass、ResourceClaim、ResourceClaimTemplateDRA driver が ResourceSlice にデバイス一覧を属性付きで公開する。driver や管理者が DeviceClass でデバイスの種類を定義する。利用者は ResourceClaimTemplate を書いて Pod から参照し、resourceclaim-controller が Pod ごとに ResourceClaim を作る。scheduler が ResourceSlice と DeviceClass を読み、割り当て結果を ResourceClaim に書き、Pod をノードに置く。kubelet が driver に NodePrepareResources を呼び、CDI のデバイス名を受け取る。供給側 (driver / 管理者)DeviceClassgpu.nvidia.comResourceSlicedevices: [gpu-0, gpu-1 …]attributes: memory, model …DRA driverDaemonSet + kubelet plugin公開利用側 (Pod の作者)ResourceClaimTemplatedeviceClassName: gpuselectors: memory >= 40Gicount: 2PodresourceClaims: [gpus]参照ResourceClaimPod ごとに生成、割当結果を持つresourceclaim-controller が生成kube-schedulerSlice と Class を読んで選ぶ読む割り当てを書くkubeletPod を受け取るPod をノードに置くNodePrepareResourcesdriver は準備したデバイスを CDI の名前で kubelet に返す
図 3DRA の 4 つのリソース。driver が ResourceSlice に在庫を公開し、利用者は ResourceClaimTemplate を書き、Pod ごとに ResourceClaim が生成される。scheduler が割り当てを Claim に書き、kubelet が driver に準備を頼む。
  • ResourceSlice:driver が公開するデバイスの一覧 (在庫)。似たデバイスのプールごとに「このデバイスが、この属性 (モデル、メモリなど) で存在する」と書く。
  • DeviceClass:driver や管理者が定義するデバイスの種類。StorageClass にあたる。
  • ResourceClaimTemplate:Pod の作者が書く要求のひな型。deviceClassName と、属性に対する条件と個数。
  • ResourceClaim:実際の要求。Template を参照する Pod ごとに、kube-controller-manager の resourceclaim-controller が生成する。scheduler が割り当て結果を書き込み、複数の Pod が 1 つの Claim を共有することもできる。

「How DRA Works」によると、scheduler は Pod ごとに ResourceSlice を調べ、Pod を置けるノードからアクセスでき、Claim の条件に合う未割り当てのデバイスを探し、見つかったら ResourceClaim に割り当ての詳細を書いてから、そのデバイスにアクセスできるノードに Pod を置きます。 Pod がノードに乗ると、kubelet と driver が gRPC で協調してデバイスを準備します。 その gRPC は kubelet 側で DRAPlugin サービスとして定義され、NodePrepareResources の応答に、次に説明する CDI のデバイス名が入ります。 制約もあり、DRA のリソースは preemption (優先度の高い Pod のために低い Pod を追い出す仕組み) の対象にならないので、デバイスを低優先度の Pod が使っていると、高優先度の Pod は Pending のまま待ちます。

API の安定性とドライバーの対応範囲

DRA の API が GA でも、すべてのデバイス用ドライバーと機能が同じ段階にあるとは限りません。 NVIDIA GPU 向け DRA driver の公式 README は、複数ノードの NVLink を扱う ComputeDomain と、GPU の割り当てを扱う plugin を分けています。 2026 年 10 月の確認時点では、前者を正式サポートとし、後者の一部機能は試用できるが正式サポートではなく、Helm の既定でも無効と説明しています。 採用を判断するときは、Kubernetes のバージョンに加え、使いたいデバイス、ドライバーのリリース、個別機能のサポート範囲を確認します。

CDI

Device Plugin でも DRA でも、最後には「このコンテナに /dev/nvidia0 と、CUDA のライブラリと、環境変数を入れろ」という指示をランタイムに渡す必要があります。 その書式が CDI (Container Device Interface) です。 CDI の仕様は自身を「コンテナランタイムがサードパーティのデバイスを扱えるコンテナを作るための仕組み」と説明しています。 デバイスは vendor.com/class=name の形の完全修飾名で指し、spec ファイルには containerEdits として環境変数、デバイスノード、マウント、フックを書き、ランタイムはその内容を OCI の config.json に適用します。

CDI: デバイスをコンテナに渡す書式ベンダーのツールが /etc/cdi/ または /var/run/cdi/ に CDI spec (JSON / YAML) を置き、デバイス名と、コンテナに必要なデバイスノード、マウント、環境変数、フックを containerEdits として書く。kubelet は CRI の CreateContainer で nvidia.com/gpu=0 のような完全修飾名だけを渡し、containerd が spec を引いて OCI の config.json に展開する。/var/run/cdi/nvidia.yamlkind: nvidia.com/gpudevices: - name: "0" containerEdits: deviceNodes, mounts, env, hooksベンダーのツール (nvidia-ctk) が生成kubeletDevice Plugin / DRACDI 名だけ渡すnvidia.com/gpu=0containerdCDI spec を解決spec を読むOCI config.jsondevices / mounts / env 展開済みkubelet はデバイスの中身 (どのファイルを渡すか) を知らなくてよい
図 4ベンダーが CDI spec を置き、kubelet は名前だけを渡し、containerd が spec を引いて OCI の config.json に展開する。

CDI 以前、NVIDIA の GPU を使うには nvidia-container-runtime という runc の薄いラッパーが要りました。 NVIDIA のドキュメントによると、これは OCI の spec に prestart hook を差し込み、コンテナ起動前にデバイスとライブラリを注入するものです。 CDI では、ベンダーのツール (NVIDIA なら nvidia-ctk cdi generate) が /etc/cdi/ か /var/run/cdi/ に spec を生成しておき、kubelet は nvidia.com/gpu=0 という名前を CRI の CDI_devices で渡すだけになります。 containerd や CRI-O がその名前で spec を引き、OCI の config.json にデバイスとマウントを展開します。 kubelet はデバイスの中身を知らなくてよく、ランタイムは Kubernetes を知らなくてよい。 それぞれが持つ実装が減りました。

実機: resource.k8s.io を眺める

GPU の無いこのクラスタでも、DRA の API が apiserver に登録されていることは確かめられます。

DRA の API リソースを列挙する
kubectl api-resources --api-group=resource.k8s.io

出力例

NAME                     SHORTNAMES   APIVERSION            NAMESPACED   KIND
deviceclasses                         resource.k8s.io/v1    false        DeviceClass
resourceclaims                        resource.k8s.io/v1    true         ResourceClaim
resourceclaimtemplates                resource.k8s.io/v1    true         ResourceClaimTemplate
resourceslices                        resource.k8s.io/v1    false        ResourceSlice
在庫と Node の拡張リソースを見る
kubectl get resourceslices,deviceclasses
kubectl get node -o jsonpath='{.items[0].status.capacity}' | jq

出力例

No resources found
{
  "cpu": "4",
  "ephemeral-storage": "...",
  "memory": "...",
  "pods": "110"
}

ResourceSlice が無いのは DRA driver が居ないからで、capacity に nvidia.com/gpu が無いのは Device Plugin が居ないからです。 GPU ノードのあるクラスタでは、この 2 つのどちらか (または両方) に在庫が現れます。 GPU の Pod が Unschedulable のとき、最初に見るのはこの 2 行です。

ふりかえり

QDevice Plugin が報告したデバイス数は、どこに載る?

kubelet が ListAndWatch で受け取った数を Node の capacity に nvidia.com/gpu: 4 のように載せ、scheduler はこれを整数の在庫として扱います。ResourceSlice は DRA の仕組みです。

QDRA が Device Plugin と最も違う点は?

Device Plugin は整数で要求し、どのデバイスかは kubelet がノード内で決めます。DRA は ResourceSlice の属性に対して CEL の条件で要求し、scheduler がクラスタ全体から選びます。

QCDI の役割は?

kubelet は nvidia.com/gpu=0 のような名前を CRI で渡すだけで、containerd が /etc/cdi/ などの spec を引いて OCI の config.json に展開します。

参考